2008-10-31 10 views
49

Estoy buscando una implementación de KDTree en Java.
He hecho una búsqueda en Google y los resultados parecen bastante caóticos. En realidad, hay muchos resultados, pero en su mayoría son pequeñas implementaciones únicas, y prefiero encontrar algo con un poco más de "valor de producción". Algo así como colecciones apache o la excelente biblioteca de colecciones C5 para .NET. Algo donde puedo ver el rastreador de errores públicos y verificar para ver cuándo ocurrió la última confirmación SVN. Además, en un mundo ideal, encontraría una buena API bien diseñada para estructuras de datos espaciales, y KDTree sería solo una clase en esa biblioteca.Implementación de KDTree en Java

Para este proyecto, solo trabajaré en 2 o 3 dimensiones, y estoy interesado principalmente en una buena implementación de vecinos más cercanos.

+10

Parece que es su turno de escribir algo y regalarlo. –

+2

su primer enlace está muerto y su segundo enlace lo lleva a http://code.openhub.net/ ... actualice o elimine estos enlaces. –

Respuesta

16

En el libro Algorithms in a Nutshell hay una implementación de árbol kd en java junto con algunas variaciones. Todo el código está en oreilly.com y el libro en sí también lo guiará a través del algoritmo para que pueda construir uno usted mismo.

+1

Específicamente: http: //examples.oreilly.com/9780596516246/Releases/ADK-1.0.zip En: ADK-1.0 \ ADK \ Deployment \ JavaCode \ src \ algs \ model \ kdtree –

+0

También disponible en Github, consulte el enlace: https://github.com/ heineman/algorithms-nutshell-2ed/tree/master/JavaCode/src/algs/model/kdtree –

9

He tenido éxito con la implementación del profesor Levy encontrado here. Me doy cuenta de que está buscando una implementación certificada para producción, por lo que probablemente no sea una buena opción.

Sin embargo, tenga en cuenta a cualquier transeúnte, lo he estado utilizando durante un tiempo en mi proyecto de fotomosaico sin problemas. Sin garantía pero mejor que nada :)

+0

