Предварительно вычисленный поиск пути все еще актуален?

12

контекст

Old Lucas Arts (эпоха ScummVM) указывает и щелкает графические приключенческие игры, использующие предварительно вычисленные пути. Вот примерный план техники.

Шаг 1

Пол в каждой комнате был разделен на то, что они называли «прогулочными коробками», которые были в значительной степени эквивалентны узлам в навигационной сетке, но ограничены трапециевидными формами. Например:

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

Шаг 2

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

      ___ ___ ___ ___
     | 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). Поскольку short обычно бывает достаточно большим, чтобы хранить каждую запись в матрице, со сложной картой из 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
Я думаю, что это все еще актуально, если у вас ограниченный бюджет процессора. Но если вам нужны динамические пути, это уже бесполезно. Кстати, я посмотрел на то, откуда вы взяли свой алгоритм A *, и вы можете оптимизировать его еще больше, используя minheap и некоторые другие приемы. Я сделал несколько итераций по улучшению A * в C #, которые вы можете увидеть здесь: roy-t.nl/index.php/2011/09/24/… может быть полезным.
Рой Т.
1
Спасибо, я добавил это в закладки и посмотрю, когда начну оптимизировать свое приложение. Я в значительной степени использовал решение Эрика Липперта с несколькими незначительными изменениями, потому что оно было настолько чистым и легким для отслеживания ... И для всех моих тестовых случаев оно работало в значительной степени "мгновенно", поэтому я даже не удосужился его оптимизировать.
Дэвид Гувейя
1
Кстати, если вы решите продолжить предварительное вычисление, вы можете посмотреть на алгоритм Флойда-Варшалла . Он строит матрицу «следующего шага» более эффективно, чем многократно используя Dijkstra / A *.
amitp
@amitp Спасибо за совет, всегда полезно знать об этих альтернативах! Хотя, поскольку в большинстве случаев предварительные вычисления будут выполняться в автономном режиме, повышение эффективности не принесет особой выгоды. Если вы не очень нетерпеливы. :-)
Дэвид Гувея
Согласен, хотя Floyd-Warshall также гораздо проще реализовать, чем алгоритм Дейкстры, поэтому, если у вас еще нет реализованного Дейкстры, стоит посмотреть :)
amitp

Ответы:

3

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

Итог, эта техника все еще актуальна в настоящее время в любом сценарии?

Я не вижу никакой выгоды от использования такой техники.

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

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

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

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

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

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

Кроме того, в качестве примечания, я занимался программированием GBA просто для удовольствия, и ничто не переходит на использование современного устройства. Для GBA все сводилось к минимизации нагрузки на процессор (потому что это было жалко). Вы также должны понимать, что большинство языков высокого уровня C # и Java (не так много C ++ или C) делают для вас тонны оптимизаций. Что касается оптимизации вашего кода, их не так много, кроме как доступ к памяти как можно меньше, и когда вы выполняете на нем столько вычислений, сколько возможно, прежде чем вводить новую память, которая вытеснит ее из кэша, и убедитесь, что вы работаете с ней. делаю только один раз.

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

ClassicThunder
источник
Что касается вашего редактирования, я на самом деле не говорил о предварительных вычислениях часто используемых путей, а строго о технике, описанной для предварительного вычисления каждого возможного пути . Я также немного озадачен тем, что большая часть вашего ответа была против использования предварительно вычисленного поиска пути, но потом вы сказали, что это будет отличная идея. Итак, будет ли это полезно в условиях ограниченного использования ЦП, таких как GBA?
Дэвид Гувея
1
Мое плохое, я пытался указать, что ответ на ваш заголовок, вырванный из контекста, - да. Пока ответа, касающегося конкретного алгоритма, описанного в вашем вопросе, нет. Таким образом, короткое предварительное вычисление всех возможных путей - плохая идея, но предварительное вычисление нескольких очень часто используемых путей может быть хорошей идеей.
ClassicThunder
2
@ClassicThunder: этот метод предварительного вычисления всех путей из нескольких ориентиров часто называют ALT : звезда с ориентирами и неравенство треугольников : cs.princeton.edu/courses/archive/spr06/cos423/Handouts/GW05. pdf
Питер Гиркенс