Эффективный алгоритм поиска пути, избегающий зигзага
Я разрабатываю программное обеспечение, которое соединяет объекты с проводами. Эта проводка имеет правило, что эти провода не могут проходить на других объектах, и диагональное перемещение не принимается. ![like in this screenshot]()
Все алгоритмы кратчайшего пути, которые я знаю (A *, dijkstra и т.д.), обнаруживают этот тип путей:
![]()
Мне не нужны лишние зигзаги во втором скриншоте. Как достичь этой цели?
Примечание. Любой, кто хочет попробовать алгоритмы, может использовать это приложение.
Другое примечание: это точная ситуация, в которой я не хочу. Он находит путь зигзага вместо того, чтобы "идти вправо, пока вы не достигнете позиции х цели, идите вверх, пока не достигнете позиции y цели", которая имеет одинаковую стоимость с зигзагообразным.
![enter image description here]()
Ответы
Ответ 1
Ваш текущий алгоритм находит кратчайший путь Pmin
, но улучшенный алгоритм должен найти кратчайший путь, который принимает минимальное количество оборотов (Pmin, Tmin)
. Общее решение требует, чтобы вы работали с парой чисел вместо одного числа. Если только что найденный Pnew
меньше текущего Pmin
ИЛИ, если он равен, но Tnew
меньше, чем Tmin
, то возьмите (Pnew, Tnew)
в качестве нового минимального пути.
Если плата достаточно мала, вы все равно можете использовать одно число, которое вы используете в настоящее время, но это число должно быть составным числом C = P * N + T
, где N
является достаточно большим и достаточно малым постоянным числом. Он должен быть больше, чем максимально возможный T для этой платы, что составляет почти общее количество плиток в доске. Он также должен быть достаточно мал, чтобы не было целочисленного переполнения, когда алгоритм обрабатывает самый большой путь на доске, что также является общим количеством плиток в доске. Таким образом, N
должно удовлетворять этим двум терминам (B - общее количество плиток в доске):
N > B
B * N < INT_MAX
Если B
больше, чем SQRT(INT_MAX)
, то эта система неразрешима, и вы должны пойти с парой значений. N
должно быть SQRT(INT_MAX)
, которое при 2 32 равно 2 16.
Основная проблема теперь состоит в том, как подсчитать все повороты, но это зависит от алгоритма, который у вас есть. Это не должно быть слишком сложно добавить.
Ответ 2
Интуитивно, вы можете сделать это, предоставив вашему агенту "импульс".
В частности, увеличьте размер вашего пространства состояния в четыре раза; вы отслеживаете, был ли агент последним перемещен вверх, вправо, влево или вниз. Увеличьте затраты в вашей сети на некоторый большой коэффициент и назначьте небольшой штраф за ход, который изменит направление.
Ответ 3
Для расчета расстояния используйте пару значений (удваивает, целые числа и т.д.).
Первое - это расстояние, второе - количество оборотов.
Сортировка лексически, поэтому первая имеет значение больше, чем вторая.
Это чище, чем "использовать небольшой штраф за повороты" как математически, так и программно.
Каждый node дублируется. node ", введенный вертикально" и "введённый горизонтально", поскольку они имеют значение с количеством оборотов.
Эвристика - это расстояние Манхэттена, с поворотом, если вы не точно горизонтали или вертикально выровнены с целью.
Как нисходящая сторона, эта техника мешает оптимизации точки перехода, так как существует гораздо меньше симметричных путей к местоположению (поскольку некоторые пути имеют больше оборотов, чем другие).
Ответ 4
Внутри алгоритм оценивает множество возможных путей и выбирает самый короткий из них.
Если вы слегка отрегулируете алгоритм, поэтому он вычисляет дополнительное наказание за каждое изменение направления, тогда он выберет самый короткий путь с наименьшим изменением направления.
Ответ 5
Я не знаком с поисковыми алгоритмами специально, но это был бы лучший программный подход, псевдоним ниже.
Объекты, которые мы используем:
vertex { //generic x,y coordinate
int x;
int y;
}
vertices[3]; //3 vertices: 0, 1, 2 (0 is start, 1 is mid, 2 is end);
И наш алгоритм, который зависит от самого эффективного пути, уже найденного не с такой странностью, как ¯ | _ | ¯
boolean hasObstacles = false;
int increment = 0;
//there some other ways to set this up, but this should make the most sense to explaining which way we need to go
if(vertices[0].x < vertices[2].x)
increment = 1;
else
increment = -1;
for(int i = vertices[0].x; i != vertices[2].x; i = i + increment) {
//check for collision at coordinate (i, vertices[0].y), set hasObstacles to true if collision
}
if(vertices[0].y < vertices[2].y)
increment = 1;
else
increment = -1;
for(int i = vertices[0].y; i != vertices[2].y; i = i + increment) {
//check for collision at coordinate (vertices[2].x, i), set hasObstacles to true if collision
}
if(!hasObstacles) {
//we can remove these 3 vertices and add this point to our list of vertices.
vertex corner = new vertex(vertices[2].x, vertices[0].y) // relocation of point
}
Сканирование должно прогрессировать по одной вершине за раз. Если 3 вершины заменены одной вершиной, следующее сканирование должно использовать эту новую вершину как 0.
Ответ 6
Сейчас ваш алгоритм отвечает на вопрос "какие квадраты проходят по оптимальному пути?" Ваш график имеет node для каждого квадрата и ребро для каждой пары соседних квадратов.
Измените его на "где оптимальный путь пересекает границы между квадратами?"
Ваш график изменится:
- Узлы графа: посередине каждого края между соседними квадратами + начало + финиш.
- Графы графа: в каждом квадрате они соединяют каждую пару квадратных ребер.
И теперь вы можете по-разному оценивать соединения противоположных квадратных краев и соединений соседних квадратных краев. Увеличение веса до второго уменьшит количество зигзагов.
Ответ 7
Ваша проблема нетривиальна, поскольку, например, если вы жадно заходите так далеко, как можете вверх или направо, то вы можете столкнуться с плотным лабиринтом предметов, для которых требуется безумный путь зигзага, а если вы остановились перед плотным лабиринтом, вы могли бы изменить направлений меньше, по существу, обходя лабиринт. И вы можете столкнуться с этой дилеммой в любом месте по пути, а не только в начале. Один из способов решения этой проблемы - использовать Dijkstra и определить сетку местоположений, к которым вы можете путешествовать, а затем определить переход на 2 шага вперед вместо 1 шага. Определить расстояние между двумя связанными точками сетки очень мало, если движение чисто горизонтально или чисто вертикально в одном ориентированном направлении и очень велико, если движение меняет направление в середине. Затем, предполагая, что длина пути от начала до конца четна, кратчайший путь от начала до конца в этой структуре с двойным перемещением - это путь, который минимизирует количество зигзага. Если длина пути от начала до конца является нечетной, то используйте точки сетки на одно место в горизонтальном и вертикальном направлениях, чтобы начать, а затем длина пути от начала до конца будет четной (хотя вам придется запускать поиск пути для обоих возможные измененные начальные позиции).
Ответ 8
Все, что вам нужно, - немного изменить эвристику вашего алгоритма A *. Один на моей голове: если вам не нравятся эти повороты, вы можете просто наказывать каждый ход.
Таким образом, ваша эвристика будет зависеть от количества поворотов и расстояния Манхэттена до цели. Важно помнить, что он не должен переоценивать расстояние до цели. Подробнее о том, как выбрать эвристику здесь.