ближайший сосед - k-d tree - wikipedia proof

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

Поиск ближайшего соседа Анимация поиска NN с помощью KD Tree в 2D

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

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

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


person oob    schedule 26.10.2009    source источник
comment
Проверьте этот stackoverflow.com/a/57490663/1029599. Он предоставляет алгоритм - в очень ясной форме и с приятным объяснением.   -  person KGhatak    schedule 04.04.2021


Ответы (2)


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

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

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

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

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

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

person Andrew    schedule 10.01.2010
comment
Это недостающее звено, помогающее понять алгоритм. Похоже, что ни одно из других объяснений не требует времени, чтобы объяснить шаг упрощения (или они просто упоминают его как кстати). - person David S.; 01.10.2013

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

Вот код на 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);
}

Изменить: некоторые люди также просят помощи со структурами данных (а не только с алгоритмом 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;
};
person Scott Smedley    schedule 09.05.2016
comment
Мой C ++ довольно грубоват, но я думаю, вам здесь не хватает важного кода. Нет определения KDNode или KDPoint. - person Robert Liberatore; 15.10.2016
comment
distance(point, node->leaf->point); Я думаю, это также заполнит точку массива всеми точками в этой подобласти? Не могли бы вы подробнее рассказать об этом? - person Axl; 25.10.2016
comment
@Project: вопрос касался NN алгоритма, но я добавил информацию о структурах данных, чтобы сделать его слишком исчерпывающим ответом. :) - person Scott Smedley; 26.10.2016
comment
@Axl: distance () - это просто разделение между двумя точками. Я отредактировал ответ, включив в него мою реализацию по умолчанию. Надеюсь, теперь эта простая, но важная концепция стала яснее? - person Scott Smedley; 26.10.2016
comment
@ScottSmedley Спасибо, я считаю дополнительный код полезным, так как я не очень хорошо знаком с древовидной структурой. - person Robert Liberatore; 26.10.2016