Нахождение самой дальней точки в одном наборе от другого набора

Моя цель - более эффективная реализация алгоритма, поставленного в этом вопросе .

Рассмотрим два набора точек (в N-пространстве. 3-пространстве для примера случая цветового пространства RGB, в то время как решение для 1-пространства 2-пространства отличается только вычислением расстояния). Как найти точку в первом наборе, которая наиболее удалена от ближайшего соседа во втором наборе?

В примере с 1 пробелом, учитывая наборы A: {2,4,6,8} и B: {1,3,5}, ответ будет 8, поскольку 8 находится на 3 единицы от 5 (ближайший сосед в B), в то время как все остальные члены A находятся всего на 1 единицу от своего ближайшего соседа в B. edit: 1-пространство чрезмерно упрощено, поскольку сортировка связана с расстоянием таким образом, что это не в более высоких измерениях.

Решение в исходном вопросе включает в себя сравнение грубой силы каждой точки в одном наборе (все R, G, B, где 512> = R + G + B> = 256 и R% 4 = 0 и G% 4 = 0 и B % 4 = 0) в каждую точку в другом наборе (colorTable). Игнорируйте ради этого вопроса, что первый набор разработан программно, а не повторяется как сохраненный список, как второй набор.

Ответов (6)

Решение

Сначала вам нужно найти ближайшего соседа каждого элемента в другом наборе.

Чтобы сделать это эффективно, вам понадобится алгоритм ближайшего соседа . Лично я бы реализовал kd-дерево только потому, что делал это в прошлом в своем классе алгоритмов, и это было довольно просто. Другой жизнеспособной альтернативой является R-дерево .

Сделайте это один раз для каждого элемента самого маленького набора. (Добавьте один элемент от наименьшего к большему и запустите алгоритм, чтобы найти его ближайшего соседа.)

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

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

Затем, как только вы закончите, просто спросите кучу о максимуме.

Время выполнения этого разбивается следующим образом:

N = размер меньшего набора
M = размер большего набора

  • N * O (log M + 1) для всех проверок ближайшего соседа kd-дерева.
  • N * O (1) для вычисления евклидова расстояния перед добавлением его в кучу.
  • N * O (log N) для добавления пар в кучу.
  • O (1), чтобы получить окончательный ответ: D

Итак, в итоге весь алгоритм - O (N * log M).

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

* Отказ от ответственности: все это предполагает, что вы не будете использовать слишком большое количество измерений и что ваши элементы распределены в основном случайным образом.

Возможно, я неправильно понимаю вопрос, но не было бы проще просто поменять знак на всех координатах в одном наборе данных (т.е. умножить один набор координат на -1), а затем найти первого ближайшего соседа (который был бы самый дальний сосед)? Вы можете использовать свой любимый алгоритм knn с k = 1.

Для каждой точки в наборе B найдите расстояние до ближайшего соседа в наборе A.

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

РЕДАКТИРОВАТЬ : я имел в виду nlog (n), где n - сумма размеров обоих наборов.

В наборе 1-Space I вы могли бы сделать что-то вроде этого (псевдокод)

Используйте такую ​​структуру

Struct Item {
    int value
    int setid
}

(1) Максимальное расстояние = 0
(2) Считать все наборы в структуры элементов
(3) Создать массив указателей на все элементы
(4) Сортировать массив указателей по полю Item-> value структуры
(5) Прогулка массив от начала до конца, проверяя, отличается ли Item-> setid от предыдущего Item-> setid, если (SetIDs разные),
проверьте, больше ли это расстояние, чем Max Distance, если так установите MaxDistance на это расстояние

Верните максимальное расстояние.

Мне кажется, что наиболее очевидный подход состоит в том, чтобы построить древовидную структуру на одном наборе, чтобы вы могли относительно быстро его искать. Для этого, вероятно, подойдет kd-tree или подобное.

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

Это nlog (n) для построения дерева и log (n) для одного поиска, поэтому все это должно выполняться в nlog (n).

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

Например, если вы работали в двух пространствах, разделите вашу плоскость на сетку 5 x 5, получив 25 квадратов с 25 группами точек.

В 3-х ячейках разделите куб на сетку 5 x 5 x 5, получив 125 кубиков, каждый с набором точек.

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