Алгоритм разделения топологических слоев
У меня следующая проблема. Предположим, у вас есть большой массив манхэттенских многоугольников на плоскости (их стороны параллельны оси x или y). Мне нужно найти полигоны, расположенные ближе, чем какое-то значение дельты. Вопрос - как сделать это наиболее эффективным способом, ведь количество таких полигонов очень велико. Я буду рад, если вы дадите мне ссылку на внедренное решение, которое будет легко адаптировать для моего случая.
1 ответ
Первое, что приходит на ум, это алгоритм развертки и сокращения (также известный как сортировка и развертка).
По сути, вы сначала узнаете "границы" каждой фигуры вдоль каждой оси. Для оси x это будут самые левые и самые правые точки на фигуре. Для оси у, самый верхний и самый нижний.
Допустим, у вас есть связанная структура, которая выглядит примерно так:
struct Bound
{
float value; // The value of the bound, ie, the x or y coordinate.
bool isLower; // True for a lower bound (leftmost point or bottommost point).
int shapeIndex; // The index (into your array of shapes) of the shape this bound is on.
};
Создайте два массива этих границ, один для оси x и один для y.
Bound xBounds* = new Bound[2 * numberOfShapes];
Bound yBounds* = new Bound[2 * numberOfShapes];
Вам также понадобятся еще два массива. Массив, который отслеживает, сколько осей каждая пара фигур находится близко друг к другу, и массив пар-кандидатов.
int closeAxes* = new int[numberOfShapes * numberOfShapes];
for (int i = 0; i < numberOfShapes * numberOfShapes; i++)
CloseAxes[i] = 0;
struct Pair
{
int shapeIndexA;
int shapeIndexB;
};
Pair candidatePairs* = new Pair[numberOfShapes * numberOfShape];
int numberOfPairs = 0;
Перебирайте список фигур и заполняйте массивы соответствующим образом, с одной оговоркой: поскольку вы проверяете на предмет близости, а не пересечения, добавьте дельту к каждой верхней границе. Затем отсортируйте каждый массив по значению, используя любой алгоритм, который вам нравится.
Затем сделайте следующее (и повторите для оси Y):
for (int i = 0; i + 1 < 2 * numberOfShapes; i++)
{
if (xBounds[i].isLower && xBounds[i + 1].isLower)
{
unsigned int L = xBounds[i].shapeIndex;
unsigned int R = xBounds[i + 1].shapeIndex;
closeAxes[L + R * numberOfShapes]++;
closeAxes[R + L * numberOfShapes]++;
if (closeAxes[L + R * numberOfShapes] == 2 ||
closeAxes[R + L * numberOfShapes] == 2)
{
candidatePairs[numberOfPairs].shapeIndexA = L;
candidatePairs[numberOfPairs].shapeIndexB = R;
numberOfPairs++;
}
}
}
Все пары кандидатов меньше, чем дельта на каждой оси. Теперь просто проверьте каждую пару кандидатов, чтобы убедиться, что они на самом деле меньше дельты друг от друга. Я не буду вдаваться в подробности, как именно это сделать в данный момент, потому что, ну, я на самом деле не думал об этом, но, надеюсь, мой ответ, по крайней мере, поможет вам начать. Я полагаю, что вы могли бы просто проверить каждую пару отрезков линии и найти кратчайшее расстояние x или y, но я уверен, что есть более эффективный способ сделать шаг "узкой фазы".
Очевидно, что фактическая реализация этого алгоритма может быть намного более сложной. Моей целью было сделать объяснение ясным и кратким, а не элегантным. В зависимости от макета ваших фигур и алгоритма сортировки, который вы используете, один прогон этого примерно между O(n) и O(n log n) с точки зрения эффективности, в отличие от O(n^2), чтобы проверить каждый пара фигур.