最近邻居 - k-d树 - 维基百科证明

时间:2009-10-26 20:58:37

标签: nearest-neighbor kdtree

wikipedia entry for k-d trees上,提出了一种算法,用于在k-d树上进行最近邻搜索。我不明白的是步骤3.2的解释。你怎么知道没有一个更接近的点只是因为搜索点的分裂坐标和当前节点之间的差异大于搜索点的分裂坐标与当前最佳点之间的差异?

  

最近邻搜索动画   NN在2D中使用KD树进行搜索

     

最近邻居(NN)算法   旨在找到树中的要点   最接近给定输入的   点。可以完成此搜索   通过使用树有效地   属性快速消除大   搜索空间的一部分。   在a中搜索最近的邻居   kd-tree继续如下:

     
      
  1. 从根节点开始,算法向下移动树   以与它相同的方式递归地递归   如果搜索点正在存在的话   插入(即向右或向左)   取决于重点是什么   大于或小于当前节点   在分裂维度中。)。
  2.   
  3. 一旦算法到达叶节点,它就将该节点指向保存为   “当前最好的”
  4.   
  5. 算法展开树的递归,执行   在每个节点执行以下步骤:        1.如果当前节点比当前节点更接近,那么它   成为当前最好的。        2.算法检查是否有任何点   分裂面的另一面   更接近搜索点   比目前最好的。在概念上,   这是通过交叉来完成的   用一个分裂超平面   搜索点周围的超球面   半径等于当前的半径   最近的距离。自从   超平面都是轴对齐的   实现为简单的比较   看是否有区别   搜索的分裂坐标   点和当前节点小于   距离(总坐标)   从搜索点到当前   最好。              如果超球体穿过飞机,可能会有   更靠近另一边的点   平面,所以算法必须向下移动   树的另一个分支来自   当前节点寻找更近   点,遵循相同的递归   进程作为整个搜索。              2.如果超球面不与分裂面相交,   然后算法继续行走   树上,整个树枝上   该节点的另一面是   消除。
  6.   
  7. 当算法完成根节点的这个过程时,那么   搜索完成。
  8.         

    通常算法使用平方   要避免比较的距离   计算平方根。另外,   它可以通过保存来节省计算量   平方当前最佳距离   变量用于比较。

2 个答案:

答案 0 :(得分:13)

仔细查看animation on that page的第6帧。

随着算法回溯递归,有可能超平面的另一侧有一个更近的点,它正在进行。我们检查了一半,但另一半可能会有更接近的点。

嗯,事实证明我们有时可以进行简化。如果不可能,那么另一半的点比我们当前的最佳(最近)点更接近,那么我们可以完全跳过该超平面。这种简化是在第6帧显示的。

通过比较超平面到我们的搜索位置的距离来确定是否可以进行这种简化。由于超平面与轴对齐,因此从它到任何其他点的最短线将沿着一个维度划线,因此我们只能比较超平面分割的维度的坐标。

如果从搜索点到超平面的距离比从搜索点到当前最近点的距离更远,则没有理由搜索该分裂坐标。

即使我的解释没有帮助,图形也会。祝你的项目好运!

答案 1 :(得分:1)

是的,维基百科上的KD树中的NN(最近邻)搜索的描述有点难以理解。对NN KD Tree搜索中的顶级Google搜索结果的很多是完全错误的,这没有任何帮助!

这里有一些C ++代码向您展示如何正确使用它:

template <class T, std::size_t N>
void KDTree<T,N>::nearest (
    const const KDNode<T,N> &node,
    const std::array<T, N> &point, // looking for closest node to this point
    const KDPoint<T,N> &closest,   // closest node (so far)
    double &minDist,
    const uint depth) const
{
    if (node->isLeaf()) {
        const double dist = distance(point, node->leaf->point);
        if (dist < minDist) {
            minDist = dist;
            closest = node->leaf;
        }
    } else {
        const T dim = depth % N;
        if (point[dim] < node->splitVal) {
            // search left first
            nearest(node->left, point, closest, minDist, depth + 1);
            if (point[dim] + minDist >= node->splitVal)
                nearest(node->right, point, closest, minDist, depth + 1);
        } else {
            // search right first
            nearest(node->right, point, closest, minDist, depth + 1);
            if (point[dim] - minDist <= node->splitVal)
                nearest(node->left, point, closest, minDist, depth + 1);
        }
    }
}

在KD树上搜索NN的API:

// Nearest neighbour
template <class T, std::size_t N>
const KDPoint<T,N> KDTree<T,N>::nearest (const std::array<T, N> &point) const {
    const KDPoint<T,N> closest;
    double minDist = std::numeric_limits<double>::max();
    nearest(root, point, closest, minDist);
    return closest;
}

默认距离函数:

template <class T, std::size_t N>
double distance (const std::array<T, N> &p1, const std::array<T, N> &p2) {
    double d = 0.0;
    for (uint i = 0; i < N; ++i) {
        d += pow(p1[i] - p2[i], 2.0);
    }
    return sqrt(d);
}

编辑:有些人也在寻求数据结构的帮助(不仅仅是NN算法),所以这就是我所使用的。根据您的目的,您可能希望稍微修改数据结构。 (注意:但你几乎肯定会想要修改NN算法。)

KDPoint类:

template <class T, std::size_t N>
class KDPoint {
    public:
        KDPoint<T,N> (std::array<T,N> &&t) : point(std::move(t)) { };
        virtual ~KDPoint<T,N> () = default;
        std::array<T, N> point;
};

KDNode类:

template <class T, std::size_t N>
class KDNode
{
    public:
        KDNode () = delete;
        KDNode (const KDNode &) = delete;
        KDNode & operator = (const KDNode &) = delete;
        ~KDNode () = default;

        // branch node
        KDNode (const T                       split,
                std::unique_ptr<const KDNode> &lhs,
                std::unique_ptr<const KDNode> &rhs) : splitVal(split), left(std::move(lhs)), right(std::move(rhs)) { };
        // leaf node
        KDNode (std::shared_ptr<const KDPoint<T,N>> p) : splitVal(0), leaf(p) { };

        bool isLeaf (void) const { return static_cast<bool>(leaf); }

        // data members
        const T                                   splitVal;
        const std::unique_ptr<const KDNode<T,N>>  left, right;
        const std::shared_ptr<const KDPoint<T,N>> leaf;
};

KDTree类:(注意:你需要添加一个成员函数来构建/填充你的树。)

template <class T, std::size_t N>
class KDTree {
    public:
        KDTree () = delete;
        KDTree (const KDTree &) = delete;
        KDTree (KDTree &&t) : root(std::move(const_cast<std::unique_ptr<const KDNode<T,N>>&>(t.root))) { };
        KDTree & operator = (const KDTree &) = delete;
        ~KDTree () = default;

        const KDPoint<T,N> nearest (const std::array<T, N> &point) const;

        // Nearest neighbour search - runs in O(log n)
        void nearest (const std::unique_ptr<const KDNode<T,N>> &node,
                      const std::array<T, N> &point,
                      std::shared_ptr<const KDPoint<T,N>> &closest,
                      double &minDist,
                      const uint depth = 0) const;

        // data members
        const std::unique_ptr<const KDNode<T,N>> root;
};
相关问题