Алгоритм поиска ближайшего объекта на 2D сетке
скажем, у вас есть 2D-сетка с каждым пятном на сетке, имеющим x количество объектов (с x >=0). Мне трудно думать о очистить алгоритм таким образом, когда пользователь задает координату, алгоритм находит ближайшую координату (включая указанную) с объектом на ней.
для простоты предположим, что если 2 координаты находятся на одинаковом расстоянии, будет возвращена первая (или если ваш алгоритм не работает таким образом, то последний, не имеет значения).
Edit: координата, которая находится на расстоянии 1, должна быть 1 вверх, вниз, влево или вправо. Координаты, которые находятся по диагонали на 2.
в качестве примечания, что такое отличная, бесплатная, онлайн-ссылка для алгоритмов?
4 ответов
Udate
новая информация:
предполагая, что координата по диагонали находится в 2 км
этот алгоритм будет работать. Алгоритм ищет наружу по спирали, проверяя каждую точку в каждом "кольце", начатом изнутри.
обратите внимание, что он не обрабатывает ситуации за пределами границ. Поэтому вы должны изменить это, чтобы соответствовать вашим потребностям.
int xs, ys; // Start coordinates
// Check point (xs, ys)
for (int d = 1; d<maxDistance; d++)
{
for (int i = 0; i < d + 1; i++)
{
int x1 = xs - d + i;
int y1 = ys - i;
// Check point (x1, y1)
int x2 = xs + d - i;
int y2 = ys + i;
// Check point (x2, y2)
}
for (int i = 1; i < d; i++)
{
int x1 = xs - i;
int y1 = ys + d - i;
// Check point (x1, y1)
int x2 = xs + d - i;
int y2 = ys - i;
// Check point (x2, y2)
}
}
старый версия
предполагая, что в вашей 2D-сетке расстояние между (0, 0) и (1, 0) такое же, как (0, 0) и (1, 1). Этот алгоритм будет работать. Алгоритм ищет наружу по спирали, проверяя каждую точку в каждом "кольце", начатом изнутри.
обратите внимание, что он не обрабатывает ситуации за пределами границ. Поэтому вы должны изменить это, чтобы соответствовать вашим потребностям.
int xs, ys; // Start coordinates
if (CheckPoint(xs, ys) == true)
{
return (xs, ys);
}
for (int d = 0; d<maxDistance; d++)
{
for (int x = xs-d; x < xs+d+1; x++)
{
// Point to check: (x, ys - d) and (x, ys + d)
if (CheckPoint(x, ys - d) == true)
{
return (x, ys - d);
}
if (CheckPoint(x, ys + d) == true)
{
return (x, ys - d);
}
}
for (int y = ys-d+1; y < ys+d; y++)
{
// Point to check = (xs - d, y) and (xs + d, y)
if (CheckPoint(x, ys - d) == true)
{
return (xs - d, y);
}
if (CheckPoint(x, ys + d) == true)
{
return (xs - d, y);
}
}
}
если у вас есть список объектов
если бы у вас были все позиции всех объектов в списке, это было бы намного проще, так как вам не нужно было бы искать все пустые квадраты и выполнять 2D вычисления расстояния определить ближайший к вам. Цикл через ваш список объектов и рассчитать расстояние следующим образом:
Define your two points. Point 1 at (x1, y1) and Point 2 at (x2, y2).
xd = x2-x1
yd = y2-y1
Distance = SquareRoot(xd*xd + yd*yd)
затем просто выберите тот, с кратчайшим расстоянием.
если вы есть только 2D массив
Если, однако, проблема, как описано, предполагает 2D-массив, где местоположения объектов не могут быть перечислены без предварительного поиска для всех из них, то вам придется сделать спиральный цикл.
Поиск 'Спиральный Метод Поиска' придумывает несколько интересных ссылок. вот некоторый код, который делает спираль петля вокруг массива, однако это не работает с произвольной точки и спирали наружу, но должно дать вам некоторые хорошие идеи о том, как достичь того, чего вы хотите.
здесь аналогичный вопрос о заполнении значений в спиральном порядке в 2D-массиве.
в любом случае, вот как я буду решать эту проблему:
данной точке P
, создайте векторную пару, которая задает область вокруг P
.
если P = 4,4
Тогда ваша векторная пара будет 3,3|5,5
Loop каждое значение в тех границы.
for x = 3 to 5
for y = 3 to 5
check(x,y)
next
next
если найдено значение, выйдите. Если нет, увеличьте границы еще раз. Итак, в этом случае мы перейдем к 2,2 / 6,6
при циклической проверке значений убедитесь, что мы не вошли в какие-либо отрицательные индексы, а также убедитесь, что мы не превысили размер массива.
также, если вы расширяете границы n раз, вам нужно только зациклить внешние граничные значения, вам не нужно перепроверять внутренние значения.
какой метод быстрее?
все зависит от:
- плотность массива
- методика распределения
- количество объектов
плотность массива
если у вас есть массив 500x500 с 2 объектами в нем, то зацикливание списка всегда будет превосходить выполнение спирали
методика распределения
если есть шаблоны распределения (т. е. объекты как правило, группируются вокруг друг друга), то спираль может работать быстрее.
количество объектов
спираль, вероятно, будет работать быстрее, если есть миллион объектов, так как техника списка требует проверки и расчета каждого расстояния.
вы должны иметь возможность рассчитать самое быстрое решение, разработав вероятность заполнения пространства, по сравнению с тем, что решение списка должно проверять каждый объект каждый раз.
однако, с помощью метода списка, вы можете быть в состоянии сделать некоторые смарт-сортировки для повышения производительности. Наверное, стоит проверить.
Если ваши объекты плотные, то просто поиск близлежащих точек, вероятно, будет лучшим способом найти ближайший объект, спираль из центра. Если ваши объекты разрежены, то a дерева квадрантов или связанная структура данных (R-дерево и т. д.), вероятно, лучше. Вот это рецензия с примерами.
Я не знаю хорошей ссылки на онлайн-алгоритм, но я могу сказать, что если вы собираетесь писать больше, чем случайную строку кода, сохраняя ваши копейки купить среды CLR будет стоить денег. Есть лекции, основанные на этой книге в интернете, которые были кропотливо аннотированы Петерис Круминьш, но они охватывают только часть книги. Это одна из немногих книг, которые вам нужны.
следующее простое решение предполагает, что вы можете позволить себе хранить дополнительную информацию на ячейку сетки и что временные затраты на добавление новых объектов в сетку могут быть относительно высокими.
идея заключается в том, что каждая ячейка содержит ссылку на ближайшую занятую ячейку, что позволяет O(1) время запроса. Всякий раз,когда объект добавляется в позицию (i, j), выполните сканирование окружающих ячеек, покрывая кольца увеличивающегося размера. Для каждой сканируемой ячейки оцените ее текущий ближайшая ссылка на занятую ячейку и при необходимости замените ее. Процесс заканчивается, когда последнее сканируемое кольцо не изменяется вообще. В худшем случае процесс сканирует все ячейки сетки, но в конечном итоге становится лучше, когда сетка становится достаточно плотной.
Это решение просто реализовать, может иметь значительные накладные расходы (в зависимости от того, как ваша сетка организована в памяти), но обеспечивает оптимальное время запроса.