Алгоритм быстрого поиска животных вдали от стада
Я разрабатываю программу моделирования. Есть стада животных (гну), и в этом стаде мне нужно найти одно животное, которое находится вне стада.
На рисунке ниже зеленые точки удалены от стада. Именно эти моменты я бы хотел найти быстро.
![Green dots are away from the herd]()
Конечно, есть простой алгоритм для решения этой проблемы. Подсчитайте количество точек в окрестности каждой точки, а затем, если эта окрестность пуста (точка 0 в ней), то мы знаем, что эта точка находится далеко от стада.
Проблема заключается в том, что этот алгоритм неэффективен. У меня есть миллион очков, и применение этого алгоритма на каждом из миллионов точек очень медленно.
Есть ли что-то, что было бы быстрее? Возможно, используя деревья?
Изменить для @amit: мы хотим избежать этого случая. Была выбрана группа зеленых точек в левом углу, хотя они должны не, потому что это не одно животное, которое находится далеко от стада, это группа животных. Мы ищем только одно животное от стада (а не группы).
![A group of green dots away from the herd]()
Ответы
Ответ 1
Для запросов ближайших соседей часто используются kd-деревья. Это приведет к запросам O (n log n) (один запрос находится в log (n) раз n запросов, а построение kd-дерева само по себе в O (n log n)), которое я могу видеть довольно быстро для пары миллионы баллов, а также библиотеки, которые уже довольно эффективны (ANN).
Кроме того, ANN означает "приближенные ближайшие соседи" и может быть еще быстрее, когда точные расстояния не нужны. Поскольку в вашем случае вам нужно только определить, будет ли расстояние до ближайшего соседа большим или маленьким, вы можете установить довольно высокий порог, который сделает вещи еще быстрее.
Из этого вы можете определить распределение расстояния всем ближайшим соседом и найти выбросы. Сортировка всех этих расстояний для определения выбросов снова в O (n log n).
Ответ 2
Я думаю, что вы ищете алгоритм обнаружения аномалий (это неподдерживаемая проблема машинного обучения).
Идея состоит в том, чтобы найти экземпляры, которые "ведут себя" ненормально по сравнению с остальными экземплярами.
Набор видеороликов, начинающийся с этот (с онлайнового курса обучения машинам в Курсере) описывает проблему и то, как она может быть хорошо подошел.
EDIT:
Простая альтернатива будет заключаться в том, чтобы найти среднее из всех точек (животных) и "выбрать" животных, которые находятся далеко от него (или, альтернативно, все точки, которые имеют расстояние больше от некоторого порога).
Если у вас несколько групп, вы можете сначала cluster их первыми. Один из способов сделать это - k - означает кластеризацию и применить один из указанных подходов к каждой группе (кластеру).
Ответ 3
Поскольку вы ищете одинокое животное, вы можете использовать два выпуклых слоя для O (N log N + ab *) O (N log N), где a - размер первый корпус и b - размер второго корпуса.
- Создайте выпуклую оболочку из списка позиций
- Создайте вторую выпуклую оболочку из списка позиций, за исключением тех, что находятся в первом корпусе.
Животное во внешнем (первом) корпусе "изолировано", если его ближайшие соседи находятся достаточно далеко. Ближайшие соседи - это точки шкафа к этой точке (не той же точке) во внутреннем и внешнем корпусе. В случае внешнего корпуса вы, вероятно, можете пройти, просто проверив расстояние до точек слева и справа от рассматриваемой точки. Следовательно, a * b в большом O вместо a (a + b)
Если вы ожидаете случаев, когда один из "внутренних" животных стада считается изолированным (в этом случае внутреннее относится к любому животному, которое не образует внешний корпус), то вышеупомянутый метод, вероятно, т работы. В этом случае вам нужно будет использовать более сложный подход.
Он также, вероятно, будет неэффективным, если a + b близок к N, поскольку он будет в основном O (N ^ 2). Хотя в этом случае маловероятно, чтобы какое-либо животное было очень изолировано.
Изменить:
Я должен также указать, что существуют динамические выпуклые структуры корпуса, которые могут использоваться для поддержания выпуклой оболочки, где точки движутся просто путем добавления и удаления точек. Это, вероятно, будет полезно для обновлений в реальном времени.
* Это фактически O (N), используя вращающиеся суппорты.
Ответ 4
Вот простая идея. (метод кластеризации)
Поместите своих животных в сетку на основе их значений x, y.
Если вы не хотите, чтобы ложные обнаруженные выбросы вы могли использовать две сетки.
В этом примере я использую два контейнера сетки, иллюстрированные черными и синими линиями.
![grid]()
Выброс определяется как: an animals which is alone in both it blue and black grid.
Вы сохраняете ссылку между индексом сетки и животным, содержащимся в сетке.
Итерируйте животных и поместите их в сетки, используя их значения x, y.
Затем повторите черную решетку. Когда содержимое сетки равно 1, тогда найдите синюю ссылку на сетку через животное, которое находится внутри черной сетки. Проверьте содержимое синей сетки. Если это 1, то животное является выбросом.
Время работы должно быть довольно быстрым.
n: number of animals
b: size of black grid
Поместите животных в сетки O(n)
. Итерирование черной сетки O(b)
Это дает O(n) + O(b)
в общей сложности для создания информации и поиска выбросов.
Локализация выбросов занимает O(b)
. Если ваша сетка достаточно мала, это обеспечит очень быстрое время работы.
Изображение выше должно иллюстрировать два отклонения.
Реализация должна быть относительно простой. Вы можете играть с вариантами стратегий на основе сетки, использовать разный макет сетки или использовать больше контейнеров с сеткой.
Edit:
Этот подход несколько связан с клеточным методом, описанным в этой статье, без расчета расстояния. http://www.slac.stanford.edu/cgi-wrap/getdoc/slac-r-186.pdf
Этот метод не будет исключать ложные обнаруженные выбросы для всех случаев. Для более совершенного решения (для всех возможных положений животных на карте) вам нужно будет добавить расчет расстояния от обнаруженного 1 животного в ячейке до содержимого соседних ячеек. Вы можете прочитать об этом здесь.
Ответ 5
Вы можете попробовать подход к кластеризации на основе триангуляции:
-
Сформируйте триангуляцию Delaunay в наборе данных. Для этого существуют эффективные алгоритмы, такие как CGAL и Triangle, которые предлагают производительность O(|V|*log(|V|))
.
-
Для каждой вершины в наборе вычисляют "меру длины", просматривая список прикрепленных ребер, записывая минимальную длину края для каждой вершины. Это должно быть O(|V|+|E|)
. (Вы также можете использовать квадратные длины краев, чтобы избежать квадратных корней!)
-
Выберите вершины на основе "мер длины", рассчитанных выше. Как это сделать будет зависеть от того, как вы классифицируете "далеко" от стада. Несколько возможностей:
-
Простым подходом было бы просто использовать допуск статической длины, чтобы любые вершины были классифицированы как "прочь", если их измерения длины превышают это значение. Это будет тест O(|V|)
.
-
Возможны также более сложные подходы, такие как установка допуска длины, основанного на коэффициенте средней длины края для всех краев в триангуляции, - это уменьшит допуск при среднем распределении стада. Это будет тест O(|V|+|E|)
.
Преимущество такого подхода заключается в том, что он должен быть устойчивым к стадам с небольшими "подгруппами" вне основного кластера (согласно вашему второму примеру).
Ответ 6
Чтобы ускорить такие запросы , используйте структуру пространственного индекса.
k-d-деревья, квадранты, R-деревья, сетки - это лишь некоторые из ваших вариантов.
В таких структурах индекса вы можете быстро найти ближайших соседей. Коровы, где ближайший (2-й ближайший, 3-й ближайший) сосед гораздо дальше, чем другие, вероятно, такие выбросы, которые вы ищете.
Какую структуру индекса выбрать, вероятно, самая большая проблема. По мере того, как вы делаете симуляцию, лучше всего можно эффективно обновлять ее. k-d-деревья не могут быть обновлены очень хорошо, но их нужно будет перестраивать время от времени (если вы реализуете это с умом, перестройка должна быть довольно быстрой). R * -trees, вероятно, лучше всего оптимизированы для перестройки, но они действительно предназначены для хранения на жестком диске.
Я полагаю, что тот, который предлагает лучшую производительность для моделирования в памяти, - это просто сетки. Вы можете поэкспериментировать с разными размерами сетки, выбирать в зависимости от того, что лучше подходит. Кроме того, они допускают некоторые довольно приятные оптимизации: в ячейке сетки с коровами n
расстояние до ближайшей коровы n-1 не более sqrt(w*w+h*h)
, где w
и h
- ваши расстояния в сетке. Таким образом, вам может не понадобиться реально смотреть на те ячейки, в которых есть "достаточно" коров. n
может быть ниже 3 для вас. Теперь в ячейках сетки с одной коровой, это еще не должно быть выбросом. Это может быть прямо на краю соседней ячейки, которая довольно заполнена. Но таких клеток не должно быть много, вы можете легко проверить этих коров.
Ответ 7
Как насчет этого:
- Сортируйте своих животных в X-направлении.
- Найти значения X, которые находятся далеко от обоих предшествующих и следующих элементов
- Это кандидаты для одиноких парней.
- Повторите то же самое для направления Y
Кандидаты в обоих списках (X и Y), безусловно, разделены. Это также почти наверняка для кандидатов, которые существуют только в одном списке.
Сложность - O (n log n) для сортировки и O (n) для сканирования.
Я сомневаюсь, что вы можете улучшить ситуацию, не раскрывая свою структуру данных.
Шаг 1 также может быть разрешен с использованием ведер или сортировки радиуса, которая имеет сложность O (n)
Если вы можете сохранить эти два отсортированных списка, я бы добавил свойство "lonley" каждому животному. Поскольку вы постоянно повторяете свои животные, вы просто обновляете статус "lonley", проверяя расстояние до элементов слева и справа от текущей позиции в отсортированном X/Y-массиве.
Ответ 8
Вот простая процедура линейного времени:
Предполагая, что в любой момент времени есть только одно стадо, подумайте о позициях вашего животного в качестве образцов из двумерного (нормального?) распределения. Вычислить среднее и стандартное отклонение популяции в линейном времени. Вычислите расстояние Махаланобиса между средним и каждым животным в линейном времени. Любое животное, превышающее некоторый порог t
, не является стадом, также предложенным @amit. Это зависит от вас, чтобы установить этот порог. Один из возможных вариантов состоит в том, чтобы вручную использовать несколько примеров и использовать их для настройки значения, что легко, потому что расстояние Махаланобиса является масштабно-инвариантным. Моя интуиция заключается в том, что 3 является хорошей отправной точкой - что-то большее, чем 3 стандартных отклонения от среднего, является выбросом.