Як працює A * просування маршрутів?


67

Я хотів би зрозуміти на фундаментальному рівні спосіб роботи A *. Будь-яка реалізація коду чи psuedo-коду, а також візуалізації будуть корисними.


Ось невеличка стаття з анімованим GIF, що показує Алгоритм Дійкстри в русі.
flafur Waage

Сторінки Amit A * для мене були хорошим вступом. На YouTube можна знайти багато хороших візуалізацій, які шукають алгоритм AStar.
jdeseno

Мене збентежило декілька пояснень A *, перш ніж я знайшов цей чудовий підручник: policyalmanac.org/games/aStarTutorial.htm. Я здебільшого згадував про це, коли писав реалізацію A * в ActionScript: newarteest.com/flash /astar.html
поштовх

4
-1 вікіпедії є стаття про * з поясненням, вихідний код, візуалізації і ... . Деякі відповіді тут містять зовнішні посилання зі сторінки вікі.
користувач712092

4
Крім того, оскільки це досить складна тема, яка цікавить розробників ігор, я думаю, що нам тут потрібна інформація. Я пригадую, як Джоел одного разу сказав, що хоче, щоб StackOverflow став найкращим хітом, коли люди задають питання програмування в Google.
поштовх

Відповіді:


63

Відмова від відповідальності

В Інтернеті можна знайти багато прикладів коду та пояснень A *. Це питання також отримало багато чудових відповідей з великою кількістю корисних посилань. У своїй відповіді я спробую надати проілюстрований приклад алгоритму, який може бути легше зрозуміти, ніж код або описи.


Алгоритм Дейкстри

Щоб зрозуміти A *, пропоную спершу ознайомитися з алгоритмом Дійкстри . Дозвольте мені ознайомити вас з кроками, які алгоритм Діккстри виконує для пошуку.

Наш стартовий вузол є, Aі ми хочемо знайти найкоротший шлях F. Кожен край графіку має пов’язану з ним вартість руху (позначається чорними цифрами поруч із ребрами). Наша мета - оцінити мінімальну вартість подорожі для кожної вершини (або вузла) графіка, поки ми не потрапимо в наш вузол цілі.

Ілюстрований Дійкстра, частина 1

Це наш вихідний пункт. У нас є вузли списку для вивчення, цей список наразі:

{ A(0) }

Aмає вартість 0, всі інші вузли встановлюються нескінченними (у типовій реалізації це буде щось подібне int.MAX_VALUEчи подібне).

Ілюстрований Дійкстра, частина 2

Ми беремо вузол з найнижчою вартістю зі нашого списку вузлів (оскільки в нашому списку міститься лише Aцей наш кандидат) і відвідуємо всіх його сусідів. Ми встановлюємо вартість кожного сусіда:

Cost_of_Edge + Cost_of_previous_Node

і відслідковувати попередній вузол (показаний як маленький рожевий лист нижче вузла). Aможе бути позначено як вирішене (червоне) зараз, щоб ми більше не відвідували його. Наш список кандидатів зараз виглядає приблизно так:

{ B(2), D(3), C(4) }

Ілюстрований Дійкстра, частина 3

Знову ми беремо вузол з найнижчою вартістю з нашого списку ( B) та оцінюємо його сусідів. Шлях до Dдорожчого, ніж поточна вартість D, тому цей шлях можна відкинути. Eбуде додано до нашого списку кандидатів, який зараз виглядає так:

{ D(3), C(4), E(4) }

Ілюстрований Дійкстра, частина 4

Наступний вузол для вивчення - зараз D. Підключення до Cможна відмовитись, оскільки шлях не коротший за існуючу вартість. EОднак ми знайшли коротший шлях , тому вартість Eі попередній вузол буде оновлено. Наш список зараз виглядає так:

{ E(3), C(4) }

Ілюстрований Дійкстра, частина 5