He tenido mucho éxito con esto también. +1 (Nota: está LGPL'd.) – Tom

+1

el enlace está roto ... – banditKing

+0

fijo del enlace ... – Chadwick

11

para futuros solicitantes. La biblioteca Java-ml tiene una implementación de árbol kd que funciona bien. http://java-ml.sourceforge.net/

+0

Lo bueno de esta biblioteca (a diferencia de los algoritmos en una implementación de Nutshell) es que la API utiliza matrices dobles nativas para claves y rangos en lugar de objetos personalizados. –

+0

La implementación de KDTree en java-ml es exactamente la del profesor Levy, pero está desactualizada. –

+1

es una lástima que no se haya publicado en un magen repositiry –

1

Existe también JTS Topology Suite

La aplicación KdTree sólo proporciona búsqueda por rango (no-vecinos más cercanos)

Si vecino más cercano es lo que más mira STRtree

2

Estás en lo correcto, no hay que muchos sitios con aplicación java para kd! de todos modos, el árbol de kd es básicamente un árbol de búsqueda binario que normalmente se calcula un valor mediano cada vez para esa dimensión. Aquí está el sencillo KDNode y en términos del método del vecino más cercano o la implementación completa, eche un vistazo a este proyecto github. Fue el mejor que pude encontrar para ti. Espero que esto te ayude.

private class KDNode { 
    KDNode left; 
    KDNode right; 
    E val; 
    int depth; 
    private KDNode(E e, int depth){ 
    this.left = null; 
    this.right = null; 
    this.val = e; 
    this.depth = depth; 
} 
+1

el enlace está roto –

0
package kdtree; 

class KDNode{ 
    KDNode left; 
    KDNode right; 
    int []data; 

    public KDNode(){ 
     left=null; 
     right=null; 
    } 

    public KDNode(int []x){ 
     left=null; 
     right=null; 
     data = new int[2]; 
     for (int k = 0; k < 2; k++) 
      data[k]=x[k]; 
    } 
} 
class KDTreeImpl{ 
    KDNode root; 
    int cd=0; 
    int DIM=2; 

    public KDTreeImpl() { 
     root=null; 
    } 

    public boolean isEmpty(){ 
     return root == null; 
    } 

    public void insert(int []x){ 
     root = insert(x,root,cd); 
    } 
    private KDNode insert(int []x,KDNode t,int cd){ 
     if (t == null) 
      t = new KDNode(x); 
     else if (x[cd] < t.data[cd]) 
      t.left = insert(x, t.left, (cd+1)%DIM); 
     else 
      t.right = insert(x, t.right, (cd+1)%DIM); 
     return t; 
    } 

    public boolean search(int []data){ 
     return search(data,root,0); 
    } 

    private boolean search(int []x,KDNode t,int cd){ 
     boolean found=false; 
     if(t==null){ 
      return false; 
     } 
     else { 
      if(x[cd]==t.data[cd]){ 
       if(x[0]==t.data[0] && x[1]==t.data[1]) 
       return true; 
      }else if(x[cd]<t.data[cd]){ 
       found = search(x,t.left,(cd+1)%DIM); 
      }else if(x[cd]>t.data[cd]){ 
       found = search(x,t.right,(cd+1)%DIM); 
      } 
      return found; 
     } 
    } 

    public void inorder(){ 
     inorder(root); 
    } 
    private void inorder(KDNode r){ 
     if (r != null){ 
      inorder(r.left); 
      System.out.print("("+r.data[0]+","+r.data[1] +") "); 
      inorder(r.right); 
     } 
    } 
    public void preorder() { 
     preorder(root); 
    } 
    private void preorder(KDNode r){ 
     if (r != null){ 
      System.out.print("("+r.data[0]+","+r.data[1] +") "); 
      preorder(r.left);    
      preorder(r.right); 
     } 
    } 
    /* Function for postorder traversal */ 
    public void postorder() { 
     postorder(root); 
    } 
    private void postorder(KDNode r) { 
     if (r != null){ 
      postorder(r.left);    
      postorder(r.right); 
      System.out.print("("+r.data[0]+","+r.data[1] +") "); 
     } 
    } 
} 
public class KDTree { 

    /** 
    * @param args the command line arguments 
    */ 
    public static void main(String[] args) { 
     // TODO code application logic here 
     KDTreeImpl kdt = new KDTreeImpl(); 
     int x[] = new int[2]; 
     x[0] = 30; 
     x[1] = 40; 
     kdt.insert(x); 

     x[0] = 5; 
     x[1] = 25; 
     kdt.insert(x); 

     x[0] = 10; 
     x[1] = 12; 
     kdt.insert(x); 

     x[0] = 70; 
     x[1] = 70; 
     kdt.insert(x); 

     x[0] = 50; 
     x[1] = 30; 
     kdt.insert(x); 
     System.out.println("Input Elements"); 
     System.out.println("(30,40) (5,25) (10,12) (70,70) (50,30)\n\n"); 
     System.out.println("Printing KD Tree in Inorder"); 
     kdt.inorder(); 
     System.out.println("\nPrinting KD Tree in PreOder"); 
     kdt.preorder(); 
     System.out.println("\nPrinting KD Tree in PostOrder"); 
     kdt.postorder(); 
     System.out.println("\nsearching..............."); 
     x[0]=40;x[1]=40; 
     System.out.println(kdt.search(x)); 
    } 
} 
1

Esta es una implementación completa para KD-Tree, he utilizado algunas bibliotecas para almacenar el punto y rectángulo. Estas bibliotecas están disponibles gratuitamente. Es posible hacer con estas clases haciendo tus propias clases para almacenar puntos y rectángulos. Por favor comparte tus comentarios.

import java.util.ArrayList; 
import java.util.List; 
import edu.princeton.cs.algs4.In; 
import edu.princeton.cs.algs4.Point2D; 
import edu.princeton.cs.algs4.RectHV; 
import edu.princeton.cs.algs4.StdDraw; 
public class KdTree { 
    private static class Node { 
     public Point2D point; // the point 
     public RectHV rect; // the axis-aligned rectangle corresponding to this 
     public Node lb; // the left/bottom subtree 
     public Node rt; // the right/top subtree 
     public int size; 
     public double x = 0; 
     public double y = 0; 
     public Node(Point2D p, RectHV rect, Node lb, Node rt) { 
      super(); 
      this.point = p; 
      this.rect = rect; 
      this.lb = lb; 
      this.rt = rt; 
      x = p.x(); 
      y = p.y(); 
     } 

    } 
    private Node root = null;; 

    public KdTree() { 
    } 

    public boolean isEmpty() { 
     return root == null; 
    } 

    public int size() { 
     return rechnenSize(root); 
    } 

    private int rechnenSize(Node node) { 
     if (node == null) { 
      return 0; 
     } else { 
      return node.size; 
     } 
    } 

    public void insert(Point2D p) { 
     if (p == null) { 
      throw new NullPointerException(); 
     } 
     if (isEmpty()) { 
      root = insertInternal(p, root, 0); 
      root.rect = new RectHV(0, 0, 1, 1); 
     } else { 
      root = insertInternal(p, root, 1); 
     } 
    } 

    // at odd level we will compare x coordinate, and at even level we will 
    // compare y coordinate 
    private Node insertInternal(Point2D pointToInsert, Node node, int level) { 
     if (node == null) { 
      Node newNode = new Node(pointToInsert, null, null, null); 
      newNode.size = 1; 
      return newNode; 
     } 
     if (level % 2 == 0) {//Horizontal partition line 
      if (pointToInsert.y() < node.y) {//Traverse in bottom area of partition 
       node.lb = insertInternal(pointToInsert, node.lb, level + 1); 
       if(node.lb.rect == null){ 
        node.lb.rect = new RectHV(node.rect.xmin(), node.rect.ymin(), 
          node.rect.xmax(), node.y); 
       } 
      } else {//Traverse in top area of partition 
       if (!node.point.equals(pointToInsert)) { 
        node.rt = insertInternal(pointToInsert, node.rt, level + 1); 
        if(node.rt.rect == null){ 
         node.rt.rect = new RectHV(node.rect.xmin(), node.y, 
           node.rect.xmax(), node.rect.ymax()); 
        } 
       } 
      } 

     } else if (level % 2 != 0) {//Vertical partition line 
      if (pointToInsert.x() < node.x) {//Traverse in left area of partition 
       node.lb = insertInternal(pointToInsert, node.lb, level + 1); 
       if(node.lb.rect == null){ 
        node.lb.rect = new RectHV(node.rect.xmin(), node.rect.ymin(), 
          node.x, node.rect.ymax()); 
       } 
      } else {//Traverse in right area of partition 
       if (!node.point.equals(pointToInsert)) { 
        node.rt = insertInternal(pointToInsert, node.rt, level + 1); 
        if(node.rt.rect == null){ 
         node.rt.rect = new RectHV(node.x, node.rect.ymin(), 
           node.rect.xmax(), node.rect.ymax()); 
        } 
       } 
      } 
     } 
     node.size = 1 + rechnenSize(node.lb) + rechnenSize(node.rt); 
     return node; 
    } 

    public boolean contains(Point2D p) { 
     return containsInternal(p, root, 1); 
    } 

    private boolean containsInternal(Point2D pointToSearch, Node node, int level) { 
     if (node == null) { 
      return false; 
     } 
     if (level % 2 == 0) {//Horizontal partition line 
      if (pointToSearch.y() < node.y) { 
       return containsInternal(pointToSearch, node.lb, level + 1); 
      } else { 
       if (node.point.equals(pointToSearch)) { 
        return true; 
       } 
       return containsInternal(pointToSearch, node.rt, level + 1); 
      } 
     } else {//Vertical partition line 
      if (pointToSearch.x() < node.x) { 
       return containsInternal(pointToSearch, node.lb, level + 1); 
      } else { 
       if (node.point.equals(pointToSearch)) { 
        return true; 
       } 
       return containsInternal(pointToSearch, node.rt, level + 1); 
      } 
     } 

    } 

    public void draw() { 
     StdDraw.clear(); 
     drawInternal(root, 1); 
    } 

    private void drawInternal(Node node, int level) { 
     if (node == null) { 
      return; 
     } 
     StdDraw.setPenColor(StdDraw.BLACK); 
     StdDraw.setPenRadius(0.02); 
     node.point.draw(); 
     double sx = node.rect.xmin(); 
     double ex = node.rect.xmax(); 
     double sy = node.rect.ymin(); 
     double ey = node.rect.ymax(); 
     StdDraw.setPenRadius(0.01); 
     if (level % 2 == 0) { 
      StdDraw.setPenColor(StdDraw.BLUE); 
      sy = ey = node.y; 
     } else { 
      StdDraw.setPenColor(StdDraw.RED); 
      sx = ex = node.x; 
     } 
     StdDraw.line(sx, sy, ex, ey); 
     drawInternal(node.lb, level + 1); 
     drawInternal(node.rt, level + 1); 
    } 

    /** 
    * Find the points which lies in the rectangle as parameter 
    * @param rect 
    * @return 
    */ 
    public Iterable<Point2D> range(RectHV rect) { 
     List<Point2D> resultList = new ArrayList<Point2D>(); 
     rangeInternal(root, rect, resultList); 
     return resultList; 
    } 

    private void rangeInternal(Node node, RectHV rect, List<Point2D> resultList) { 
     if (node == null) { 
      return; 
     } 
     if (node.rect.intersects(rect)) { 
      if (rect.contains(node.point)) { 
       resultList.add(node.point); 
      } 
      rangeInternal(node.lb, rect, resultList); 
      rangeInternal(node.rt, rect, resultList); 
     } 

    } 

    public Point2D nearest(Point2D p) { 
     if(root == null){ 
      return null; 
     } 
     Champion champion = new Champion(root.point,Double.MAX_VALUE); 
     return nearestInternal(p, root, champion, 1).champion; 
    } 

    private Champion nearestInternal(Point2D targetPoint, Node node, 
      Champion champion, int level) { 
     if (node == null) { 
      return champion; 
     } 
     double dist = targetPoint.distanceSquaredTo(node.point); 
     int newLevel = level + 1; 
     if (dist < champion.championDist) { 
      champion.champion = node.point; 
      champion.championDist = dist; 
     } 
     boolean goLeftOrBottom = false; 
     //We will decide which part to be visited first, based upon in which part point lies. 
     //If point is towards left or bottom part, we traverse in that area first, and later on decide 
     //if we need to search in other part too. 
     if(level % 2 == 0){ 
      if(targetPoint.y() < node.y){ 
       goLeftOrBottom = true; 
      } 
     } else { 
      if(targetPoint.x() < node.x){ 
       goLeftOrBottom = true; 
      } 
     } 
     if(goLeftOrBottom){ 
      nearestInternal(targetPoint, node.lb, champion, newLevel); 
      Point2D orientationPoint = createOrientationPoint(node.x,node.y,targetPoint,level); 
      double orientationDist = orientationPoint.distanceSquaredTo(targetPoint); 
      //We will search on the other part only, if the point is very near to partitioned line 
      //and champion point found so far is far away from the partitioned line. 
      if(orientationDist < champion.championDist){ 
       nearestInternal(targetPoint, node.rt, champion, newLevel); 
      } 
     } else { 
      nearestInternal(targetPoint, node.rt, champion, newLevel); 
      Point2D orientationPoint = createOrientationPoint(node.x,node.y,targetPoint,level); 
      //We will search on the other part only, if the point is very near to partitioned line 
      //and champion point found so far is far away from the partitioned line. 
      double orientationDist = orientationPoint.distanceSquaredTo(targetPoint); 
      if(orientationDist < champion.championDist){ 
       nearestInternal(targetPoint, node.lb, champion, newLevel); 
      } 

     } 
     return champion; 
    } 
    /** 
    * Returns the point from a partitioned line, which can be directly used to calculate 
    * distance between partitioned line and the target point for which neighbours are to be searched. 
    * @param linePointX 
    * @param linePointY 
    * @param targetPoint 
    * @param level 
    * @return 
    */ 
    private Point2D createOrientationPoint(double linePointX, double linePointY, Point2D targetPoint, int level){ 
     if(level % 2 == 0){ 
      return new Point2D(targetPoint.x(),linePointY); 
     } else { 
      return new Point2D(linePointX,targetPoint.y()); 
     } 
    } 

    private static class Champion{ 
     public Point2D champion; 
     public double championDist; 
     public Champion(Point2D c, double d){ 
      champion = c; 
      championDist = d; 
     } 
    } 

    public static void main(String[] args) { 
     String filename = "/home/raman/Downloads/kdtree/circle100.txt"; 
     In in = new In(filename); 
     KdTree kdTree = new KdTree(); 
     while (!in.isEmpty()) { 
      double x = in.readDouble(); 
      double y = in.readDouble(); 
      Point2D p = new Point2D(x, y); 
      kdTree.insert(p); 
     } 
     // kdTree.print(); 
     System.out.println(kdTree.size()); 
     kdTree.draw(); 
     System.out.println(kdTree.nearest(new Point2D(0.4, 0.5))); 
     System.out.println(new Point2D(0.7, 0.4).distanceSquaredTo(new Point2D(0.9,0.5))); 
     System.out.println(new Point2D(0.7, 0.4).distanceSquaredTo(new Point2D(0.9,0.4))); 

    } 
}