Чи попередньо обчислена налагодження маршруту все ще актуальна?


12

Контекст

Old Lucas Arts (епоха ScummVM) вказує та натискає графічні пригодницькі ігри, що використовуються заздалегідь обчисленими маршрутами. Ось приблизний контур методики.

Крок 1

Підлогу в кожній кімнаті було розділено на те, що вони називали "прогулянкові коробки", які були майже еквівалентними вузлам у навігаційній сітці, але обмежені трапецієподібними формами. Наприклад:

 ______ _____ _________ _____
\   A  |  B  |    C    |  D   \
 \_____|     |         |_______\
       |_____|         |
             |_________|

Крок 2

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

      ___ ___ ___ ___
     | A | B | C | D | <- Start Node
  ___|___|___|___|___|
 | A | A | A | B | C |  ---
 |___|___|___|___|___|     |
 | B | B | B | B | C |     |
 |___|___|___|___|___|     |-- Next node in shortest path
 | C | B | C | C | C |     |   from Start to End
 |___|___|___|___|___|     | 
 | D | B | C | D | D |  ---
 |___|___|___|___|___| 
   ^
   |
End Node

Як ви можете здогадатися, вимоги до пам'яті швидко зростають із збільшенням кількості вузлів (N ^ 2). Оскільки короткий звичайно буде достатньо великим для зберігання кожного запису в матриці, зі складною картою з 300 вузлів, що призведе до збереження зайвих:

300^2 * sizeof(short) = 176 kilobytes

Крок 3

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

// Find shortest path from Start to End
Path = {Start}
Current = Start
WHILE Current != End
    Current = LookUp[Current, End]
    Path.Add(Current)
ENDWHILE

Застосувавши цей простий алгоритм, щоб знайти найкоротший шлях повернення від C до A:

1) Path = { C }, Current = C
2) Path = { C, B }, Current = B
3) Path = { C, B, A }, Current = A, Exit

Питання

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

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

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

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

Підсумок, чи ця методика досі актуальна в будь-якому сценарії?


2
Я думаю, що це все ще актуально, якщо ви обмежуєте бюджет CPU. Але як тільки ви хочете динамічних шляхів, це просто більше не корисно. До речі, я подивився, звідки ви взяли свій алгоритм A *, і ви можете ще більше оптимізувати його за допомогою minheap та деяких інших хитрощів. Я зробив кілька ітерацій вдосконалення A * в C #, які ви можете побачити тут: roy-t.nl/index.php/2011/09/24/… може бути корисним.
Рой Т.

1
Дякую, я зробив закладки для цього і буду вивчати це, коли я почну оптимізувати свою програму. Я в значній мірі використовував рішення Еріка Ліпперта з кількома незначними модифікаціями, оскільки він був таким чистим і легким для дотримання ... І для всіх моїх тестових випадків він працював досить швидко "миттєво", тому я навіть не заважав оптимізувати його.
Девід Гувейя

1
BTW, якщо ви вирішили проводити попередні обчислення, ви можете подивитися алгоритм Floyd-Warshall . Він будує матрицю "Наступний крок" ефективніше, ніж багаторазово, використовуючи Dijkstra / A *.
amitp

@amitp Дякую за пораду, про ці альтернативи завжди добре знати! Хоча, оскільки в більшості випадків попереднє обчислення здійснюватиметься в режимі офлайн, не буде багато корисного, щоб зробити його більш ефективним. Якщо ви справді нетерплячі. :-)
Девід Гувейя

Погоджено, хоча Floyd-Warshall також набагато простіший у виконанні, ніж алгоритм Dijkstra, тому, якщо у вас ще не було реалізовано Dijkstra, варто подивитися :)
amitp

Відповіді:


3

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

Підсумок, чи ця методика досі актуальна в будь-якому сценарії?

Я не бачу користі від використання такої методики.

Мені не вистачає гнучкості Графіка (у вас можуть бути різні КЗП, вони не повинні бути певної форми, тощо ...). Також будь-який користувач вашого двигуна дізнається, що таке графік і як ним користуватися. Тож якщо вони хочуть додати додаткову функціональність, їм доведеться навчитися реалізовувати їх розширення, використовуючи абсолютно новітню для них ситуацію.

Як ви згадали, схоже, це могло б жахливо масштабуватися. Також варто зауважити, що якщо графік відповідає грошовим коштам, і ви запускаєте всі свої результати шляху назад, то він дійсно скорочується на час IO. Схоже, незабаром ваша реалізація стане надто великою, щоб вміститись у будь-який кеш.

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

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

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

Також зрозумійте, що багато ігор мають окремі петлі для оновлення AI. Я вважаю, що спосіб мого проекту налаштований на те, що існує цикл оновлення для введення користувачем при 60 Гц, AI - лише 20 Гц, а ігри малюються якнайшвидше.

Також в якості побічної записки я зробив кілька програм програмування GBA просто для розваги і зовсім нічого не переходить на використання сучасного пристрою. Для GBA все полягало в тому, щоб мінімізувати навантаження процесора (бо це було жалко). Ви також повинні усвідомити, що більшість мов високого рівня C # та Java (не стільки C ++ чи C) роблять для вас безліч оптимізацій. Що стосується оптимізації коду, то їх не багато іншого, крім якнайменшого доступу до пам'яті, і коли ви виконаєте якомога більше обчислень на ній, перш ніж ввести нову пам'ять, яка виведе її з кеша і переконається, що ви є робив речі лише один раз.

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


Щодо вашої редагування, я насправді не говорив про попередньо обчислювані часто використовувані шляхи, а строго про техніку, окреслену попередньою обчислюваністю кожного можливого шляху . Я також трохи розгублений з приводу того, як більшість ваших відповідей було проти використання попередньо обчислених шляхів пошуку, але потім, наприкінці ви сказали, що це буде чудовою ідеєю. Отже, чи було б корисним середовище з обмеженим процесором, наприклад, GBA?
Девід Гувейя

1
Моє погано, я намагався вказати, що відповідь на ваш заголовок, винесений із контексту, - так. Хоча відповідь, що стосується конкретного алгоритму, описаного у вашому запитанні, - ні. Отже, коротко попередньо обчислюючи всі можливі шляхи - це погана ідея, але попереднє обчислення кількох дуже часто використовуваних контурів може бути хорошою ідеєю.
ClassicThunder

2
@ClassicThunder: Ця методика попереднього обчислення всіх шляхів від кількох орієнтирів часто називається ALT : зірка зі знаками
Pieter Geerkens
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.