Отже, як ми робили раніше, ми вивчаємо вузол з найнижчою вартістю з нашого списку, який зараз є E. Eмає лише один невирішений сусід, який також є цільовим вузлом. Вартість досягнення цільового вузла встановлена 10та попередній вузол до E. Наш список кандидатів зараз виглядає приблизно так:

{ C(4), F(10) }

Ілюстрований Дійкстра, частина 6

Далі ми розглядаємо C. Ми можемо оновити вартість та попередній вузол для F. Оскільки наш список зараз має Fяк вузол з найнижчою вартістю, ми закінчили. Наш шлях можна побудувати, відслідковуючи попередні найкоротші вузли.


Алгоритм *

Тож ви можете задуматися, чому я пояснив вам Dijkstra замість алгоритму A * ? Ну, різниця лише в тому, як ви зважуєте (або сортуєте) своїх кандидатів. З Dijkstra це:

Cost_of_Edge + Cost_of_previous_Node

З A * це:

Cost_of_Edge + Cost_of_previous_Node + Estimated_Cost_to_reach_Target_from(Node)

Де Estimated_Cost_to_reach_Target_fromзазвичай називають евристичну функцію. Це функція, яка спробує оцінити вартість досягнення цільового вузла. Хороша евристична функція дозволить досягти того, що для пошуку цілі доведеться відвідувати менше вузлів. Хоча алгоритм Діккстри розшириться на всі сторони, A * (завдяки евристичному) шукатиме у напрямку до цілі.

На сторінці Еміта про евристику є хороший огляд щодо поширеної евристики.


2
Варто зазначити, що евристика не завжди підштовхує пошук, щоб знайти найкращий маршрут. Наприклад, якщо ваш евристичний шлях - це відстань до цілі, але життєздатний маршрут знаходиться біля краю карти - пошук в цьому випадку шукатиме всю карту, перш ніж вона отримає потрібний маршрут. Напевно, ти, мабуть, думаєш, є щось, чого я не розумію? Це не працює! - що потрібно розуміти, це те, що мета евристики - скоротити пошук у випадках, які БОЮТЬ, а ваше завдання - знайти той, який є "найкращим" з усіх доступних рішень для ваших конкретних потреб.
SirYakalot

2
@AsherEinhorn Це все ще буде кращим (або в гіршому випадку рівним), ніж неінформований пошук, як у Джикстра.
bummzack

так, так, ви абсолютно праві. Можливо, мені було незрозуміло, що цей приклад, про який я говорив у вищенаведеному коментарі, є теоретичним "найгіршим випадком" сенаріо для A * з цим евристичним, Але це те, що зробив би Dijkstra КОЖЕН час. Більшу частину часу A * буде краще навіть при дуже простому евристиці. Моя думка полягала в тому, що евристичний спочатку може бути заплутаним, оскільки "відстань до цілі" не завжди має сенс для кожного сценарію - справа в тому, що це робить для більшості.
SirYakalot

Також дивіться це: qiao.github.io/PathFinding.js/visual
David Chouinard

Ця відповідь може використовувати згадку про те, що робить евристичним допустимим, в сенсі гарантування того, що A * знайде найкоротший шлях. (Коротко: якщо допустимо, евристика ніколи не повинна переоцінювати фактичну відстань до цілі. Неприпустима евристика іноді може бути корисною, але вони можуть спричинити повернення A * неоптимальних шляхів.)
Ілмарі Каронен

26

Пошук * шляху - це найкращий перший тип пошуку, який використовує додатковий евристичний характер.

Перше, що вам потрібно зробити - це розділити область пошуку. Для цього пояснення карта є квадратною сіткою плиток, оскільки більшість 2D-ігор використовують сітку плиток і тому, що це просто уявити. Однак зауважте, що область пошуку може бути розбита будь-яким способом: можливо, шістнадцятковою сіткою або навіть довільними формами, такими як ризик. Різні положення карти називаються "вузлами", і цей алгоритм буде працювати будь-який час, коли у вас буде купа вузлів для переходу та встановлення зв'язків між вузлами.

