ближайший сосед-дерево k - d-Википедия доказательство

на запись Википедии для K-D деревьев, представлен алгоритм для выполнения поиска ближайшего соседа по дереву k-D. Чего я не понимаю, так это объяснения шага 3.2. Откуда вы знаете, что нет более близкой точки только потому, что разница между координатами расщепления точки поиска и текущего узла больше, чем разница между координатами расщепления точки поиска и текущим лучшим?

EDIT: добавлен текст Статья Википедии, о которой идет речь, в случае ее последующего изменения в Википедии.

анимация поиска ближайшего соседа NN поиск с деревом KD в 2D

алгоритм ближайшего соседа (NN) цель-найти точку в дереве который ближе всего к заданному входу точка. Этот поиск можно сделать эффективно с помощью дерева свойства для быстрого устранения больших части пространства поиска. Поиск ближайшего соседа в KD-дерево поступлений следующим образом:

  1. начиная с корневого узла, алгоритм движется вниз по дереву рекурсивно, так же, как это если бы точка поиска была вставлено (т. е. идет вправо или влево в зависимости от точки больше или меньше текущего узла в разделенном измерении).
  2. как только алгоритм достигает листового узла, он сохраняет эту точку узла как "текущий лучший"
  3. алгоритм разматывает рекурсию дерево, выполняющее следующие шаги на каждом узле: 1. Если текущий узел ближе, чем текущий best, то это становится нынешним лучшим. 2. Алгоритм проверяет, могут ли быть какие-либо точки На другая сторона плоскости расщепления которые находятся ближе к точке поиска чем нынешние лучше. В понятие, это делается путем пересечения разделение гиперплоскостью с гиперсфера вокруг точки поиска который имеет радиус, равный текущему ближайшее расстояние. Поскольку гиперплоскости выровнены по оси реализовано как простое сравнение чтобы увидеть, есть ли разница между разделяющая координата поиска точка и текущий узел меньше, чем расстояние (общие координаты) от точки поиска к текущей лучший. 1. Если гиперсфера пересечет плоскость, то ближе точки На другой стороне плоскость, поэтому алгоритм должен двигаться вниз другая ветвь дерева от текущий узел ищу ближе точки, следующие за той же рекурсивной процесс как весь поиск. 2. Если гиперсфера не пересекает плоскость расщепления, затем алгоритм продолжает ходить вверх по дереву, и вся ветка на другая сторона этого узла устраненный.
  4. когда алгоритм завершает этот процесс для корневого узла, затем поиск завершен.

обычно алгоритм использует квадрат расстояния для сравнения с избегать вычисление квадратных корней. Вдобавок, он может сохранить вычисление путем держать квадрат тока самое лучшее расстояние в переменная для сравнения.

2 ответов


внимательно посмотрите на 6-й кадр анимации на этой странице.

поскольку алгоритм возвращается к рекурсии, возможно, что на другой стороне гиперплоскости есть более близкая точка. Мы проверили одну половину, но на другой может быть еще более близкая точка.

ну, оказывается, иногда мы можем сделать упрощение. Если это невозможно для того, чтобы быть точка на другой половине ближе, чем наша текущая лучшая (ближайшая) точка, тогда мы можем полностью пропустить эту гиперплоскость. Это упрощение показано на 6-м кадре.

выяснение того, возможно ли это упрощение, выполняется путем сравнения расстояния от гиперплоскости до нашего местоположения поиска. Поскольку гиперплоскость выровнена по осям, Кратчайшая линия от нее до любой другой точки будет линией вдоль одного измерения, поэтому мы можем сравнить только координату измерения, которое гиперплоскость расщепляет.

Если он дальше от точки поиска до гиперплоскости, чем от точки поиска до вашей текущей ближайшей точки, то нет причин искать дальше этой координаты расщепления.

даже если мое объяснение не поможет, графика будет. Удачи в вашем проекте!


да, описание поиска NN (ближайшего соседа) в дереве KD в Википедии немного сложно следовать. Это не помогает, что a много из лучших результатов поиска Google на NN KD дерево поиска просто неправильно!

вот некоторые 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);
        }
    }
}

API для поиска NN на дереве KD:

// 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);
}

Edit: некоторые люди просят о помощи с структуры данных тоже (а не только алгоритм 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;
};