// Method rsearch translated from 352.range.c of Gonnet & Baeza-Yates public static void RangeSearch <T>(HPoint lowk, HPoint uppk, KDNode <T> t, int lev, int k, List <KDNode <T> > v) { if (t == null) { return; } if (lowk.Coord[lev] <= t.key.Coord[lev]) { RangeSearch(lowk, uppk, t.left, (lev + 1) % k, k, v); } if (!t.deleted) { int j = 0; while (j < k && lowk.Coord[j] <= t.key.Coord[j] && uppk.Coord[j] >= t.key.Coord[j]) { j++; } if (j == k) { v.Add(t); } } if (uppk.Coord[lev] > t.key.Coord[lev]) { RangeSearch(lowk, uppk, t.right, (lev + 1) % k, k, v); } }
private KDNode(HPoint key, T val) { this.key = key; this.value = val; this.left = null; this.right = null; this.deleted = false; }
/// <summary> ///Find KD-tree node whose key is identical to key. Uses algorithm ///translated from 352.srch.c of Gonnet & Baeza-Yates. /// </summary> /// <param name="key">key for KD-tree node</param> /// <returns>Element associated to key</returns> /// <exception cref="KeySizeException">if key.Length mismatches K</exception> public T Search(double[] key) { if (key.Length != this.m_K) { throw new KeySizeException(); } KDNode <T> kd = KDNode <T> .Search(new HPoint(key), this.m_root, this.m_K); return(kd == null ? default(T) : kd.Value); }
public static KDNode <T> Create <T>(HPoint key, IEditor <T> editor) { KDNode <T> t = new KDNode <T>(key, editor.Edit(default(T))); if (Equals(t.value, default(T))) { t.deleted = true; } return(t); }
public static bool Delete <T>(KDNode <T> t) { lock (t) { if (!t.deleted) { t.deleted = true; return(true); } } return(false); }
/// <summary> /// Edit a node in a KD-tree /// </summary> /// <param name="key">key for KD-tree node</param> /// <param name="editor">object to edit the value at that key</param> /// <exception cref="KeySizeException">if key.Length mismatches K</exception> /// <exception cref="KeyDuplicateException">if key already in tree</exception> public void Edit(double[] key, IEditor <T> editor) { if (key.Length != this.m_K) { throw new KeySizeException(); } lock (this) { // the first insert has to be lock if (null == this.m_root) { this.m_root = KDNode <T> .Create(new HPoint(key), editor); this.m_count = this.m_root.Deleted ? 0 : 1; return; } } this.m_count += KDNode <T> .Edit(new HPoint(key), editor, this.m_root, 0, this.m_K); }
private NearestNeighborList <KDNode <T> > getNbrs(double[] key, int n, Predicate <T> checker) { if (key.Length != this.m_K) { throw new KeySizeException(); } NearestNeighborList <KDNode <T> > nnl = new NearestNeighborList <KDNode <T> >(n); // initial call is with infinite hyper-rectangle and max distance HRect hr = HRect.InfiniteHRect(key.Length); double max_dist_sqd = Double.MaxValue; HPoint keyp = new HPoint(key); if (this.m_count > 0) { long timeout = (this.m_timeout > 0) ? (CurrentTimeMillis() + this.m_timeout) : 0; KDNode <T> .NearestNeighbors(this.m_root, keyp, hr, max_dist_sqd, 0, this.m_K, nnl, checker, timeout); } return(nnl); }
/// <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="n">how many neighbors to find</param> /// <param name="checker">an optional object to filter matches</param> /// <returns>objects at node nearest to key, or null on failure</returns> /// <exception cref="KeySizeException">if key.Length mismatches K</exception> public List <T> Nearest(double[] key, int n, Predicate <T> checker) { if (n <= 0) { return(new List <T>()); } NearestNeighborList <KDNode <T> > nnl = this.getNbrs(key, n, checker); n = nnl.Count; List <T> nbrs = new List <T>(); for (int i = 0; i < n; ++i) { KDNode <T> kd = nnl.RemoveHighest(); nbrs.Add(kd.Value); } //nbrs.Reverse(); return(nbrs); }
// Method srch translated from 352.srch.c of Gonnet & Baeza-Yates public static KDNode <T> Search <T>(HPoint key, KDNode <T> t, int K) { for (int lev = 0; t != null; lev = (lev + 1) % K) { if (!t.deleted && key.Equals(t.key)) { return(t); } else if (key.Coord[lev] > t.key.Coord[lev]) { t = t.right; } else { t = t.left; } } return(null); }
// Method ins translated from 352.ins.c of Gonnet & Baeza-Yates public static int Edit <T>(HPoint key, IEditor <T> editor, KDNode <T> t, int lev, int k) { KDNode <T> next_node; int nextLev = (lev + 1) % k; lock (t) { if (key.Equals(t.key)) { bool wasDeleted = t.deleted; t.value = editor.Edit(t.deleted ? default(T) : t.value); t.deleted = (t.value == null); if (t.deleted == wasDeleted) { return(0); } else if (wasDeleted) { return(-1); } return(1); } else if (key.Coord[lev] > t.key.Coord[lev]) { next_node = t.right; if (next_node == null) { t.right = Create(key, editor); return(t.right.deleted ? 0 : 1); } } else { next_node = t.left; if (next_node == null) { t.left = Create(key, editor); return(t.left.deleted ? 0 : 1); } } } return(Edit(key, editor, next_node, nextLev, k)); }
private List <T> nearestDistance(double[] key, double dist, DistanceMetric metric) { NearestNeighborList <KDNode <T> > nnl = this.getNbrs(key); int n = nnl.Count; List <T> nbrs = new List <T>(); for (int i = 0; i < n; ++i) { KDNode <T> kd = nnl.RemoveHighest(); HPoint p = kd.Key; //HACK metric.Distance(kd.Key.Coord, key) < dist changed to - metric.Distance(kd.Key.Coord, key) <= dist if (metric.Distance(kd.Key.Coord, key) <= dist) { nbrs.Add(kd.Value); } } //HACK Verificar se o reverse é necessario //nbrs.Reverse(); return(nbrs); }
/// <summary> /// Range search in a KD-tree. Uses algorithm translated from 352.range.c of /// Gonnet & Baeza-Yates. /// </summary> /// <param name="lowk">lower-bounds for key</param> /// <param name="uppk">upper-bounds for key</param> /// <returns>array of Objects whose keys fall in range [lowk,uppk]</returns> /// <exception cref="KeySizeException">on mismatch among lowk.Length, uppk.Length, or K</exception> public List <T> Range(double[] lowk, double[] uppk) { if (lowk.Length != uppk.Length) { throw new KeySizeException(); } else if (lowk.Length != this.m_K) { throw new KeySizeException(); } else { List <KDNode <T> > found = new List <KDNode <T> >(); KDNode <T> .RangeSearch(new HPoint(lowk), new HPoint(uppk), this.m_root, 0, this.m_K, found); List <T> o = new List <T>(); foreach (KDNode <T> node in found) { o.Add(node.Value); } return(o); } }
/// <summary> /// Delete a node from a KD-tree. Instead of actually deleting node and /// rebuilding tree, marks node as deleted. Hence, it is up to the caller to /// rebuild the tree as needed for efficiency. /// </summary> /// <param name="key">key for KD-tree node</param> /// <param name="optional">if false and node not found, throw an exception</param> /// <exception cref="KeyMissingException">if no node in tree has key</exception> /// <exception cref="KeySizeException">if key.Length mismatches K</exception> public void Delete(double[] key, bool optional) { if (key.Length != this.m_K) { throw new KeySizeException(); } KDNode <T> t = KDNode <T> .Search(new HPoint(key), this.m_root, this.m_K); if (t == null) { if (optional == false) { throw new KeyMissingException(); } } else { if (KDNode <T> .Delete(t)) { this.m_count--; } } }
public KDTree(int k, long timeout) { this.m_timeout = timeout; this.m_K = k; this.m_root = null; }
// Method Nearest Neighbor from Andrew Moore's thesis. Numbered // comments are direct quotes from there. NearestNeighborList solution // courtesy of Bjoern Heckel. public static void NearestNeighbors <T>(KDNode <T> kd, HPoint target, HRect hr, double max_dist_sqd, int lev, int K, NearestNeighborList <KDNode <T> > nnl, Predicate <T> checker, long timeout) { // 1. if kd is empty then set dist-sqd to infinity and exit. if (kd == null) { return; } if ((timeout > 0) && (timeout < DateTime.Now.TimeOfDay.TotalMilliseconds)) { return; } // 2. s := split field of kd int s = lev % K; // 3. pivot := dom-elt field of kd HPoint pivot = kd.key; double pivot_to_target = HPoint.SqrDist(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 left_hr = hr; // optimize by not cloning HRect right_hr = (HRect)hr.Clone(); left_hr.Max.Coord[s] = pivot.Coord[s]; right_hr.Min.Coord[s] = pivot.Coord[s]; // 5. target-in-left := target_s <= pivot_s bool target_in_left = target.Coord[s] < pivot.Coord[s]; KDNode <T> nearer_kd; HRect nearer_hr; KDNode <T> further_kd; HRect further_hr; // 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 if (target_in_left) { nearer_kd = kd.left; nearer_hr = left_hr; further_kd = kd.right; further_hr = right_hr; } // // 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 else { nearer_kd = kd.right; nearer_hr = right_hr; further_kd = kd.left; further_hr = left_hr; } // 8. Recursively call Nearest Neighbor with paramters // (nearer-kd, target, nearer-hr, max-dist-sqd), storing the // results in nearest and dist-sqd NearestNeighbors(nearer_kd, target, nearer_hr, max_dist_sqd, lev + 1, K, nnl, checker, timeout); // KDNode<T> nearest = nnl.getHighest(); double dist_sqd; if (!nnl.IsCapacityReached()) { dist_sqd = Double.MaxValue; } else { dist_sqd = nnl.MaxPriority; } // 9. max-dist-sqd := minimum of max-dist-sqd and dist-sqd max_dist_sqd = Math.Min(max_dist_sqd, dist_sqd); // 10. A nearer point could only lie in further-kd if there were some // part of further-hr within distance max-dist-sqd of // target. HPoint closest = further_hr.Closest(target); if (HPoint.SqrDist(closest, target) < max_dist_sqd) { // 10.1 if (pivot-target)^2 < dist-sqd then if (pivot_to_target < dist_sqd) { // 10.1.1 nearest := (pivot, range-elt field of kd) // nearest = kd; // 10.1.2 dist-sqd = (pivot-target)^2 dist_sqd = pivot_to_target; // add to nnl if (!kd.deleted && ((checker == null) || checker.Invoke(kd.value))) { nnl.Insert(kd, dist_sqd); } // 10.1.3 max-dist-sqd = dist-sqd // max_dist_sqd = dist_sqd; if (nnl.IsCapacityReached()) { max_dist_sqd = nnl.MaxPriority; } else { max_dist_sqd = 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 NearestNeighbors(further_kd, target, further_hr, max_dist_sqd, lev + 1, K, nnl, checker, timeout); } }