В информатике k - мерное дерево (сокращение от k-мерного дерева ) — это структура данных , разбивающая пространство на разделы, для организации точек в k -мерном пространстве . K-мерное — это то, что касается ровно k ортогональных осей или пространства с любым числом измерений . [1] k -мерные деревья — это полезная структура данных для нескольких приложений, таких как:
Деревья k -d являются частным случаем деревьев разбиения двоичного пространства .
Дерево k -d — это бинарное дерево , в котором каждый узел является k -мерной точкой. [2] Каждый неконечный узел можно рассматривать как неявно генерирующий разделяющую гиперплоскость , которая делит пространство на две части, известные как полупространства . Точки слева от этой гиперплоскости представлены левым поддеревом этого узла, а точки справа от гиперплоскости представлены правым поддеревом. Направление гиперплоскости выбирается следующим образом: каждый узел в дереве связан с одним из k измерений, причем гиперплоскость перпендикулярна оси этого измерения. Так, например, если для конкретного разбиения выбрана ось «x», все точки в поддереве с меньшим значением «x», чем у узла, появятся в левом поддереве, а все точки с большим значением «x» будут в правом поддереве. В таком случае гиперплоскость будет задана значением x точки, а ее нормаль будет единичной осью x. [3]
Поскольку существует множество возможных способов выбора плоскостей разделения, выровненных по осям, существует множество различных способов построения деревьев k -d. Канонический метод построения деревьев k -d имеет следующие ограничения: [4]
Этот метод приводит к сбалансированному дереву k -d, в котором каждый листовой узел находится примерно на одинаковом расстоянии от корня. Однако сбалансированные деревья не обязательно оптимальны для всех приложений.
Обратите внимание, что выбирать срединную точку не обязательно . В случае, когда срединные точки не выбраны, нет гарантии, что дерево будет сбалансированным. Чтобы избежать кодирования сложного алгоритма поиска медианы [5] [6] или использования сортировки, такой как пирамидальная сортировка или сортировка слиянием, для сортировки всех n точек, популярной практикой является сортировка фиксированного числа случайно выбранных точек и использование медианы этих точек в качестве плоскости разделения. На практике этот метод часто приводит к хорошо сбалансированным деревьям.
При наличии списка из n точек следующий алгоритм использует сортировку с поиском медианы для построения сбалансированного k -d дерева, содержащего эти точки.
функция kdtree ( список точек pointList, int depth){ // Выбираем ось на основе глубины, чтобы ось циклически перебирала все допустимые значения var int axis := depth mod k; // Сортируем список точек и выбираем медиану в качестве опорного элемента select median by axis from pointList; // Создать узел и построить поддерево узел.расположение := медиана; node.leftChild := kdtree(точки в pointList до медианы, глубина+1); node.rightChild := kdtree(точки в pointList после медианы, глубина+1); возврат узла;}
Обычно точки "после" медианы включают только те, которые строго больше медианы в текущем измерении. Для точек, которые лежат на медиане в текущем измерении, можно определить функцию, которая сравнивает их во всех измерениях. В некоторых случаях допустимо позволить точкам, равным медиане, лежать по одну сторону от медианы, например, разделив точки на подмножество "меньше чем" и подмножество "больше или равно".
Этот алгоритм создает инвариант , что для любого узла все узлы в левом поддереве находятся по одну сторону плоскости разделения , а все узлы в правом поддереве — по другую сторону. Точки, лежащие на плоскости разделения, могут появляться по обе стороны. Плоскость разделения узла проходит через точку, связанную с этим узлом (в коде именуемую node.location ).
Альтернативные алгоритмы построения сбалансированного k -d дерева предварительно сортируют данные перед построением дерева. Затем они поддерживают порядок предварительной сортировки во время построения дерева и, следовательно, устраняют затратный шаг поиска медианы на каждом уровне подразделения. Два таких алгоритма строят сбалансированное k -d дерево для сортировки треугольников, чтобы улучшить время выполнения трассировки лучей для трехмерной компьютерной графики . Эти алгоритмы предварительно сортируют n треугольников перед построением k -d дерева , а затем в лучшем случае строят дерево вовремя . [7] [8] Алгоритм, который строит сбалансированное k -d дерево для сортировки точек, имеет сложность в худшем случае . [9] [10] Этот алгоритм предварительно сортирует n точек в каждом из k измерений, используя сортировку, такую как пирамидальная сортировка или сортировка слиянием, перед построением дерева. Затем он поддерживает порядок этих k предварительных сортировок во время построения дерева и, таким образом, избегает поиска медианы на каждом уровне подразделения.
Добавляется новая точка в k -d дерево таким же образом, как добавляется элемент в любое другое дерево поиска . Сначала пройдите по дереву, начиная с корня и двигаясь либо к левому, либо к правому потомку в зависимости от того, находится ли вставляемая точка на "левой" или "правой" стороне плоскости разделения. Как только вы доберетесь до узла, под которым должен располагаться потомок, добавьте новую точку как левого или правого потомка листового узла, снова в зависимости от того, какая сторона плоскости разделения узла содержит новый узел.
Добавление точек таким образом может привести к тому, что дерево станет несбалансированным, что приведет к снижению производительности дерева. Скорость ухудшения производительности дерева зависит от пространственного распределения добавляемых точек дерева и количества добавляемых точек по отношению к размеру дерева. Если дерево становится слишком несбалансированным, может потребоваться его повторная балансировка для восстановления производительности запросов, которые полагаются на балансировку дерева, например, поиск ближайшего соседа.
Чтобы удалить точку из существующего k -d дерева, не нарушая инварианта, проще всего сформировать набор всех узлов и листьев из дочерних узлов целевого узла и заново создать эту часть дерева. [11]
Другой подход заключается в поиске замены для удаленной точки. [12] Сначала найдите узел , содержащий удаляемую точку. Для базового случая, где R — листовой узел, замена не требуется. Для общего случая найдите точку замены, скажем , из поддерева с корнем в . Замените точку, хранящуюся в , на . Затем рекурсивно удалите .
Для поиска точки замены, если дискриминирует на (скажем) и имеет правого потомка, найдите точку с минимальным значением из поддерева с корнем в правом потомке. В противном случае найдите точку с максимальным значением из поддерева с корнем в левом потомке.
Балансировка k -d дерева требует осторожности, поскольку k -d деревья сортируются по нескольким измерениям, поэтому метод поворота деревьев не может быть использован для их балансировки, поскольку это может нарушить инвариант.
Существует несколько вариантов сбалансированных k -d деревьев. Они включают в себя разделенное k -d дерево, псевдо k -d дерево, KDB-дерево , hB-дерево и Bkd-дерево . Многие из этих вариантов являются адаптивными k-d деревьями .
Алгоритм поиска ближайшего соседа (NN) направлен на поиск точки в дереве, которая находится ближе всего к заданной входной точке. Этот поиск может быть выполнен эффективно, если использовать свойства дерева для быстрого исключения больших участков пространства поиска.
Поиск ближайшего соседа в дереве k -d происходит следующим образом:
Обычно алгоритм использует квадраты расстояний для сравнения, чтобы избежать вычисления квадратных корней. Кроме того, он может сэкономить вычисления, удерживая квадрат текущего наилучшего расстояния в переменной для сравнения.
Алгоритм можно расширить несколькими способами с помощью простых модификаций. Он может предоставить k ближайших соседей к точке, поддерживая k текущих лучших вместо одного. Ветвь удаляется только тогда, когда найдено k точек, и ветка не может иметь точек ближе, чем любой из k текущих лучших.
Его также можно преобразовать в алгоритм приближения, чтобы он работал быстрее. Например, приблизительный поиск ближайшего соседа можно осуществить, просто установив верхнюю границу количества точек для проверки в дереве или прервав процесс поиска на основе часов реального времени (что может быть более уместным в аппаратных реализациях). Ближайшего соседа для точек, которые уже находятся в дереве, можно достичь, не обновляя уточнение для узлов, которые дают нулевое расстояние. В результате это имеет недостаток в виде отбрасывания точек, которые не являются уникальными, но расположены совместно с исходной точкой поиска.
Приблизительный ближайший сосед полезен в приложениях реального времени, таких как робототехника, из-за значительного увеличения скорости, достигаемого за счет отсутствия исчерпывающего поиска лучшей точки. Одной из его реализаций является поиск best-bin-first .
Поиск по диапазону ищет диапазоны параметров. Например, если дерево хранит значения, соответствующие доходу и возрасту, то поиск по диапазону может быть чем-то вроде поиска всех членов дерева, имеющих возраст от 20 до 50 лет и доход от 50 000 до 80 000. Поскольку деревья kd делят диапазон домена пополам на каждом уровне дерева, они полезны для выполнения поиска по диапазону.
Анализ двоичных деревьев поиска показал, что худшее время для диапазона поиска в k -мерном k -d дереве, содержащем n узлов, определяется следующим уравнением. [13]
Нахождение ближайшей точки в среднем является операцией в случае случайно распределенных точек, хотя анализ в целом сложен. [14]
В пространствах высокой размерности проклятие размерности приводит к тому, что алгоритму необходимо посещать гораздо больше ветвей, чем в пространствах низкой размерности. В частности, когда число точек лишь немного превышает число измерений, алгоритм лишь немного лучше линейного поиска по всем точкам. Как правило, если размерность равна k , число точек в данных, n , должно быть . В противном случае, когда деревья k -d используются с данными высокой размерности, большинство точек в дереве будут оценены, и эффективность будет не лучше, чем при исчерпывающем поиске, [15] и, если требуется достаточно быстрый ответ, вместо этого следует использовать приближенные методы ближайшего соседа.
Кроме того, даже в низкоразмерном пространстве, если среднее попарное расстояние между k ближайшими соседями точки запроса значительно меньше среднего расстояния между точкой запроса и каждым из k ближайших соседей, производительность поиска ближайшего соседа ухудшается до линейной, поскольку расстояния от точки запроса до каждого ближайшего соседа имеют одинаковую величину. (В худшем случае рассмотрим облако точек, распределенных на поверхности сферы с центром в начале координат. Каждая точка равноудалена от начала координат, поэтому поиск ближайшего соседа от начала координат должен будет перебрать все точки на поверхности сферы, чтобы определить ближайшего соседа, который в этом случае даже не является уникальным.)
Чтобы смягчить потенциально значительное снижение производительности поиска по дереву k -d в худшем случае, можно задать параметр максимального расстояния для алгоритма поиска по дереву , и рекурсивный поиск может быть сокращен всякий раз, когда ближайшая точка в данной ветви дерева не может быть ближе, чем это максимальное расстояние. Это может привести к тому, что поиск ближайшего соседа не вернет ближайшего соседа, что означает, что ни одна точка не находится в пределах этого максимального расстояния от точки запроса.
Вместо точек k -d дерево может также содержать прямоугольники или гиперпрямоугольники . [16] [17] Таким образом, поиск по диапазону становится проблемой возврата всех прямоугольников, пересекающих прямоугольник поиска. Дерево строится обычным способом со всеми прямоугольниками в листьях. В ортогональном поиске по диапазону противоположная координата используется при сравнении с медианой. Например, если текущий уровень разделен вдоль x high , мы проверяем координату x low прямоугольника поиска. Если медиана меньше координаты x low прямоугольника поиска, то никакой прямоугольник в левой ветви никогда не сможет пересечься с прямоугольником поиска и, следовательно, может быть отсечен. В противном случае следует пройти обе ветви. См. также interval tree , которое является одномерным особым случаем.
Также возможно определить k -d дерево с точками, хранящимися исключительно в листьях. [4] Эта форма k -d дерева допускает множество механик разделения, отличных от стандартного медианного разделения. Правило разделения по средней точке [18] выбирает середину самой длинной оси пространства, в котором выполняется поиск, независимо от распределения точек. Это гарантирует, что соотношение сторон будет не более 2:1, но глубина зависит от распределения точек. Разновидность, называемая скользящей средней точкой, разделяет только по середине, если есть точки по обе стороны от разделения. В противном случае она разделяет по точке, ближайшей к середине. Манивонгватана и Маунт показывают, что это обеспечивает «достаточно хорошую» производительность на общих наборах данных.
Используя скользящую среднюю точку, можно ответить на приблизительный запрос ближайшего соседа в . Приблизительный подсчет диапазона можно ответить с помощью этого метода.
Близкие варианты:
Похожие вариации:
Проблемы, которые можно решить с помощью k -d деревьев: