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

25

пример

Это пример того, что я хочу сделать с помощью кода. Я знаю, что вы можете использовать поиск точек перехода, чтобы без проблем добраться от зеленого узла к красному узлу или даже к A *. Но как вы рассчитываете это с перекосами.

На изображении вы можете видеть, что требуется всего 8 ходов, чтобы пройти от зеленого узла к красному узлу при выборе синего пути. Синий путь мгновенно перемещает вашу позицию от одного фиолетового узла к следующему. Пространство посередине, которое стоит 2 хода, является точкой между двумя зонами деформации, в которую вы должны перейти, чтобы добраться.

Очевидно, что быстрее идти по синему пути, поскольку вам нужно всего лишь приблизиться на половину (примерно) до желтого пути, но как мне сделать это программно?

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

Некоторые фиолетовые перекосы являются двунаправленными, а некоторые нет, то есть иногда вы можете входить в варп только с одной стороны, но не возвращаться после деформации.

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

Программа должна была бы каким-то образом выяснить, что выгоднее сделать второй перекос, а не идти с первого прыжка. Таким образом, вместо перемещения 6 точек, затем деформации, а затем перемещения оставшихся 8 шагов пешком (что также быстрее, чем вообще не использовать деформации), потребуется 6 ходов, а затем два шага ко второй деформации.

РЕДАКТИРОВАТЬ: я понял, что синий путь на самом деле займет 12 ходов, а не 8, но вопрос остается тем же.

Джефф Смит
источник
4
Разве синий путь не должен быть 12 (включая два, чтобы добраться от последнего фиолетового до красного)?
BlueRaja - Дэнни Пфлюгофт
5
Синий на самом деле 12 (7 + 3 + 2) ходов, нет?
Даниэль Жур
Ой, перепутал, спасибо ребята! @DanielJour and Blue
Джефф Смит
«Правильный» способ моделирования расстояний - использовать топологию и моделировать ее как поверхность более высокого размера. Интересно, будет ли такой ответ уместным здесь?
Geeky I

Ответы:

49

Большинство алгоритмов поиска пути определены в терминах графиков, а не в терминах сеток. На графике связь между двумя удаленными узлами в действительности не является проблемой.

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

Конечно, алгоритмы поиска пути, такие как Dijkstra, которые не используют эвристику, будут работать. Это больше похоже на поиск в ширину и выберет ваши червоточины без лишних усилий. Тем не менее, Дейкстра будет посещать больше узлов, чем A *, с хорошей эвристикой. (Дейкстра эквивалентен A * с heuristic(x) = 0.)

Я думаю, что A * будет работать, если вы используете эвристику, которая рассматривает все исходящие червоточины как червоточину непосредственно до цели: эвристика может недооценивать расстояние, но никогда не должна переоценивать его. Т.е. эвристика будет:

def wormhole_heuristic(x):
  return min(euclidean_distance(x, g) for g in [goal, wormholes...])

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

def wormhole_heuristic(x):
  direct = euclidean_distance(x, goal)
  via_wormhole = min(euclidean_distance(x, w) + wormhole_path_distance(w, goal) for w in wormholes)
  return min(direct, via_wormhole)

Как отмечает @Caleth в комментариях, все это очень настраиваемо, и мы можем улучшить эвристику первой червоточины, не выполняя полный поиск пути через сеть червоточин, добавляя расстояние между последним выходом червоточины и целью. Поскольку мы не знаем, какой выход из червоточины будет использоваться последним, и мы не должны переоценивать, мы должны принять выход, ближайший к цели:

def wormhole_heuristic(x):
  direct = euclidean_distance(x, goal)
  to_next_wormhole = min(euclidean_distance(x, w) for w in wormholes)
  from_last_wormhole = min(euclidean_distance(w.exit, goal) for w in wormholes)
  via_wormhole = to_next_wormhole + from_last_wormhole
  return min(direct, via_wormhole)
Амон
источник
10
Стоит отметить, что Дейкстра просто А * сdijkstra_heuristic(x) = 0
Caleth
Я не понимаю, что вы подразумеваете под [* червоточины, цель], вы могли бы объяснить это?
Джефф Смит
1
«Евклидово расстояние до ближайшего выхода из червоточины» является более дешевой оценкой, wormhole_path_distanceчем поиск по подграфу, и менее недооценено, чем «все выходы находятся на цели».
Caleth
3
@Caleth конечно! Здесь много возможностей для тонкой настройки, например, мы могли бы посмотреть вперед n = 3 прыжка. Поиск по подграфу соответствует закрытию всех ациклических скачков червоточины. Ваше предложение , чтобы смотреть вперед п = 1 скачками очень элегантно , как это имеет в основном нулевую добавочную стоимость :)
Амон
1
Для простоты предположим, что существует только одна червоточина (два узла), затем вы можете преобразовать эту плоскую 1-червоточину в 2 зеркальные плоскости, скопировав симметричную плоскость с равноотстоящей линией между этими двумя точками в качестве оси симметрии. Теперь у вас есть два плана, давайте назовем их реальным планом (вы не берете червоточину) и воображаемым планом (вы взяли червоточину). Теперь введем координату Z. Эта координата будет 0 для каждой точки в реальной плоскости, и это будет dist (червоточина, точка) для каждой точки воображаемой плоскости. После этого примените A * для трехмерного пространства.
lilezek
5

