【问题标题】:KD TREES (3-D) Nearest Neighbour SearchKD TREES (3-D) 最近邻搜索
【发布时间】:2012-06-11 17:33:29
【问题描述】:

我正在查看 KD 树最近邻搜索的 Wikipedia 页面。

当点在 2-D(x,y) 中时,维基百科中给出的伪代码有效。

我想知道,当点是 3-D(x,y,z) 时,我应该进行哪些更改。

我在谷歌上搜索了很多,甚至在堆栈溢出中浏览了类似的问题链接,但我在任何地方都没有找到 3-d 实现,之前的所有问题都以 2-D 点作为输入,而不是 3-D 点我正在寻找的。

Wiki中构建KD树的伪代码是::

function kdtree (list of points pointList, int depth)
{
    // Select axis based on depth so that axis cycles through all valid values
    var int axis := depth mod k;

    // Sort point list and choose median as pivot element
    select median by axis from pointList;

    // Create node and construct subtrees
    var tree_node node;
    node.location := median;
    node.leftChild := kdtree(points in pointList before median, depth+1);
    node.rightChild := kdtree(points in pointList after median, depth+1);
    return node;
}

构建 KD 树后如何找到最近的邻居?

谢谢!

【问题讨论】:

    标签: algorithm data-structures kdtree


    【解决方案1】:

    您可以完全按照 Wikipedia 页面上“最近邻搜索”标题下的描述找到最近邻。那里的描述适用于任何数量的维度。那就是:

    • 从根递归地向下遍历树,就好像您要插入要查找最近邻的点一样。
    • 当您到达一片叶子时,请注意它是迄今为止最好的。
    • 在再次上树的过程中,对于遇到的每个节点:
      • 如果它比目前最好的更接近,请更新目前最好的。
      • 如果 best-so-far 到目标点的距离大于目标点到该节点的分裂超平面的距离,
      • 也处理节点的另一个子节点(使用相同的递归)。

    【讨论】:

      【解决方案2】:

      我最近编写了一个 KDTree,用于在 3-D 空间中进行最近邻搜索,但在理解 NNS 时遇到了同样的问题,尤其是 wiki 的 3.2。我最终使用了这个算法,它似乎在我的所有测试中都有效:

      这是最初的叶子搜索:

      public Collection<T> nearestNeighbourSearch(int K, T value) {
          if (value==null) return null;
      
          //Map used for results
          TreeSet<KdNode> results = new TreeSet<KdNode>(new EuclideanComparator(value));
      
          //Find the closest leaf node
          KdNode prev = null;
          KdNode node = root;
          while (node!=null) {
              if (KdNode.compareTo(node.depth, node.k, node.id, value)<0) {
                  //Greater
                  prev = node;
                  node = node.greater;
              } else {
                  //Lesser
                  prev = node;
                  node = node.lesser;
              }
          }
          KdNode leaf = prev;
      
          if (leaf!=null) {
              //Used to not re-examine nodes
              Set<KdNode> examined = new HashSet<KdNode>();
      
              //Go up the tree, looking for better solutions
              node = leaf;
              while (node!=null) {
                  //Search node
                  searchNode(value,node,K,results,examined);
                  node = node.parent;
              }
          }
      
          //Load up the collection of the results
          Collection<T> collection = new ArrayList<T>(K);
          for (KdNode kdNode : results) {
              collection.add((T)kdNode.id);
          }
          return collection;
      }
      

      这是从最近的叶节点开始的递归搜索:

      private static final <T extends KdTree.XYZPoint> void searchNode(T value, KdNode node, int K, TreeSet<KdNode> results, Set<KdNode> examined) {
          examined.add(node);
      
          //Search node
          KdNode lastNode = null;
          Double lastDistance = Double.MAX_VALUE;
          if (results.size()>0) {
              lastNode = results.last();
              lastDistance = lastNode.id.euclideanDistance(value);
          }
          Double nodeDistance = node.id.euclideanDistance(value);
          if (nodeDistance.compareTo(lastDistance)<0) {
              if (results.size()==K && lastNode!=null) results.remove(lastNode);
              results.add(node);
          } else if (nodeDistance.equals(lastDistance)) {
              results.add(node);
          } else if (results.size()<K) {
              results.add(node);
          }
          lastNode = results.last();
          lastDistance = lastNode.id.euclideanDistance(value);
      
          int axis = node.depth % node.k;
          KdNode lesser = node.lesser;
          KdNode greater = node.greater;
      
          //Search children branches, if axis aligned distance is less than current distance
          if (lesser!=null && !examined.contains(lesser)) {
              examined.add(lesser);
      
              double nodePoint = Double.MIN_VALUE;
              double valuePlusDistance = Double.MIN_VALUE;
              if (axis==X_AXIS) {
                  nodePoint = node.id.x;
                  valuePlusDistance = value.x-lastDistance;
              } else if (axis==Y_AXIS) {
                  nodePoint = node.id.y;
                  valuePlusDistance = value.y-lastDistance;
              } else {
                  nodePoint = node.id.z;
                  valuePlusDistance = value.z-lastDistance;
              }
              boolean lineIntersectsCube = ((valuePlusDistance<=nodePoint)?true:false);
      
              //Continue down lesser branch
              if (lineIntersectsCube) searchNode(value,lesser,K,results,examined);
          }
          if (greater!=null && !examined.contains(greater)) {
              examined.add(greater);
      
              double nodePoint = Double.MIN_VALUE;
              double valuePlusDistance = Double.MIN_VALUE;
              if (axis==X_AXIS) {
                  nodePoint = node.id.x;
                  valuePlusDistance = value.x+lastDistance;
              } else if (axis==Y_AXIS) {
                  nodePoint = node.id.y;
                  valuePlusDistance = value.y+lastDistance;
              } else {
                  nodePoint = node.id.z;
                  valuePlusDistance = value.z+lastDistance;
              }
              boolean lineIntersectsCube = ((valuePlusDistance>=nodePoint)?true:false);
      
              //Continue down greater branch
              if (lineIntersectsCube) searchNode(value,greater,K,results,examined);
          }
      }
      

      完整的java源码可以在here找到。

      【讨论】:

        【解决方案3】:

        我想知道,我应该做些什么改变,什么时候点 3-D(x,y,z)。

        你在这条线上得到当前轴

        var int axis := depth mod k;
        

        现在根据轴,您可以通过比较相应的属性来找到中位数。例如。如果 axis = 0 您与 x 属性进行比较。实现这一点的一种方法是在执行搜索的例程中传递一个比较器函数。

        【讨论】:

          猜你喜欢
          • 2015-03-17
          • 2011-08-07
          • 2011-05-24
          • 2012-11-10
          • 2012-06-27
          • 2018-06-16
          • 1970-01-01
          • 2018-03-12
          • 2011-10-23
          相关资源
          最近更新 更多