Задать вопрос
@allex997

Как найти кратчайший путь с минимальным количеством поворотов(повороты в приоритете)?

Как найти кратчайший путь с минимальным количеством поворотов ?

В данном примере 1 это оранжевая стена а 0 черная пустота:
5fa044698a457815018169.png

Красная линия это то как путь должен быть проложен. А белая как он проложен.

Код можно взять с https://github.com/StanislavPetrovV/Python-Dijkstr... файл bfs_pygame_control.py
  • Вопрос задан
  • 491 просмотр
Подписаться Средний 1 комментарий
Решения вопроса 1
@allex997 Автор вопроса
Для решения задачи я использовал алгоритм Ли для построение карты перемещения, потом использовал графы для нахождения все путей, а потом нашёл все пути с минимальным количеством поворотов . У меня всё вышло в 300 строк кода.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 5
xmoonlight
@xmoonlight
https://sitecoder.blogspot.com
Начинать нужно с поиска угла между начальной и конечной точкой.
Здесь: L-образный угол (левого нижнего) квадрата 9x9 (не вдоль кромки красная линия, а на одну ближе к центру!).
А уже от него - достраивать до начального и конечного пункта.
Ответ написан
Комментировать
Adamos
@Adamos
Найдя путь тем алгоритмом, который его сейчас успешно находит (если он действительно способен находить обход препятствий, например), можно применить к нему простые упрощения - заменять два колена их отображением так, чтобы хоть одно из них стало продолжением предыдущего колена, и количество загибов уменьшилось. Если, конечно, препятствия этому не мешают. На этой карте вы придете к тем же трем загибам.
Ответ написан
Комментировать
dollar
@dollar
Делай добро и бросай его в воду.
Неоптимизированный алгоритм (для понимания сути):
Перебираем вообще все всевозможные пути достижения цели. Считаем количество шагов. Так как нужен самый быстрый путь, то ищем минимальное количество шагов. Теперь среди множества найденных "мнимальных" путей выбираем те пути, которые имеют минимальное количество поворотов. Ответ будет любой из найденных в итоге.

Можно немного модифицировать, считая сразу (шаги + повороты). Естественно, поворот будет иметь вес больше, чем вес 1 шага. Например, в 10 раз больше, чтобы подчеркнуть важность именно поворотов, что они в приоритете. Тогда расстояние будет считаться по формуле:
S = шаги + 10 * повороты
Очевидно, что при такой схеме любой лишний поворот резко увеличит расстояние. Это и будет критерием для отбраковывания неудачного пути.

Эту идею можно интегрировать в существующий алгоритм, которым вы пользуетесь, но зависит от типа вашего алгоритма, могут быть нюансы.
Ответ написан
mayton2019
@mayton2019
Bigdata Engineer
Похоже это называется Расстояние Манхеттена.
Как пешеход идёт по кварталам.

Кстати автор слегка хитрит. Там ещё есть два пути с таким же числом поворотов.

Нужно ли искать все? И как долго автор хочет искать пока задача не превратилась чисто в комбинаторную?
Ответ написан
wataru
@wataru Куратор тега Алгоритмы
Разработчик на С++, экс-олимпиадник.
Вам надо построить хитрый граф.

Во-первых, все расстояния в графе будут не одно число, а пара - собственно, длина пути и количество поворотов. При суммировании двух длин складывайте их покомпонентно, при сравнении сравнивайте сначала длины, а потом количество поворотов. Теоретически, можно схлопнуть все назад в одно число, если считать 100000*<длина> + <количество поворотов>. Но могут быть спецэффекты, если пути очень сложные и имеюют более 100000 поворотов. Еще, опасайтесь переполнения.

Для каждой клетки сделайте 4 вершины, которые будут означать, что вы находитесь в этой клетке и "смотрите" в одну из 4-х сторон.

Создайте 4 ребера между соседними направлениями с длиной {0, 1} (длина 0, но 1 поворот). От каждой вершины создайте также ребро длинной {1, 0} (длина - 1, 0 поворотов) в вершину в соседней клетке с тем же направлением (от "верхней" из 4-х вершин сделайте ребро в "верхнюю" же вершину в клетке сверху. Аналогично по трем остальным направлениям).

Теперь кратчайший путь в этом графе будет то - что вам надо. Есть еще вопрос с начальными конечным направлением. Можно добавить в начальную и конечную клетки "центральную" вершину, в которая связанна ребрами длины {0, 1} (Важно сделать 1 поворот, иначе можно будет крутиться на месте через эту центральную вершину). Считайте, что эта вершина - "смотреть в пол". Вы начинаете в какой-то клетке, смотря в пол, и вам надо оказаться в другой клетке, опять же смотря в пол. Вы можете в клетке повернуться, или перейти в клетку впереди вас.

Поскольку тут уже разные длины ребер, обход в ширину уже не будет искать кратчайшее расстояние. Но можно использовать дейкстру/A*.

При реализации не надо даже хранить все вершины и ребра. Просто у вас теперь вершина вместо {x, y} будет описываться {x, y, d}. Вместо перебора 4-х направлений [-1,0],[0,-1],[0,1],[1,0] - вы делаете переход {x+dx[d],y+dy[d],d} или меняете d на (d+1)%d и (d+3)%4. И в очередях вы сохраняете не одно число, а пару {length, turns}. Для A* нужно еще прикидывать оставшуюся длину - ну вы по длине берите, что у вас уже есть, а по количеству поворотов берите 1, если у начала и конца разные направления.

Играясь с длинами ребер и их сравнениями вы можете, например, разрешать чуть более длинные пути, если в них сильно меньше поворотов. (например, можно обойти лабиринт по периметру, чем чуть короче но зиг-загами ходить внутри). Для этого длина ребра может быть length*K+turns.

Поскольку тут в 4-5 раз больше вершин, это будет работать в 4-5 раз медленнее.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы