Difference between revisions of "User:Chase-san/Kd-Tree"
Jump to navigation
Jump to search
(Reduced the source some.) |
(Updated, now with nearest N neighbors) |
||
Line 1: | Line 1: | ||
[[Category:Code Snippets|Kd-Tree]] | [[Category:Code Snippets|Kd-Tree]] | ||
− | + | Everyone and their brother has one of these now, me and Simonton started it, but I was to inexperienced to get anything written, I took an hour or two to rewrite it today, because I am no longer completely terrible at these things. So here is mine if you care to see it. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
=== KDTreeB === | === KDTreeB === | ||
<pre> | <pre> | ||
package org.csdgn.util; | package org.csdgn.util; | ||
+ | |||
+ | import java.util.Comparator; | ||
public class KDTreeB { | public class KDTreeB { | ||
− | public final static int DEFAULT_BUCKET_SIZE = | + | public final static int DEFAULT_BUCKET_SIZE = 200; |
− | protected | + | protected NodeKD root; |
protected int dimensions; | protected int dimensions; | ||
− | protected | + | protected int bucket_size; |
− | protected | + | protected long size = 0; |
− | + | ||
− | + | /** | |
+ | * This creates a KDTreeB with the given number of dimensions and | ||
+ | * the default bucket size. | ||
+ | * @param dimensions | ||
+ | */ | ||
+ | public KDTreeB(int dimensions) { | ||
+ | this(dimensions,DEFAULT_BUCKET_SIZE); | ||
+ | } | ||
+ | /** | ||
+ | * This creates a KDTreeB with the given number of dimensions and | ||
+ | * the given bucket size. | ||
+ | * @param dimensions | ||
+ | * @param bucket_size | ||
+ | */ | ||
+ | public KDTreeB(int dimensions, int bucket_size) { | ||
+ | this.dimensions = dimensions; | ||
+ | this.bucket_size = bucket_size; | ||
+ | root = new BucketKD(this); | ||
+ | } | ||
+ | |||
/** | /** | ||
− | * | + | * Adds the given point to the tree. Uses a recursive algorithm. |
− | + | * @param point | |
− | * @param | ||
− | |||
*/ | */ | ||
− | public | + | public void add(PointKD point) { |
− | + | root.add(point); | |
} | } | ||
− | + | ||
/** | /** | ||
− | * | + | * Returns the nearest neighbor to the given point. |
− | + | * @param point - PointKD to find the nearest to. | |
− | * @param | + | * @return - The nearest PointKD to |
− | |||
− | * @ | ||
− | |||
*/ | */ | ||
− | public | + | public PointKD getNearestNeighbor(PointKD point) { |
− | + | return root.nearest(point); | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | + | ||
/** | /** | ||
− | * | + | * Returns the nearest num neighbors to the given point. |
− | * | + | * @param point - PointKD to find the nearest to. |
− | * @param | + | * @param num - The number of points to find. |
− | * | + | * @return - The nearest PointKD's to point. |
*/ | */ | ||
− | public | + | public PointKD[] getNearestNeighbors(final PointKD point, int num) { |
− | if ( | + | if(num == 1) { |
− | + | return new PointKD[] { getNearestNeighbor(point) }; | |
} | } | ||
− | + | PriorityDeque<PointKD> queue = new PriorityDeque<PointKD>( | |
− | + | new Comparator<PointKD>(){ | |
− | + | @Override | |
− | + | public int compare(PointKD o1, PointKD o2) { | |
− | + | double dist0 = point.distance(o1); | |
− | + | double dist1 = point.distance(o2); | |
+ | return (dist0-dist1 < 0) ? -1 : 1; | ||
+ | } | ||
+ | },num); | ||
+ | |||
+ | root.nearestn(queue,point); | ||
+ | |||
+ | PointKD[] array = new PointKD[num]; | ||
+ | Object[] obj = queue.toArray(); | ||
+ | for(int i=0; i<num; ++i) { | ||
+ | array[i] = (PointKD)obj[i]; | ||
} | } | ||
− | + | ||
− | + | return array; | |
} | } | ||
− | + | ||
/** | /** | ||
− | * | + | * Returns all PointKD within a certain RectangleKD. |
− | + | * @param rect - area to get PointKD from | |
− | + | * @return - All PointKD within rect. | |
− | * @param | ||
− | * @return | ||
*/ | */ | ||
− | public PointKD | + | public PointKD[] getRange(RectangleKD rect) { |
− | + | return root.range(rect); | |
− | |||
− | return root. | ||
} | } | ||
/** | /** | ||
− | * | + | * Returns all PointKD within a certain range defined by an upper and lower PointKD. |
− | * | + | * @param low - lower bounds of area |
− | * @param | + | * @param high - upper bounds of area |
− | * @return | + | * @return - All PointKD between low and high. |
*/ | */ | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
public PointKD[] getRange(PointKD low, PointKD high) { | public PointKD[] getRange(PointKD low, PointKD high) { | ||
− | return this.getRange(new | + | return this.getRange(new RectangleKD(low, high)); |
} | } | ||
− | |||
− | + | protected abstract class NodeKD { | |
− | protected KDTreeB | + | protected KDTreeB owner; |
protected BranchKD parent; | protected BranchKD parent; | ||
− | protected | + | protected RectangleKD rect; |
− | protected | + | protected int depth = 0; |
− | + | ||
protected abstract void add(PointKD k); | protected abstract void add(PointKD k); | ||
− | |||
protected abstract PointKD nearest(PointKD k); | protected abstract PointKD nearest(PointKD k); | ||
− | protected abstract PointKD[] range( | + | protected abstract void nearestn(PriorityDeque<PointKD> queue, PointKD k); |
+ | protected abstract PointKD[] range(RectangleKD r); | ||
} | } | ||
− | + | protected class BucketKD extends NodeKD { | |
− | |||
protected PointKD bucket[]; | protected PointKD bucket[]; | ||
protected int current; | protected int current; | ||
− | + | ||
− | + | public BucketKD(KDTreeB owner) { | |
− | + | this.owner = owner; | |
− | + | bucket = new PointKD[owner.bucket_size]; | |
− | bucket = new PointKD[ | + | rect = new RectangleKD(); |
parent = null; | parent = null; | ||
− | + | } | |
− | depth = | + | public BucketKD(BranchKD branch) { |
+ | this(branch.owner); | ||
+ | parent = branch; | ||
+ | depth = branch.depth + 1; | ||
} | } | ||
− | protected | + | @Override |
− | + | protected void add(PointKD point) { | |
− | + | if(current >= bucket.length) { | |
− | + | //Split the bucket into a branch | |
− | + | BranchKD branch = new BranchKD(this); | |
− | + | if(parent == null) { | |
+ | owner.root = branch; | ||
+ | } else { | ||
+ | if(parent.isLeft(this)) { | ||
+ | parent.left = branch; | ||
+ | } else { | ||
+ | parent.right = branch; | ||
+ | } | ||
+ | } | ||
+ | branch.add(point); | ||
+ | bucket = null; | ||
+ | current = 0; | ||
+ | return; | ||
+ | } | ||
+ | bucket[current++] = point; | ||
+ | rect.expand(point); | ||
} | } | ||
− | protected PointKD | + | @Override |
+ | protected PointKD nearest(PointKD k) { | ||
double nearestDist = Double.POSITIVE_INFINITY; | double nearestDist = Double.POSITIVE_INFINITY; | ||
int nearest = 0; | int nearest = 0; | ||
Line 152: | Line 165: | ||
return bucket[nearest]; | return bucket[nearest]; | ||
} | } | ||
− | + | ||
− | + | @Override | |
− | + | protected void nearestn(PriorityDeque<PointKD> queue, PointKD k) { | |
− | + | for(int i = 0; i < current; i++) { | |
− | + | queue.offer(bucket[i]); | |
− | protected void | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
} | } | ||
− | |||
− | |||
} | } | ||
− | protected PointKD[] range( | + | @Override |
+ | protected PointKD[] range(RectangleKD r) { | ||
PointKD[] tmp = new PointKD[current]; | PointKD[] tmp = new PointKD[current]; | ||
int n = 0; | int n = 0; | ||
Line 196: | Line 186: | ||
return tmp2; | return tmp2; | ||
} | } | ||
+ | |||
} | } | ||
− | + | protected class BranchKD extends NodeKD { | |
− | |||
protected NodeKD left, right; | protected NodeKD left, right; | ||
protected double slice; | protected double slice; | ||
− | + | protected int dim; | |
− | + | ||
+ | public BranchKD(BucketKD k) { | ||
+ | owner = k.owner; | ||
+ | parent = k.parent; | ||
slice = 0; | slice = 0; | ||
− | + | rect = k.rect; | |
− | + | depth = k.depth; | |
− | depth = | ||
− | |||
left = new BucketKD(this); | left = new BucketKD(this); | ||
right = new BucketKD(this); | right = new BucketKD(this); | ||
+ | |||
+ | dim = depth % owner.dimensions; | ||
+ | double total = 0; | ||
+ | for (int i = 0; i < k.current; i++) { | ||
+ | total += k.bucket[i].internal[dim]; | ||
+ | } | ||
+ | slice = total / k.current; | ||
+ | for (int i = 0; i < k.current; i++) | ||
+ | add(k.bucket[i]); | ||
} | } | ||
− | + | ||
− | + | @Override | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
protected void add(PointKD k) { | protected void add(PointKD k) { | ||
− | + | if(k.internal[dim] > slice) { | |
− | |||
− | if (k. | ||
right.add(k); | right.add(k); | ||
} else { | } else { | ||
Line 231: | Line 221: | ||
} | } | ||
− | + | @Override | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
protected PointKD nearest(PointKD k) { | protected PointKD nearest(PointKD k) { | ||
− | |||
PointKD near = null; | PointKD near = null; | ||
− | if (k. | + | if (k.internal[dim] > slice) { |
near = right.nearest(k); | near = right.nearest(k); | ||
double t = near.distanceSq(k); | double t = near.distanceSq(k); | ||
Line 260: | Line 243: | ||
} | } | ||
} | } | ||
− | |||
return near; | return near; | ||
+ | } | ||
+ | |||
+ | @Override | ||
+ | protected void nearestn(PriorityDeque<PointKD> queue, PointKD k) { | ||
+ | //TODO | ||
+ | if(k.internal[dim] > slice) { | ||
+ | right.nearestn(queue, k); | ||
+ | double t = queue.peekBottom().distanceSq(k); | ||
+ | if(k.distanceSq(left.rect.getNearest(k)) < t) { | ||
+ | left.nearestn(queue, k); | ||
+ | } | ||
+ | } else { | ||
+ | left.nearestn(queue, k); | ||
+ | double t = queue.peekBottom().distanceSq(k); | ||
+ | if(k.distanceSq(right.rect.getNearest(k)) < t) { | ||
+ | right.nearestn(queue, k); | ||
+ | } | ||
+ | } | ||
} | } | ||
− | protected PointKD[] range( | + | @Override |
+ | protected PointKD[] range(RectangleKD r) { | ||
PointKD[] tmp = new PointKD[0]; | PointKD[] tmp = new PointKD[0]; | ||
if (r.intersects(left.rect)) { | if (r.intersects(left.rect)) { | ||
Line 284: | Line 285: | ||
return tmp; | return tmp; | ||
} | } | ||
− | + | ||
− | protected | + | protected boolean isLeft(BucketKD kd) { |
− | if ( | + | if(left == kd) return true; |
− | + | return false; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | return | ||
} | } | ||
} | } | ||
Line 304: | Line 297: | ||
<pre> | <pre> | ||
package org.csdgn.util; | package org.csdgn.util; | ||
+ | |||
+ | import java.io.Serializable; | ||
/** | /** | ||
− | * | + | * PointKD class is a class that wraps a double array, for use in K-Dimensional structures. |
− | * | + | * This wrapping is done to improve readability and modularity. Often used to define a |
− | * | + | * point in k-dimensional space. |
− | |||
* | * | ||
+ | * @author Chase | ||
*/ | */ | ||
− | public class PointKD { | + | public class PointKD implements Serializable { |
− | protected double[] | + | private static final long serialVersionUID = -841162798668123755L; |
− | + | protected double[] internal; | |
+ | |||
/** | /** | ||
− | * Constructor | + | * Constructor |
− | + | * @param dimensions - Number of dimensions for this PointKD. | |
− | * @param dimensions | ||
− | |||
*/ | */ | ||
public PointKD(int dimensions) { | public PointKD(int dimensions) { | ||
− | + | internal = new double[dimensions]; | |
+ | } | ||
+ | |||
+ | /** | ||
+ | * Constructor | ||
+ | * @param array - An array to use for the internal array of this PointKD. | ||
+ | */ | ||
+ | public PointKD(double[] array) { | ||
+ | internal = array.clone(); | ||
} | } | ||
− | + | ||
/** | /** | ||
− | * | + | * Constructor |
− | + | * @param point - PointKD to clone. | |
− | * @param | ||
− | |||
*/ | */ | ||
− | public PointKD( | + | public PointKD(PointKD point) { |
− | + | this.internal = point.internal.clone(); | |
− | |||
− | |||
} | } | ||
− | + | ||
+ | |||
/** | /** | ||
− | * | + | * Returns array. |
− | + | * @return The internal array of this pointKD, changes made to this will effect this PointKD. | |
− | * @ | ||
− | |||
*/ | */ | ||
− | public | + | public double[] get() { |
− | + | return internal; | |
− | |||
} | } | ||
− | + | ||
/** | /** | ||
− | * Sets the | + | * Sets the location of this point. |
− | + | * @param point - PointKD to copy. | |
− | * @param | ||
− | |||
*/ | */ | ||
− | public void | + | public void set(PointKD point) { |
− | this. | + | this.internal = point.internal.clone(); |
− | |||
} | } | ||
− | + | ||
/** | /** | ||
− | * | + | * Sets the location of this point. |
− | + | * @param array - Array to copy. | |
− | * @ | ||
*/ | */ | ||
− | public | + | public void set(double[] array) { |
− | + | this.internal = array.clone(); | |
} | } | ||
− | + | ||
/** | /** | ||
− | * | + | * Sets the value at dimension index |
− | * | + | * @param index - Index |
− | * @param | + | * @param value - Value to set |
− | |||
− | |||
*/ | */ | ||
− | public | + | public void set(int index, double value) { |
− | + | internal[index] = value; | |
} | } | ||
− | + | ||
/** | /** | ||
− | + | * @return number of dimensions | |
− | |||
− | * @ | ||
− | |||
− | |||
− | |||
*/ | */ | ||
− | public | + | public int size() { |
− | + | return internal.length; | |
} | } | ||
− | + | ||
/** | /** | ||
* Compares this to a selected point and returns the euclidean distance | * Compares this to a selected point and returns the euclidean distance | ||
Line 400: | Line 385: | ||
*/ | */ | ||
public double distance(PointKD p) { | public double distance(PointKD p) { | ||
− | return distance(this, p); | + | return distance(this.internal, p.internal); |
} | } | ||
Line 412: | Line 397: | ||
*/ | */ | ||
public double distanceSq(PointKD p) { | public double distanceSq(PointKD p) { | ||
− | return distanceSq(this, p); | + | return distanceSq(this.internal, p.internal); |
+ | } | ||
+ | |||
+ | /** | ||
+ | * Clones this point. | ||
+ | */ | ||
+ | public PointKD clone() { | ||
+ | return new PointKD(internal); | ||
} | } | ||
− | + | ||
/** | /** | ||
* Prints the class name and the point coordinates. | * Prints the class name and the point coordinates. | ||
*/ | */ | ||
public String toString() { | public String toString() { | ||
− | String output = getClass(). | + | String output = getClass().getSimpleName() + "["; |
− | for (int i = 0; i < | + | for (int i = 0; i < internal.length; ++i) { |
if (0 != i) | if (0 != i) | ||
output += ","; | output += ","; | ||
− | output += | + | output += internal[i]; |
} | } | ||
return output + "]"; | return output + "]"; | ||
} | } | ||
− | + | ||
/** | /** | ||
− | * | + | * Compares arrays of double and returns the euclidean distance |
− | + | * between them. | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
* | * | ||
− | * @param a | + | * @param a - The first set of numbers |
− | + | * @param b - The second set of numbers | |
− | * @param b | + | * @return The distance squared between <b>a</b> and <b>b</b>. |
− | |||
− | * @return The distance | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
*/ | */ | ||
public static final double distance(double[] a, double[] b) { | public static final double distance(double[] a, double[] b) { | ||
− | return Math.sqrt( | + | double total = 0; |
+ | for (int i = 0; i < a.length; ++i) | ||
+ | total += (b[i] - a[i]) * (b[i] - a[i]); | ||
+ | return Math.sqrt(total); | ||
} | } | ||
− | + | ||
/** | /** | ||
* Compares arrays of double and returns the squared euclidean distance | * Compares arrays of double and returns the squared euclidean distance | ||
* between them. | * between them. | ||
* | * | ||
− | * @param a | + | * @param a - The first set of numbers |
− | + | * @param b - The second set of numbers | |
− | * @param b | ||
− | |||
* @return The distance squared between <b>a</b> and <b>b</b>. | * @return The distance squared between <b>a</b> and <b>b</b>. | ||
*/ | */ | ||
public static final double distanceSq(double[] a, double[] b) { | public static final double distanceSq(double[] a, double[] b) { | ||
− | |||
− | |||
double total = 0; | double total = 0; | ||
− | for (int i = 0; i < a.length; | + | for (int i = 0; i < a.length; ++i) |
total += (b[i] - a[i]) * (b[i] - a[i]); | total += (b[i] - a[i]) * (b[i] - a[i]); | ||
return total; | return total; | ||
Line 497: | Line 452: | ||
</pre> | </pre> | ||
− | === | + | === RectangleKD === |
<pre> | <pre> | ||
package org.csdgn.util; | package org.csdgn.util; | ||
− | + | import java.io.Serializable; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
+ | public class RectangleKD implements Serializable { | ||
+ | private static final long serialVersionUID = 524388821816648020L; | ||
+ | protected PointKD upper, lower; | ||
/** | /** | ||
− | * Creates an empty | + | * Creates an empty RectangleKD |
*/ | */ | ||
− | public | + | public RectangleKD() { |
upper = null; | upper = null; | ||
lower = null; | lower = null; | ||
} | } | ||
− | + | /** | |
− | public | + | * Creates a RectangleKD from two points. |
− | this( | + | * @param lower |
+ | * @param upper | ||
+ | */ | ||
+ | public RectangleKD(PointKD lower, PointKD upper) { | ||
+ | this.lower = lower.clone(); | ||
+ | this.upper = upper.clone(); | ||
} | } | ||
− | + | ||
− | public | + | /** |
− | + | * Expands this RectangleKD to include point. | |
− | + | * @param point - Point used to expand this Rectangle | |
− | upper = | + | */ |
− | + | public void expand(PointKD point) { | |
+ | if(upper == null) { | ||
+ | upper = point.clone(); | ||
+ | lower = point.clone(); | ||
+ | return; | ||
+ | } | ||
+ | for(int i=0; i<point.size(); ++i) { | ||
+ | if(upper.internal[i] < point.internal[i]) | ||
+ | upper.internal[i] = point.internal[i]; | ||
+ | if(lower.internal[i] > point.internal[i]) | ||
+ | lower.internal[i] = point.internal[i]; | ||
} | } | ||
} | } | ||
− | + | ||
− | public | + | /** |
− | if (upper == null) | + | * Checks if this RectangleKD contains the given point. |
− | + | * @param point - PointKD to check. | |
− | lower | + | * @return true - if this rectangle contains the given point. |
− | + | */ | |
+ | public boolean contains(PointKD point) { | ||
+ | if(upper == null) return false; | ||
+ | for(int i=0; i<point.size(); ++i) { | ||
+ | if(point.internal[i] > upper.internal[i] || | ||
+ | point.internal[i] < lower.internal[i]) | ||
+ | return false; | ||
} | } | ||
− | + | return true; | |
− | for (int i = 0; i < upper. | + | } |
− | if ( | + | |
− | + | /** | |
− | + | * Checks if this table intersects the given table. | |
− | + | * @param rectangle - The rectangle to check intersection with. | |
+ | * @return true - if this rectangle intersects the given rectangle. | ||
+ | */ | ||
+ | public boolean intersects(RectangleKD rectangle) { | ||
+ | if(rectangle.upper == null || upper == null) return false; | ||
+ | for(int i=0; i<upper.size(); ++i) { | ||
+ | if(rectangle.upper.internal[i] < lower.internal[i] | ||
+ | || rectangle.lower.internal[i] > upper.internal[i]) return false; | ||
} | } | ||
+ | return true; | ||
} | } | ||
− | + | ||
− | public | + | /** |
− | if (upper == null) | + | * Gets the nearest point in this RectangleKD to the given point |
− | + | * @param point - PointKD to get the nearest point to. | |
− | if ( | + | * @return the nearest point in this RectangleKD to the given point. |
− | + | */ | |
− | + | public PointKD getNearest(PointKD point) { | |
− | for (int i = 0; i < upper. | + | if(upper == null) return null; |
− | if ( | + | if(contains(point)) return point; //This check may not be needed. |
− | + | PointKD nearest = new PointKD(point); | |
− | if ( | + | for(int i = 0; i < upper.size(); ++i) { |
− | + | if(nearest.internal[i] > upper.internal[i]) | |
+ | nearest.internal[i] = upper.internal[i]; | ||
+ | if(nearest.internal[i] < lower.internal[i]) | ||
+ | nearest.internal[i] = lower.internal[i]; | ||
} | } | ||
− | return | + | return nearest; |
} | } | ||
+ | } | ||
+ | </pre> | ||
− | + | === PriorityDeque === | |
− | + | <pre> | |
+ | package org.csdgn.util; | ||
− | + | import java.util.Comparator; | |
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | int | + | /** |
− | + | * This is a home made PriorityDeque with maximum size limiter, and | |
− | if ( | + | * comparator or natural ordering selection. |
− | + | * | |
− | if ( | + | * @author Chase |
− | + | * @param <E> | |
+ | */ | ||
+ | public class PriorityDeque<E> { | ||
+ | private class Item { | ||
+ | public Item down, up; | ||
+ | public E obj; | ||
+ | public Item(E item) { | ||
+ | obj = item; | ||
+ | down = up = null; | ||
+ | } | ||
+ | } | ||
+ | private final Comparator<? super E> comparator; | ||
+ | private Item bottom, top; | ||
+ | private int maximum_size; | ||
+ | private int size; | ||
+ | |||
+ | /** | ||
+ | * Generic Constructor | ||
+ | */ | ||
+ | public PriorityDeque() { | ||
+ | this(null,-1); | ||
+ | } | ||
+ | /** | ||
+ | * Constructor with a defined maximum number of entries | ||
+ | * @param maximum | ||
+ | */ | ||
+ | public PriorityDeque(int maximum) { | ||
+ | this(null,maximum); | ||
+ | } | ||
+ | /** | ||
+ | * Constructor with a defined comparator and an unlimited number of items. | ||
+ | * @param comp | ||
+ | */ | ||
+ | public PriorityDeque(Comparator<? super E> comp) { | ||
+ | this(comp,-1); | ||
+ | } | ||
+ | /** | ||
+ | * Constructor with a defined conparator and limited number of items. | ||
+ | * @param comp | ||
+ | * @param maximum | ||
+ | */ | ||
+ | public PriorityDeque(Comparator<? super E> comp, int maximum) { | ||
+ | comparator = comp; | ||
+ | maximum_size = maximum; | ||
+ | bottom = top = null; | ||
+ | size = 0; | ||
+ | } | ||
+ | /** | ||
+ | * Adds an item to this deque. | ||
+ | * @param value - item to add | ||
+ | */ | ||
+ | public void offer(E value) { | ||
+ | //System.err.println("Offering: " + value.toString()); | ||
+ | if(bottom == null) { | ||
+ | //System.err.println("-Is first item."); | ||
+ | bottom = top = new Item(value); | ||
+ | return; | ||
+ | } | ||
+ | //do ordering etc | ||
+ | if(comparator != null) { | ||
+ | //System.err.println("-Comparator."); | ||
+ | offerComparator(value); | ||
+ | } else { | ||
+ | //System.err.println("-Natural."); | ||
+ | offerNatural(value); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * Removes and returns an item from the bottom of the list (lowest value) | ||
+ | * @return the lowest value (bottom) | ||
+ | */ | ||
+ | public E pollBottom() { | ||
+ | if(bottom == null) return null; | ||
+ | Item tmp = bottom; | ||
+ | if(bottom == top) bottom = top = null; | ||
+ | else { | ||
+ | bottom = bottom.up; | ||
+ | bottom.down = null; | ||
+ | tmp.up = null; | ||
+ | } | ||
+ | --size; | ||
+ | return tmp.obj; | ||
+ | } | ||
+ | /** | ||
+ | * Returns but does not remove the item from the bottom of the list. | ||
+ | * @return the lowest value (bottom) | ||
+ | */ | ||
+ | public E peekBottom() { | ||
+ | if(bottom == null) return null; | ||
+ | return bottom.obj; | ||
+ | } | ||
+ | /** | ||
+ | * Removes and returns an item from the top of the list (highest value). | ||
+ | * @return the highest value (top) | ||
+ | */ | ||
+ | public E pollTop() { | ||
+ | if(top == null) return null; | ||
+ | Item tmp = top; | ||
+ | if(bottom == top) bottom = top = null; | ||
+ | else { | ||
+ | top = top.down; | ||
+ | top.up = null; | ||
+ | tmp.down = null; | ||
+ | } | ||
+ | --size; | ||
+ | return tmp.obj; | ||
+ | } | ||
+ | /** | ||
+ | * Returns but does not remove the item from the top of the list (highest value). | ||
+ | * @return the highest value (top) | ||
+ | */ | ||
+ | public E peekTop() { | ||
+ | if(top == null) return null; | ||
+ | return top.obj; | ||
+ | } | ||
+ | /** | ||
+ | * This is a generic toArray, returns a non-castable Object | ||
+ | * array with all the items in this deque. | ||
+ | * @return an Object array with everything in this deque. | ||
+ | */ | ||
+ | public Object[] toArray() { | ||
+ | Object array[] = new Object[size+1]; | ||
+ | Item current = bottom; | ||
+ | int i = 0; | ||
+ | while(current != null) { | ||
+ | array[i++] = current.obj; | ||
+ | current = current.up; | ||
+ | } | ||
+ | return array; | ||
+ | } | ||
+ | /** | ||
+ | * Add/sorts a value using the comparator | ||
+ | * @param value | ||
+ | */ | ||
+ | private void offerComparator(E value) { | ||
+ | if(maximum_size > 0 && size >= maximum_size) { | ||
+ | if(comparator.compare(value, top.obj) >= 0) { | ||
+ | return; | ||
+ | } | ||
+ | } | ||
+ | Item nItem = new Item(value); | ||
+ | if(comparator.compare(value,bottom.obj) < 0) { | ||
+ | //less than the bottom, put on the bottom | ||
+ | nItem.up = bottom; | ||
+ | bottom.down = nItem; | ||
+ | bottom = nItem; | ||
+ | ++size; | ||
+ | if(maximum_size > 0 && size > maximum_size) { | ||
+ | pollTop(); | ||
+ | } | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | //start at the bottom | ||
+ | Item current = bottom; | ||
+ | while(current != null) { | ||
+ | if(comparator.compare(value,current.obj) < 0) { | ||
+ | nItem.up = current; | ||
+ | nItem.down = current.down; | ||
+ | current.down.up = nItem; | ||
+ | current.down = nItem; | ||
+ | ++size; | ||
+ | if(maximum_size > 0 && size > maximum_size) { | ||
+ | pollTop(); | ||
+ | } | ||
+ | return; | ||
+ | } | ||
+ | current = current.up; | ||
+ | } | ||
+ | |||
+ | //else put it on top | ||
+ | nItem.down = top; | ||
+ | top.up = nItem; | ||
+ | top = nItem; | ||
+ | ++size; | ||
+ | if(maximum_size > 0 && size > maximum_size) { | ||
+ | pollTop(); | ||
} | } | ||
− | |||
− | |||
} | } | ||
− | + | ||
− | + | /** | |
− | if ( | + | * Add/sorts a value using the natural order |
− | + | * @param value | |
− | + | */ | |
− | + | @SuppressWarnings("unchecked") | |
− | + | private void offerNatural(E value) { | |
− | + | Comparable<? super E> key = (Comparable<? super E>)value; | |
− | + | if(maximum_size > 0 && size >= maximum_size) { | |
− | if ( | + | if(key.compareTo(top.obj) >= 0) { |
− | + | return; | |
− | + | } | |
− | + | } | |
+ | Item nItem = new Item(value); | ||
+ | if(key.compareTo(bottom.obj) < 0) { | ||
+ | //less than the bottom, put on the bottom | ||
+ | nItem.up = bottom; | ||
+ | bottom.down = nItem; | ||
+ | bottom = nItem; | ||
+ | ++size; | ||
+ | if(maximum_size > 0 && size > maximum_size) { | ||
+ | pollTop(); | ||
+ | } | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | //start at the bottom | ||
+ | Item current = bottom; | ||
+ | while(current != null) { | ||
+ | if(key.compareTo(current.obj) < 0) { | ||
+ | nItem.up = current; | ||
+ | nItem.down = current.down; | ||
+ | current.down.up = nItem; | ||
+ | current.down = nItem; | ||
+ | ++size; | ||
+ | if(maximum_size > 0 && size > maximum_size) { | ||
+ | pollTop(); | ||
+ | } | ||
+ | return; | ||
+ | } | ||
+ | current = current.up; | ||
} | } | ||
− | + | ||
+ | //else put it on top | ||
+ | nItem.down = top; | ||
+ | top.up = nItem; | ||
+ | top = nItem; | ||
+ | ++size; | ||
+ | if(maximum_size > 0 && size > maximum_size) { | ||
+ | pollTop(); | ||
+ | } | ||
} | } | ||
} | } | ||
+ | |||
</pre> | </pre> |
Revision as of 22:39, 1 March 2010
Everyone and their brother has one of these now, me and Simonton started it, but I was to inexperienced to get anything written, I took an hour or two to rewrite it today, because I am no longer completely terrible at these things. So here is mine if you care to see it.
Contents
KDTreeB
package org.csdgn.util; import java.util.Comparator; public class KDTreeB { public final static int DEFAULT_BUCKET_SIZE = 200; protected NodeKD root; protected int dimensions; protected int bucket_size; protected long size = 0; /** * This creates a KDTreeB with the given number of dimensions and * the default bucket size. * @param dimensions */ public KDTreeB(int dimensions) { this(dimensions,DEFAULT_BUCKET_SIZE); } /** * This creates a KDTreeB with the given number of dimensions and * the given bucket size. * @param dimensions * @param bucket_size */ public KDTreeB(int dimensions, int bucket_size) { this.dimensions = dimensions; this.bucket_size = bucket_size; root = new BucketKD(this); } /** * Adds the given point to the tree. Uses a recursive algorithm. * @param point */ public void add(PointKD point) { root.add(point); } /** * Returns the nearest neighbor to the given point. * @param point - PointKD to find the nearest to. * @return - The nearest PointKD to */ public PointKD getNearestNeighbor(PointKD point) { return root.nearest(point); } /** * Returns the nearest num neighbors to the given point. * @param point - PointKD to find the nearest to. * @param num - The number of points to find. * @return - The nearest PointKD's to point. */ public PointKD[] getNearestNeighbors(final PointKD point, int num) { if(num == 1) { return new PointKD[] { getNearestNeighbor(point) }; } PriorityDeque<PointKD> queue = new PriorityDeque<PointKD>( new Comparator<PointKD>(){ @Override public int compare(PointKD o1, PointKD o2) { double dist0 = point.distance(o1); double dist1 = point.distance(o2); return (dist0-dist1 < 0) ? -1 : 1; } },num); root.nearestn(queue,point); PointKD[] array = new PointKD[num]; Object[] obj = queue.toArray(); for(int i=0; i<num; ++i) { array[i] = (PointKD)obj[i]; } return array; } /** * Returns all PointKD within a certain RectangleKD. * @param rect - area to get PointKD from * @return - All PointKD within rect. */ public PointKD[] getRange(RectangleKD rect) { return root.range(rect); } /** * Returns all PointKD within a certain range defined by an upper and lower PointKD. * @param low - lower bounds of area * @param high - upper bounds of area * @return - All PointKD between low and high. */ public PointKD[] getRange(PointKD low, PointKD high) { return this.getRange(new RectangleKD(low, high)); } protected abstract class NodeKD { protected KDTreeB owner; protected BranchKD parent; protected RectangleKD rect; protected int depth = 0; protected abstract void add(PointKD k); protected abstract PointKD nearest(PointKD k); protected abstract void nearestn(PriorityDeque<PointKD> queue, PointKD k); protected abstract PointKD[] range(RectangleKD r); } protected class BucketKD extends NodeKD { protected PointKD bucket[]; protected int current; public BucketKD(KDTreeB owner) { this.owner = owner; bucket = new PointKD[owner.bucket_size]; rect = new RectangleKD(); parent = null; } public BucketKD(BranchKD branch) { this(branch.owner); parent = branch; depth = branch.depth + 1; } @Override protected void add(PointKD point) { if(current >= bucket.length) { //Split the bucket into a branch BranchKD branch = new BranchKD(this); if(parent == null) { owner.root = branch; } else { if(parent.isLeft(this)) { parent.left = branch; } else { parent.right = branch; } } branch.add(point); bucket = null; current = 0; return; } bucket[current++] = point; rect.expand(point); } @Override protected PointKD nearest(PointKD k) { double nearestDist = Double.POSITIVE_INFINITY; int nearest = 0; for (int i = 0; i < current; i++) { double distance = k.distanceSq(bucket[i]); if (distance < nearestDist) { nearestDist = distance; nearest = i; } } return bucket[nearest]; } @Override protected void nearestn(PriorityDeque<PointKD> queue, PointKD k) { for(int i = 0; i < current; i++) { queue.offer(bucket[i]); } } @Override protected PointKD[] range(RectangleKD r) { PointKD[] tmp = new PointKD[current]; int n = 0; for (int i = 0; i < current; i++) { if (r.contains(bucket[i])) { tmp[n++] = bucket[i]; } } PointKD[] tmp2 = new PointKD[n]; System.arraycopy(tmp, 0, tmp2, 0, n); return tmp2; } } protected class BranchKD extends NodeKD { protected NodeKD left, right; protected double slice; protected int dim; public BranchKD(BucketKD k) { owner = k.owner; parent = k.parent; slice = 0; rect = k.rect; depth = k.depth; left = new BucketKD(this); right = new BucketKD(this); dim = depth % owner.dimensions; double total = 0; for (int i = 0; i < k.current; i++) { total += k.bucket[i].internal[dim]; } slice = total / k.current; for (int i = 0; i < k.current; i++) add(k.bucket[i]); } @Override protected void add(PointKD k) { if(k.internal[dim] > slice) { right.add(k); } else { left.add(k); } } @Override protected PointKD nearest(PointKD k) { PointKD near = null; if (k.internal[dim] > slice) { near = right.nearest(k); double t = near.distanceSq(k); if (k.distanceSq(left.rect.getNearest(k)) < t) { PointKD tmp = left.nearest(k); if (tmp.distanceSq(k) < t) { near = tmp; } } } else { near = left.nearest(k); double t = near.distanceSq(k); if (k.distanceSq(right.rect.getNearest(k)) < t) { PointKD tmp = right.nearest(k); if (tmp.distanceSq(k) < t) { near = tmp; } } } return near; } @Override protected void nearestn(PriorityDeque<PointKD> queue, PointKD k) { //TODO if(k.internal[dim] > slice) { right.nearestn(queue, k); double t = queue.peekBottom().distanceSq(k); if(k.distanceSq(left.rect.getNearest(k)) < t) { left.nearestn(queue, k); } } else { left.nearestn(queue, k); double t = queue.peekBottom().distanceSq(k); if(k.distanceSq(right.rect.getNearest(k)) < t) { right.nearestn(queue, k); } } } @Override protected PointKD[] range(RectangleKD r) { PointKD[] tmp = new PointKD[0]; if (r.intersects(left.rect)) { PointKD[] tmpl = left.range(r); if(0 == tmp.length) tmp = tmpl; } if (r.intersects(right.rect)) { PointKD[] tmpr = right.range(r); if (0 == tmp.length) tmp = tmpr; else if (0 < tmpr.length) { PointKD[] tmp2 = new PointKD[tmp.length + tmpr.length]; System.arraycopy(tmp, 0, tmp2, 0, tmp.length); System.arraycopy(tmpr, 0, tmp2, tmp.length, tmpr.length); tmp = tmp2; } } return tmp; } protected boolean isLeft(BucketKD kd) { if(left == kd) return true; return false; } } }
PointKD
package org.csdgn.util; import java.io.Serializable; /** * PointKD class is a class that wraps a double array, for use in K-Dimensional structures. * This wrapping is done to improve readability and modularity. Often used to define a * point in k-dimensional space. * * @author Chase */ public class PointKD implements Serializable { private static final long serialVersionUID = -841162798668123755L; protected double[] internal; /** * Constructor * @param dimensions - Number of dimensions for this PointKD. */ public PointKD(int dimensions) { internal = new double[dimensions]; } /** * Constructor * @param array - An array to use for the internal array of this PointKD. */ public PointKD(double[] array) { internal = array.clone(); } /** * Constructor * @param point - PointKD to clone. */ public PointKD(PointKD point) { this.internal = point.internal.clone(); } /** * Returns array. * @return The internal array of this pointKD, changes made to this will effect this PointKD. */ public double[] get() { return internal; } /** * Sets the location of this point. * @param point - PointKD to copy. */ public void set(PointKD point) { this.internal = point.internal.clone(); } /** * Sets the location of this point. * @param array - Array to copy. */ public void set(double[] array) { this.internal = array.clone(); } /** * Sets the value at dimension index * @param index - Index * @param value - Value to set */ public void set(int index, double value) { internal[index] = value; } /** * @return number of dimensions */ public int size() { return internal.length; } /** * Compares this to a selected point and returns the euclidean distance * between them. * * @param p * - The Point to get the distance to. * @return The distance between this and <b>p</b>. */ public double distance(PointKD p) { return distance(this.internal, p.internal); } /** * Compares this to a selected point and returns the squared euclidean * distance between them. * * @param p * - The Point to get the distance to. * @return The distance between this and <b>p</b>. */ public double distanceSq(PointKD p) { return distanceSq(this.internal, p.internal); } /** * Clones this point. */ public PointKD clone() { return new PointKD(internal); } /** * Prints the class name and the point coordinates. */ public String toString() { String output = getClass().getSimpleName() + "["; for (int i = 0; i < internal.length; ++i) { if (0 != i) output += ","; output += internal[i]; } return output + "]"; } /** * Compares arrays of double and returns the euclidean distance * between them. * * @param a - The first set of numbers * @param b - The second set of numbers * @return The distance squared between <b>a</b> and <b>b</b>. */ public static final double distance(double[] a, double[] b) { double total = 0; for (int i = 0; i < a.length; ++i) total += (b[i] - a[i]) * (b[i] - a[i]); return Math.sqrt(total); } /** * Compares arrays of double and returns the squared euclidean distance * between them. * * @param a - The first set of numbers * @param b - The second set of numbers * @return The distance squared between <b>a</b> and <b>b</b>. */ public static final double distanceSq(double[] a, double[] b) { double total = 0; for (int i = 0; i < a.length; ++i) total += (b[i] - a[i]) * (b[i] - a[i]); return total; } }
RectangleKD
package org.csdgn.util; import java.io.Serializable; public class RectangleKD implements Serializable { private static final long serialVersionUID = 524388821816648020L; protected PointKD upper, lower; /** * Creates an empty RectangleKD */ public RectangleKD() { upper = null; lower = null; } /** * Creates a RectangleKD from two points. * @param lower * @param upper */ public RectangleKD(PointKD lower, PointKD upper) { this.lower = lower.clone(); this.upper = upper.clone(); } /** * Expands this RectangleKD to include point. * @param point - Point used to expand this Rectangle */ public void expand(PointKD point) { if(upper == null) { upper = point.clone(); lower = point.clone(); return; } for(int i=0; i<point.size(); ++i) { if(upper.internal[i] < point.internal[i]) upper.internal[i] = point.internal[i]; if(lower.internal[i] > point.internal[i]) lower.internal[i] = point.internal[i]; } } /** * Checks if this RectangleKD contains the given point. * @param point - PointKD to check. * @return true - if this rectangle contains the given point. */ public boolean contains(PointKD point) { if(upper == null) return false; for(int i=0; i<point.size(); ++i) { if(point.internal[i] > upper.internal[i] || point.internal[i] < lower.internal[i]) return false; } return true; } /** * Checks if this table intersects the given table. * @param rectangle - The rectangle to check intersection with. * @return true - if this rectangle intersects the given rectangle. */ public boolean intersects(RectangleKD rectangle) { if(rectangle.upper == null || upper == null) return false; for(int i=0; i<upper.size(); ++i) { if(rectangle.upper.internal[i] < lower.internal[i] || rectangle.lower.internal[i] > upper.internal[i]) return false; } return true; } /** * Gets the nearest point in this RectangleKD to the given point * @param point - PointKD to get the nearest point to. * @return the nearest point in this RectangleKD to the given point. */ public PointKD getNearest(PointKD point) { if(upper == null) return null; if(contains(point)) return point; //This check may not be needed. PointKD nearest = new PointKD(point); for(int i = 0; i < upper.size(); ++i) { if(nearest.internal[i] > upper.internal[i]) nearest.internal[i] = upper.internal[i]; if(nearest.internal[i] < lower.internal[i]) nearest.internal[i] = lower.internal[i]; } return nearest; } }
PriorityDeque
package org.csdgn.util; import java.util.Comparator; /** * This is a home made PriorityDeque with maximum size limiter, and * comparator or natural ordering selection. * * @author Chase * @param <E> */ public class PriorityDeque<E> { private class Item { public Item down, up; public E obj; public Item(E item) { obj = item; down = up = null; } } private final Comparator<? super E> comparator; private Item bottom, top; private int maximum_size; private int size; /** * Generic Constructor */ public PriorityDeque() { this(null,-1); } /** * Constructor with a defined maximum number of entries * @param maximum */ public PriorityDeque(int maximum) { this(null,maximum); } /** * Constructor with a defined comparator and an unlimited number of items. * @param comp */ public PriorityDeque(Comparator<? super E> comp) { this(comp,-1); } /** * Constructor with a defined conparator and limited number of items. * @param comp * @param maximum */ public PriorityDeque(Comparator<? super E> comp, int maximum) { comparator = comp; maximum_size = maximum; bottom = top = null; size = 0; } /** * Adds an item to this deque. * @param value - item to add */ public void offer(E value) { //System.err.println("Offering: " + value.toString()); if(bottom == null) { //System.err.println("-Is first item."); bottom = top = new Item(value); return; } //do ordering etc if(comparator != null) { //System.err.println("-Comparator."); offerComparator(value); } else { //System.err.println("-Natural."); offerNatural(value); } } /** * Removes and returns an item from the bottom of the list (lowest value) * @return the lowest value (bottom) */ public E pollBottom() { if(bottom == null) return null; Item tmp = bottom; if(bottom == top) bottom = top = null; else { bottom = bottom.up; bottom.down = null; tmp.up = null; } --size; return tmp.obj; } /** * Returns but does not remove the item from the bottom of the list. * @return the lowest value (bottom) */ public E peekBottom() { if(bottom == null) return null; return bottom.obj; } /** * Removes and returns an item from the top of the list (highest value). * @return the highest value (top) */ public E pollTop() { if(top == null) return null; Item tmp = top; if(bottom == top) bottom = top = null; else { top = top.down; top.up = null; tmp.down = null; } --size; return tmp.obj; } /** * Returns but does not remove the item from the top of the list (highest value). * @return the highest value (top) */ public E peekTop() { if(top == null) return null; return top.obj; } /** * This is a generic toArray, returns a non-castable Object * array with all the items in this deque. * @return an Object array with everything in this deque. */ public Object[] toArray() { Object array[] = new Object[size+1]; Item current = bottom; int i = 0; while(current != null) { array[i++] = current.obj; current = current.up; } return array; } /** * Add/sorts a value using the comparator * @param value */ private void offerComparator(E value) { if(maximum_size > 0 && size >= maximum_size) { if(comparator.compare(value, top.obj) >= 0) { return; } } Item nItem = new Item(value); if(comparator.compare(value,bottom.obj) < 0) { //less than the bottom, put on the bottom nItem.up = bottom; bottom.down = nItem; bottom = nItem; ++size; if(maximum_size > 0 && size > maximum_size) { pollTop(); } return; } //start at the bottom Item current = bottom; while(current != null) { if(comparator.compare(value,current.obj) < 0) { nItem.up = current; nItem.down = current.down; current.down.up = nItem; current.down = nItem; ++size; if(maximum_size > 0 && size > maximum_size) { pollTop(); } return; } current = current.up; } //else put it on top nItem.down = top; top.up = nItem; top = nItem; ++size; if(maximum_size > 0 && size > maximum_size) { pollTop(); } } /** * Add/sorts a value using the natural order * @param value */ @SuppressWarnings("unchecked") private void offerNatural(E value) { Comparable<? super E> key = (Comparable<? super E>)value; if(maximum_size > 0 && size >= maximum_size) { if(key.compareTo(top.obj) >= 0) { return; } } Item nItem = new Item(value); if(key.compareTo(bottom.obj) < 0) { //less than the bottom, put on the bottom nItem.up = bottom; bottom.down = nItem; bottom = nItem; ++size; if(maximum_size > 0 && size > maximum_size) { pollTop(); } return; } //start at the bottom Item current = bottom; while(current != null) { if(key.compareTo(current.obj) < 0) { nItem.up = current; nItem.down = current.down; current.down.up = nItem; current.down = nItem; ++size; if(maximum_size > 0 && size > maximum_size) { pollTop(); } return; } current = current.up; } //else put it on top nItem.down = top; top.up = nItem; top = nItem; ++size; if(maximum_size > 0 && size > maximum_size) { pollTop(); } } }