У будь-якому випадку, починаючи з заданої стартової плитки:

  • 8 плиток навколо стартової плитки "набираються" виходячи з а) вартості переходу від поточної плитки до наступної плитки (як правило, 1 для горизонтальних або вертикальних рухів, sqrt (2) для діагонального руху).

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

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

  • Цей процес повторюється, поки не буде досягнуто цільового вузла або не буде більше відкритих вузлів (тобто агент заблокований).

Діаграми, що ілюструють ці кроки, перегляньте цей посібник для початківців .

Є деякі вдосконалення, в основному в частині поліпшення евристики:

  • З урахуванням відмінностей місцевості, нерівності, крутості тощо.

  • Також іноді корисно робити «розгортку» по всій сітці, щоб заблокувати ділянки карти, які не є ефективними шляхами: наприклад, форма U, звернена до агента. Без тесту перевірки агент спершу входив би до U, перевертався, потім виходив і обходив край У. "Справжній" розумний агент відзначав би пастку у формі U та просто уникав цього. Підмітання може допомогти імітувати це.


1
Пояснення з графіком, вузлами, ребрами було б зрозуміліше, ніж лише про плитки. Це не допоможе зрозуміти, що незалежно від структури простору вашої гри, ви можете застосовувати той самий алгоритм, що стосується інформації про позиції, пов'язані між собою.
Клайм

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

14

Це далеко не найкраще, але це реалізація, яку я зробив A * в C ++ кілька років тому.

Напевно, краще, щоб я вказав вам на ресурси, ніж намагаюся пояснити весь алгоритм. Також, читаючи статтю wiki, пограйте з демонстрацією та побачите, чи можете ви уявити, як вона працює. Залиште коментар, якщо у вас є конкретні запитання.

  1. A * у Вікіпедії
  2. * Демонстрація Java

4
Ваш приклад Python знаходиться в C ++.
Булочки з алюмінію

@finish - Приємно бачити, що хтось це спіймає! Повсякденна діяльність обертається навколо Python в ці дні. Дякую!
Девід Макграу

3
Вашим прикладом C ++ також може бути C.
deceleratedcaviar

4
Цей приклад може бути в асемблері для всієї структури, яку він має. Це навіть не A *, як це прийнята відповідь?

4
Вибачте, це не на зразок, це була одна з моїх перших спроб кодування ще коли я почав. Не соромтесь щось додати до коментарів / відредагуйте публікацію, щоб поділитися власним рішенням.
Девід Макгре

6

Стаття ActiveTut про пошук шляху може бути корисною. Він перевершує як алгоритм A *, так і Дійкстра і відмінності між ними. Він орієнтований на розробників Flash, але він повинен дати гарне уявлення про теорію, навіть якщо ви не використовуєте Flash.


4

Одне, що важливо візуалізувати при роботі з A * та алгоритмом Дейкстри, - це те, що A * спрямований; вона намагається знайти найкоротший шлях до певної точки, "здогадуючись", в якому напрямку шукати. Алгоритм Дейкстри знаходить найкоротший шлях до / кожного / точки.


1
Це насправді не точний опис різниці між A * і Dijkstra. Це правда, що Dijkstra вирішує єдине джерело для всіх точок, але при використанні в іграх зазвичай відключається, як тільки ви знайдете шлях до мети. Справжня різниця між ними полягає в тому, що A * інформується евристично і може знайти цю мету з меншою кількістю гілок.

