Як знайти найкоротший шлях з червоточиними вузлами?


25

приклад

Це приклад того, що я хочу зробити за допомогою коду. Я знаю, що ви можете скористатися пошуком точки стрибка, щоб легко дістатися із зеленого вузла до червоного вузла без проблем, або навіть A *. Але як ви обчислите це за допомогою основи.

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

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

Для вирішення цієї проблеми припустимо, що навколо графіка є декілька фіолетових «деформацій», якими ви можете скористатися, І ми точно знаємо, куди буде перетворюватися кожна фіолетова точка і де вони знаходяться на графіку.

Деякі фіолетові основи мають двосторонній характер, а деякі - ні, значить, іноді ви можете вводити основу лише з одного боку, але не повертатися назад після викривлення.

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

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

EDIT: Я зрозумів, що синій шлях насправді займе 12 рухів замість 8, але питання залишається тим самим.


4
Чи не повинен синій шлях бути 12 (включаючи два, щоб дістатися від останнього фіолетового до червоного)?
BlueRaja - Danny Pflughoeft

5
Синій - це насправді 12 (7 + 3 + 2) рухів, ні?
Даніель Жур

Ой, заплутався, спасибі хлопці! @DanielJour and Blue
Джефф Сміт

"Правильним" способом моделювання відстаней було б використання топології та моделювання її як поверхні більш високого розміру. Цікаво, чи була б відповідь така відповідь тут?
Geeky I

Відповіді:


49

Більшість алгоритмів пошуку шляхів визначаються з точки зору графіків, а не через сітки. У графіку зв’язок між двома інакше віддаленими вузлами насправді не є проблемою.

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

Звичайно, алгоритми пошуку шляхів на зразок Dijkstra, які не використовують евристику, все одно працюватимуть. Це більше схоже на пошук за шириною, і без додаткових зусиль виберіть черв'ячні отвори. Однак Dijkstra відвідає більше вузлів, що A * з хорошою евристикою. (Dijkstra еквівалентний 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
Варто зазначити, що Дайкстра - це лише A * зdijkstra_heuristic(x) = 0
Калет

Я не розумію, що ви маєте на увазі під [* червоточиною, ціллю], чи пояснюєте ви це?
Джефф Сміт

1
"Евклідова відстань до найближчого виходу з червоточини" - це дешевша оцінка, wormhole_path_distanceніж пошук підграфів, і менша заниження, ніж "усі виходи на ціль".
Калет

3
@Caleth звичайно! Тут є великий потенціал тонкої настройки, наприклад, ми могли б вирішити заздалегідь n = 3 стрибки. Пошук підграграфа відповідає закриттю всіх ациклічних стрибків червоточини. Ваша пропозиція дивитися вперед n = 1 стрибків дуже елегантна, оскільки в основному нульова додаткова вартість :)
amon

1
Для похитнення простоти припустимо, що існує лише одна червоточина (два вузли), тоді ви можете перетворити цю площину 1-червового отвору в 2 дзеркальні площини, скопіювавши симетричну площину з рівновіддаленою лінією між цими двома точками як вісь симетрії. Тепер у вас є дві площини, давайте назвемо їх справжньою площиною (ви не приймаєте червоточину) і уявною площиною (ви взяли в червоточину). Тепер введемо координату Z. Ця координата буде дорівнює 0 для кожної точки реальної площини, і вона буде віддалена (червоточина, точка) для кожної точки уявної площини. Після цього застосуйте 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.


0

Створіть матрицю, що містить усі вершини, і використовуйте алгоритм Флойда-Валенштайна або Алгоритм Беллмана-Форда. Обидва отримають матрицю з найкоротшими можливими шляхами між усіма точками. Потім ви можете повторити матрицю, щоб знайти найкоротший шлях, що з'єднує дві точки. (ваша проблема така ж, як і асиметричний TSP).

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.