Guest User

Daily Programmer 321 (Reashu)

a guest
Jul 2nd, 2017
235
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Kotlin 6.09 KB | None | 0 0
  1. import org.apache.commons.math.linear.Array2DRowRealMatrix
  2. import org.apache.commons.math.linear.ArrayRealVector
  3. import org.apache.commons.math.linear.LUDecompositionImpl
  4. import org.apache.commons.math.linear.RealVector
  5.  
  6. /**
  7.  * https://www.reddit.com/r/dailyprogrammer/comments/6ksmh5/20170630_challenge_321_hard_circle_splitter/
  8.  */
  9. val EPSILON = 0.00001
  10.  
  11. fun main(args: Array<String>) {
  12.     val inputCoordinates = args.drop(1).map(String::toDouble)
  13.     val xs = inputCoordinates.filterIndexed { index, _ -> even(index) }
  14.     val ys = inputCoordinates.filterIndexed { index, _ -> odd(index) }
  15.     val pairedCoordinates = xs.zip(ys);
  16.     val points = pairedCoordinates.map { PointX(arrayOf(it.first, it.second)) }
  17.  
  18.     val bestCircle = solve(points)
  19.  
  20.     // TODO Iterate on the circle, increasing / decreasing size slightly to compensate for the errors in matrix solving
  21.  
  22.     if (bestCircle != null) {
  23.         println(bestCircle.center.vector.data.map(Double::toString).joinToString(separator = " "))
  24.         println(bestCircle.radius)
  25.     } else {
  26.         println("No solution")
  27.     }
  28. }
  29.  
  30. fun even(i: Int) = i % 2 == 0
  31. fun odd(i: Int) = i % 2 == 1
  32.  
  33. fun solve(points: List<PointX>): CircleX? {
  34.     val scoredPoints = points.map({ point ->
  35.         val closestPoints = closestHalf(point, points)
  36.         val score = 1 / closestPoints.last().euclideanDistanceTo(point)
  37.         Triple(point, score, closestPoints)
  38.     }).sortedByDescending{ it.second }
  39.  
  40.     // Create an n-dimensional "square" at 0.5, 0.5, ...
  41.     val squareToFit = SquareX(PointX(Array(points[0].dimensions, {0.5})), 1.0)
  42.     for (point in scoredPoints) {
  43.         val minimumCircle = minimumBallMTF(point.third, listOf())
  44.         if (squareToFit.contains(minimumCircle)) {
  45.             return minimumCircle
  46.         }
  47.     }
  48.     return null
  49. }
  50.  
  51. /**
  52.  * Finds the N/2 points closest to this point among the given N points, sorted by ascending distance
  53.  */
  54. fun closestHalf(primary: PointX, all: List<PointX>): List<PointX> {
  55.     val sortedPoints = all.sortedBy { it.euclideanDistanceTo(primary) }
  56.     return sortedPoints.subList(0, all.size / 2)
  57. }
  58.  
  59. data class PointX(val vector: RealVector) {
  60.     val dimensions = vector.dimension
  61.  
  62.     constructor(positions: Array<Double>): this(ArrayRealVector(positions))
  63.  
  64.     fun euclideanDistanceTo(other: PointX): Double = vector.getDistance(other.vector)
  65.     fun longestOrthogonalDistanceTo(other: PointX): Double = vector.getLInfDistance(other.vector)
  66. }
  67.  
  68. fun origo(dimensions: Int): PointX {
  69.     return PointX(Array(dimensions, {0.0}))
  70. }
  71.  
  72. data class CircleX(val center: PointX, val radius: Double) {
  73.     fun contains(point: PointX): Boolean {
  74.         // Sometimes we need an "empty" circle without knowing how many dimensions it has
  75.         // That circle should never contain anything
  76.         if (center.dimensions == 0) return false
  77.  
  78.         return center.euclideanDistanceTo(point) <= radius + EPSILON;
  79.     }
  80.     val area = Math.pow(radius, center.dimensions.toDouble())
  81. }
  82.  
  83. data class SquareX(val center: PointX, val sideLength: Double) {
  84.     fun contains(point: PointX): Boolean {
  85.         return center.longestOrthogonalDistanceTo(point) <= sideLength / 2 + EPSILON
  86.     }
  87.  
  88.     fun contains(circle: CircleX): Boolean {
  89.         return center.longestOrthogonalDistanceTo(circle.center) + circle.radius <= sideLength / 2 + EPSILON
  90.     }
  91. }
  92.  
  93.  
  94. // Based on https://people.inf.ethz.ch/gaertner/subdir/texts/own_work/esa99_final.pdf
  95. fun minimumBallMTF(points: List<PointX>, boundaryPoints: List<PointX>): CircleX {
  96.     var l = points;
  97.     var b = boundaryPoints;
  98.  
  99.     // Find the smallest ball which fits our boundary points
  100.     var mb = ballForBoundary(b)
  101.     if (l.isEmpty()) return mb
  102.  
  103.     // If we have enough boundary points, it is guaranteed to fit all points
  104.     if (b.size == points[0].dimensions + 1)
  105.         return mb
  106.     // If not, find the points which do not fit and calculate a new ball which includes them
  107.     for (i in 0 until l.size) {
  108.         if (!mb.contains(l[i])) {
  109.             // Calculate a new ball with all previously included points, and the failing point on the edge
  110.             mb = minimumBallMTF(l.subList(0, i), b.plus(l[i]))
  111.             /*
  112.              * Reorder the list of points so that the now-included ones are
  113.              *  (1) kept track of and
  114.              *  (2) checked early in future recursive calls, as they have proved "important"
  115.              * Reordering the beginning of the list is safe because all of those points are included, and
  116.              * iteration will continue with the next potentially unincluded point
  117.              */
  118.             l = listOf(l[i]).union(l).toList()
  119.         }
  120.     }
  121.     return mb
  122. }
  123.  
  124. fun ballForBoundary(boundaryPoints: List<PointX>): CircleX {
  125.     if (boundaryPoints.size == 0)
  126.         return CircleX(PointX(arrayOf()), 0.0)
  127.     if (boundaryPoints.size == 1)
  128.         return CircleX(boundaryPoints[0], 0.0)
  129.  
  130.     val firstBoundaryVector = boundaryPoints[0].vector
  131.     val relativeBoundaryVectors = boundaryPoints.drop(1).map({ it.vector.subtract(firstBoundaryVector) })
  132.  
  133.     val a = Array2DRowRealMatrix(relativeBoundaryVectors.size, relativeBoundaryVectors.size)
  134.     for (row in 0 until relativeBoundaryVectors.size) {
  135.         for (col in 0 until relativeBoundaryVectors.size) {
  136.             a.setEntry(row, col, 2.0 * (relativeBoundaryVectors[row].dotProduct(relativeBoundaryVectors[col])))
  137.         }
  138.     }
  139.     val b = ArrayRealVector(relativeBoundaryVectors.map({ it.dotProduct(it) }).toDoubleArray())
  140.     val solver = LUDecompositionImpl(a).solver
  141.     try {
  142.         val x = solver.solve(b)
  143.         val dimensions = boundaryPoints[0].dimensions
  144.         val relativeCenter = x.data.zip(relativeBoundaryVectors).fold(origo(dimensions).vector, { acc, (weight, point) ->
  145.             acc.add(point.mapMultiply(weight))
  146.         })
  147.         val radius = Math.sqrt(relativeCenter.dotProduct(relativeCenter))
  148.         return CircleX(PointX(relativeCenter.add(firstBoundaryVector)), radius);
  149.     } catch (e: Exception) {
  150.         throw RuntimeException("Error when solving for points $boundaryPoints: ${e.message}")
  151.     }
  152. }
Add Comment
Please, Sign In to add comment