/// <summary> /// Find KD-tree nodes whose keys are <I>n</I> nearest neighbors to /// key. Uses algorithm above. Neighbors are returned in ascending /// order of distance to key. /// </summary> /// <param name="key">key for KD-tree node</param> /// <param name="numNeighbors">The Integer showing how many neighbors to find</param> /// <returns>An array of objects at the node nearest to the key</returns> /// <exception cref="KeySizeException">Mismatch if key length doesn't match the dimension for the tree</exception> /// <exception cref="NeighborsOutOfRangeException">if <I>n</I> is negative or exceeds tree size </exception> public object[] Nearest(double[] key, int numNeighbors) { if (numNeighbors < 0 || numNeighbors > _count) { throw new NeighborsOutOfRangeException(); } if (key.Length != _k) { throw new KeySizeException(); } object[] neighbors = new object[numNeighbors]; NearestNeighborList nnl = new NearestNeighborList(numNeighbors); // initial call is with infinite hyper-rectangle and max distance HRect hr = HRect.InfiniteHRect(key.Length); const double maxDistSqd = 1.79769e+30; HPoint keyp = new HPoint(key); KdNode.Nnbr(_root, keyp, hr, maxDistSqd, 0, _k, nnl); for (int i = 0; i < numNeighbors; ++i) { KdNode kd = (KdNode)nnl.RemoveHighest(); neighbors[numNeighbors - i - 1] = kd.V; } return(neighbors); }
/// <summary> /// Method Nearest Neighbor from Andrew Moore's thesis. Numbered /// comments are direct quotes from there. Step "SDL" is added to /// make the algorithm work correctly. NearestNeighborList solution /// courtesy of Bjoern Heckel. /// </summary> /// <param name="kd"></param> /// <param name="target"></param> /// <param name="hr"></param> /// <param name="maxDistSqd"></param> /// <param name="lev"></param> /// <param name="k"></param> /// <param name="nnl"></param> public static void Nnbr(KdNode kd, HPoint target, HRect hr, double maxDistSqd, int lev, int k, NearestNeighborList nnl) { // 1. if kd is empty then set dist-sqd to infinity and exit. if (kd == null) { return; } // 2. s := split field of kd int s = lev % k; // 3. pivot := dom-elt field of kd HPoint pivot = kd.K; double pivotToTarget = HPoint.SquareDistance(pivot, target); // 4. Cut hr into to sub-hyperrectangles left-hr and right-hr. // The cut plane is through pivot and perpendicular to the s // dimension. HRect leftHr = hr; // optimize by not cloning HRect rightHr = hr.Copy(); leftHr.Max[s] = pivot[s]; rightHr.Min[s] = pivot[s]; // 5. target-in-left := target_s <= pivot_s bool targetInLeft = target[s] < pivot[s]; KdNode nearerKd; HRect nearerHr; KdNode furtherKd; HRect furtherHr; if (targetInLeft) { // 6. if target-in-left then // 6.1. nearer-kd := left field of kd and nearer-hr := left-hr // 6.2. further-kd := right field of kd and further-hr := right-hr nearerKd = kd.Left; nearerHr = leftHr; furtherKd = kd.Right; furtherHr = rightHr; } else { // // 7. if not target-in-left then // 7.1. nearer-kd := right field of kd and nearer-hr := right-hr // 7.2. further-kd := left field of kd and further-hr := left-hr nearerKd = kd.Right; nearerHr = rightHr; furtherKd = kd.Left; furtherHr = leftHr; } // 8. Recursively call Nearest Neighbor with paramters // (nearer-kd, target, nearer-hr, max-dist-sqd), storing the // results in nearest and dist-sqd Nnbr(nearerKd, target, nearerHr, maxDistSqd, lev + 1, k, nnl); double distSqd; if (!nnl.IsCapacityReached) { distSqd = 1.79769e+30; // Double.MaxValue; } else { distSqd = nnl.MaxPriority; } // 9. max-dist-sqd := minimum of max-dist-sqd and dist-sqd maxDistSqd = Math.Min(maxDistSqd, distSqd); // 10. A nearer point could only lie in further-kd if there were some // part of further-hr within distance sqrt(max-dist-sqd) of // target. If this is the case then HPoint closest = furtherHr.Closest(target); if (HPoint.EuclideanDistance(closest, target) < Math.Sqrt(maxDistSqd)) { // 10.1 if (pivot-target)^2 < dist-sqd then if (pivotToTarget < distSqd) { // 10.1.1 nearest := (pivot, range-elt field of kd) // 10.1.2 dist-sqd = (pivot-target)^2 distSqd = pivotToTarget; // add to nnl if (!kd.IsDeleted) { nnl.Insert(kd, distSqd); } // 10.1.3 max-dist-sqd = dist-sqd // max_dist_sqd = dist_sqd; if (nnl.IsCapacityReached) { maxDistSqd = nnl.MaxPriority; } else { maxDistSqd = 1.79769e+30; //Double.MaxValue; } } // 10.2 Recursively call Nearest Neighbor with parameters // (further-kd, target, further-hr, max-dist_sqd), // storing results in temp-nearest and temp-dist-sqd Nnbr(furtherKd, target, furtherHr, maxDistSqd, lev + 1, k, nnl); double tempDistSqd = nnl.MaxPriority; // 10.3 If tmp-dist-sqd < dist-sqd then if (tempDistSqd < distSqd) { // 10.3.1 nearest := temp_nearest and dist_sqd := temp_dist_sqd distSqd = tempDistSqd; } } else if (pivotToTarget < maxDistSqd) { // SDL: otherwise, current point is nearest distSqd = pivotToTarget; } }
/// <summary> /// Find KD-tree nodes whose keys are <I>n</I> nearest neighbors to /// key. Uses algorithm above. Neighbors are returned in ascending /// order of distance to key. /// </summary> /// <param name="key">key for KD-tree node</param> /// <param name="numNeighbors">The Integer showing how many neighbors to find</param> /// <returns>An array of objects at the node nearest to the key</returns> /// <exception cref="KeySizeException">Mismatch if key length doesn't match the dimension for the tree</exception> /// <exception cref="NeighborsOutOfRangeException">if <I>n</I> is negative or exceeds tree size </exception> public object[] Nearest(double[] key, int numNeighbors) { if (numNeighbors < 0 || numNeighbors > _count) throw new NeighborsOutOfRangeException(); if (key.Length != _k) throw new KeySizeException(); object[] neighbors = new object[numNeighbors]; NearestNeighborList nnl = new NearestNeighborList(numNeighbors); // initial call is with infinite hyper-rectangle and max distance HRect hr = HRect.InfiniteHRect(key.Length); const double maxDistSqd = 1.79769e+30; HPoint keyp = new HPoint(key); KdNode.Nnbr(_root, keyp, hr, maxDistSqd, 0, _k, nnl); for (int i = 0; i < numNeighbors; ++i) { KdNode kd = (KdNode)nnl.RemoveHighest(); neighbors[numNeighbors - i - 1] = kd.V; } return neighbors; }