2014-10-10 7 views
22

Я разрабатываю программное обеспечение, которое соединяет объекты с проводами. Эта проводка имеет правило, что эти провода не могут проходить на других объектах, и диагональное перемещение не принимается. like in this screenshotЭффективный алгоритм поиска пути, избегающий zigzag's

Все кратчайшие пути алгоритмы, которые я знаю (A *, Дейкстра и т.д.) найти этот тип путей:

Я не хочу ненужных зигзагов во втором скриншоте. Как достичь этой цели?

Примечание: Любой, кто хочет попробовать алгоритмы, может использовать приложение this.

Другое примечание: Это точная ситуация, в которой я не хочу. Он находит путь зигзага вместо того, чтобы «идти вправо, пока вы не достигнете позиции х цели, идите вверх, пока не достигнете позиции y цели», которая имеет одинаковую стоимость с зигзагообразным. enter image description here

+3

Я знаю, что A * всегда найдет эти шаблоны зигзага из-за эвристики, которую он использует, но способ справиться с этим - установить эвристику в сторону горизонтальных/вертикальных движений по диагонали. Я не видел вашего алгоритма для этого, но его не должно быть сложно реализовать. Это также может работать для Dijkstra, потому что оба алгоритма работают почти одинаково. – smac89

+0

Возможно, вам удастся избежать этого с L1 (abs [x + y]) вместо L2 (sqrt [x² + y²]) в качестве эвристики. Если вы не можете шагнуть по диагонали, в любом случае это лучший эвристический подход. Но это всего лишь взломать, и работает ли он, зависит от того, как узлы выходят из очереди. –

+0

@larsmans, что вы подразумеваете под L1, L2? – Alper

ответ

17

Ваш текущий алгоритм находит кратчайший путь, 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 is 2 .

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

+0

Я принимаю это как ответ, потому что он дает детальную информацию о том, как я могу дать штраф за повороты. – Alper

8

Интуитивно вы можете сделать это, предоставив вашему агенту «импульс».

В частности, увеличьте размер вашего пространства состояния в четыре раза; вы отслеживаете, был ли агент последним перемещен вверх, вправо, влево или вниз. Увеличьте затраты в вашей сети на какой-то большой коэффициент и назначьте небольшой штраф за ход, который изменит направление.

+3

Мне это нравится. Тем не менее, пространство состояний удвоения кажется достаточно: «сюда, горизонтальным движением» и «попал сюда вертикальным движением». – Emile

1

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

объектов мы используем:

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's 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.

+0

Это интуитивный и эвристический подход. если я не смогу найти лучший способ, я это реализую. – Alper

1

Прямо сейчас ваш алгоритм отвечает на вопрос «какие квадраты проходят оптимальный путь?» У вашего графика есть узел для каждого квадрата и край для каждой пары смежных квадратов.

Изменить его на «где оптимальный путь пересекает границы между квадратами?»

Ваш график изменится:

  • узлов графа: середину каждого ребра между соседними квадратами + старт + финиш.
  • Графические края: на каждом квадрате они соединяют каждую пару квадратных ребер.

И теперь вы можете по-разному рассчитывать соединения противоположных квадратных краев и соединений соседних квадратных краев. Увеличение веса до второго уменьшит количество зигзагов.

1

Ваша проблема нетривиальна, поскольку, например,если вы жадно заходите так далеко, как можете вверх или направо, то вы можете столкнуться с плотным лабиринтом предметов, для которых требуется безумный путь зигзага, а если вы остановились перед плотным лабиринтом, вы могли бы изменить направлений меньше, по существу, обходя лабиринт. И вы можете столкнуться с этой дилеммой в любом месте по пути, а не только в начале. Один из способов решения этой проблемы - использовать Dijkstra и определить сетку местоположений, к которым вы можете путешествовать, а затем определить переход на 2 шага вперед вместо 1 шага. Определить расстояние между двумя связанными точками сетки очень мало, если движение чисто горизонтально или чисто вертикально в одном ориентированном направлении и очень велико, если движение меняет направление в середине. Затем, предполагая, что длина пути от начала до конца четна, кратчайший путь от начала до конца в этой структуре с двойным перемещением - это путь, который минимизирует количество зигзага. Если длина пути от начала до конца является нечетной, то используйте точки сетки на одно место в горизонтальном и вертикальном направлениях, чтобы начать, а затем длина пути от начала до конца будет четной (хотя вам придется запускать поиск пути для обоих возможные измененные начальные позиции).

4

Используйте пару значений (удваивает, целые числа и т. Д.) Для расчета расстояния.

Первый - это расстояние, второе - количество оборотов.

Сортировка лексически, поэтому первое имеет значение больше, чем второе.

Это чище, чем «использовать штраф за повороты» как математически, так и программно.

Каждый узел дублируется. Узел «вошел вертикально» и «вступил в горизонталь», поскольку они имеют значение для количества поворотов.

Эвристика - это расстояние в Манхэттене, с поворотом, если вы не точно горизонтали или вертикально выровнены с целью.

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

2

Внутренний алгоритм оценивает множество возможных путей и выбирает самый короткий.

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

1

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

Таким образом, ваша эвристика будет зависеть от количества поворотов и расстояния Манхэттена до цели. Важно не забывать, что он не должен переоценивать расстояние до цели. Подробнее о том, как выбрать heuristic here.

Смежные вопросы