Щоб додати пояснення Джо: A * також знайде шлях до всіх точок, якщо ви дозволите це, але в іграх ми зазвичай хочемо зупинитися рано. A * працює як алгоритм Дійскри, за винятком того, що евристика допомагає впорядкувати вузли, щоб спочатку вивчити найбільш перспективні шляхи. Таким чином ви зазвичай можете зупинитися навіть раніше, ніж з алгоритмом Дікстри. Наприклад, якщо ви хочете знайти шлях від центру карти до східної сторони, алгоритм Дейкстри вивчить однаково в усіх напрямках і зупиниться, коли знайде східну сторону. A * витратить більше часу на схід, ніж на захід, і швидше потрапить туди.
amitp

3

Отже, як і перше твердження, A * лежить в основі алгоритму дослідження графіків. Зазвичай в іграх ми використовуємо як графік плитки чи іншу геометрію світу, але ви можете використовувати A * для інших речей. Два ур-алгоритми для переходу графіків - це глибина-перший-пошук та широта-перший-пошук. У DFS ви завжди повністю вивчаєте поточну гілку, перш ніж дивитися на братів та сестер поточного вузла, а в BFS ви завжди спочатку дивитесь на братів та сестер, а потім на дітей. * Намагається знайти середину між ними, де ви досліджуєте вниз гілку (так більше схоже на DFS), коли ви наближаєтесь до бажаної мети, але іноді зупиняєтесь і намагаєтеся побратимів, якщо це може мати кращі результати на її гілці. Фактична математика полягає в тому, що ви зберігаєте список можливих вузлів, щоб вивчити наступне, де кожен має "користь" оцінка, що вказує на те, наскільки близька (в якомусь абстрактному сенсі) до мети, нижчі показники кращі (0 означатиме, що ви знайшли мету). Ви вибираєте, що використовувати далі, знаходячи мінімальний бал плюс кількість вузлів від кореня (що, як правило, є поточною конфігурацією або поточною позицією в маршрутизації). Кожен раз, коли ви досліджуєте вузол, ви додаєте до цього списку всіх його дітей, а потім вибираєте новий найкращий.


3

На абстрактному рівні A * працює так:

  • Ви ставитесь до світу як до дискретної кількості підключених вузлів, наприклад. сітка або графік.
  • Щоб знайти шлях до цього світу, вам потрібно знайти список суміжних «вузлів» у цьому просторі, що веде від початку до мети.
  • Наївний підхід був би таким: обчисліть усі можливі перестановки вузлів, які починаються з пускового вузла і закінчуються в кінцевому вузлі, і виберіть найдешевший. Це, очевидно, займе назавжди на всіх, крім найдрібніших просторів.
  • Тому альтернативні підходи намагаються використати певні знання про світ, щоб здогадатися, які перестановки варто спочатку розглянути, а також знати, чи можна бити якесь рішення. Ця оцінка називається евристичною.
  • A * вимагає допустимої евристики . Це означає, що він ніколи не переоцінює.
    • Хорошим евристикою для задач проходження маршрутів є евклідова відстань, оскільки ми знаємо, що найкоротший шлях між двома точками - це пряма. Це ніколи не переоцінює відстань у реальних моделюваннях.
  • A * починається з пускового вузла і намагається послідовно перестановити цей вузол плюс кожного сусіда та його сусіда тощо, використовуючи евристичний, щоб вирішити, яку перестановку спробувати далі.
  • На кожному кроці A * розглядає найбільш перспективний шлях до цих пір і вибирає наступний сусідній вузол, який видається "найкращим", виходячи з пройденої на даний момент відстані, та евристичної оцінки того, як далеко залишилося б піти від цього вузол.
  • Оскільки евристика ніколи не завищує, а пройдена поки що відстань, як відомо, є точною, вона завжди обере найбільш оптимістичний наступний крок.
    • Якщо наступний крок досягне мети, ви знаєте, що він знайшов найкоротший шлях з останньої позиції, оскільки це був найоптимістичніший здогад із дійсних.
    • Якщо вона не досягла мети, вона залишається можливою точкою для вивчення згодом. Тепер алгоритм вибирає наступну найбільш перспективну можливість, тому логіка вище все ще діє.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.