У вас есть график с 6 вершинами на сетке с координатами:

A ( 0,0)
B ( 4,7)
C ( 7,4)
D (10,4)
E (16,2)
F (16,0)

Вы можете создать полный график по этим вершинам и назначить стоимость для каждого ребра, где стоимость MAX( ABS( x1 - x2 ), ABS( y1 - y2 ) )для стандартных ребер и стоимость 0 для червоточин.

Это даст вам затраты (в виде матрицы смежности):

   A  B  C  D  E  F
- -- -- -- -- -- --
A  -  7  7 10 16 16
B  7  -  0  6 12 12
C  7  0  -  3  9  9
D 10  6  3  -  0  6
E 16 12  9  0  -  2
F 16 12  9  6  2  -

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

Затем вы можете использовать алгоритм Дейкстры с приоритетной очередью .

Начните с Aи вставьте каждый соседний край в очередь с приоритетами:

Формат: (путь: стоимость)

queue     = [ (A-B : 7), (A-C : 7), (A-D : 10), (A-E : 16), (A-F : 16) ]

Поскольку элементы помещаются в очередь - отслеживайте минимальную стоимость для каждой вершины и проталкивайте пути в очередь, только если она дешевле, чем существующая минимальная стоимость.

min-costs = { A: 0, B: 7, C: 7, D: 10, E: 16, F: 16 }

Удалите первый элемент из очереди и, если его стоимость все еще соответствует минимальной стоимости, переместите все составные пути, образованные этим путем и его смежными ребрами, обратно в очередь с приоритетами (если стоимость составных путей ниже, чем существующий минимум):

Удалять: (A-B : 7)

  • Попробуй (A-B-A : 14)- отвергни как дороже
  • Попробуй (A-B-C : 7)- отвергни как за ту же цену
  • Попробуй (A-B-D : 13)- отвергни как дороже
  • Попробуй (A-B-E : 19)- отвергни как дороже
  • Попробуй (A-B-F : 19)- отвергни как дороже

удалять (A-C : 7)

  • Попробуй (A-C-A : 14)- отвергни как дороже
  • Попробуй (A-C-B : 7)- отвергни как за ту же цену
  • Попробуй (A-C-D : 10)- отвергни как за ту же цену
  • Попробуй (A-C-E : 16)- отвергни как за ту же цену
  • Попробуй (A-C-F : 16)- отвергни как за ту же цену

удалять (A-D : 10)

  • Попробуй (A-D-A : 20)- отвергни как дороже
  • Попробуй (A-D-B : 16)- отвергни как дороже
  • Попробуй (A-D-C : 13)- отвергни как дороже
  • Попробуй (A-D-E : 10)- вставь в очередь
  • Попробуй (A-D-F : 16)- отвергни как за ту же цену

Теперь очередь будет выглядеть так:

queue     = [ (A-D-E : 10), (A-E : 16), (A-F : 16) ]
min-costs = { A: 0, B: 7, C: 7, D: 10, E: 10, F: 16 }

удалять (A-D-E : 10)

  • Попробуй (A-D-E-A : 26)- отвергни как дороже
  • Попробуй (A-D-E-B : 22)- отвергни как дороже
  • Попробуй (A-D-E-C : 19)- отвергни как дороже
  • Попробуй (A-D-E-D : 10)- отвергни как за ту же цену
  • Попробуй (A-D-E-F : 12)- вставь в очередь

Тогда очередь:

queue     = [ (A-D-E-F : 12), (A-E : 16), (A-F : 16) ]
min-costs = { A: 0, B: 7, C: 7, D: 10, E: 10, F: 12 }

Удалите (A-D-E-F : 12), найдите, что вы добрались до узла назначения по цене 12.

Примечание: путь (A-B-C-D-E-F), (A-C-D-E-F)и (A-D-E-F)все имеют одинаковую минимальную стоимость 12.

mt0
источник
0

Установите матрицу, содержащую все вершины, и используйте алгоритм Флойд-Валленштейна или алгоритм Беллмана-Форда. Оба приведут к матрице с кратчайшими путями между всеми точками. Затем вы можете пройтись по матрице, чтобы найти кратчайший путь, соединяющий две точки. (ваша проблема такая же, как асимметричный TSP).

Rene
источник