Алгоритм D* — различия между версиями
Kabanov (обсуждение | вклад) |
Kabanov (обсуждение | вклад) |
||
| Строка 11: | Строка 11: | ||
Аналогично множество <tex>Pred(s) \in V</tex> как множество вершин, входящих в вершину <tex>s</tex>. | Аналогично множество <tex>Pred(s) \in V</tex> как множество вершин, входящих в вершину <tex>s</tex>. | ||
| − | Функция <tex>0 \ | + | Функция <tex>0 \leqslant с(s, s') \leqslant +\infty</tex> будет возвращать стоимость перехода из вершины <tex>s</tex> в вершину <tex>s'</tex>. При этом <tex>s' \in Succ(s)</tex>. |
Функция <tex>g(s)</tex> будет возвращать последнее известное (и самое минимальное) значение расстояния от вершины <tex>s_{start}</tex> до <tex>s</tex>. | Функция <tex>g(s)</tex> будет возвращать последнее известное (и самое минимальное) значение расстояния от вершины <tex>s_{start}</tex> до <tex>s</tex>. | ||
| Строка 18: | Строка 18: | ||
:<tex dpi="120">rhs(s) = 0</tex> | :<tex dpi="120">rhs(s) = 0</tex> | ||
Иначе | Иначе | ||
| − | :<tex dpi="120">rhs(s) = min_{s' \in | + | :<tex dpi="120">rhs(s) = min_{s' \in Pred(s)}(g(s') + c(s', s)</tex> |
Вершина <tex>s</tex> может быть 3-х видов: | Вершина <tex>s</tex> может быть 3-х видов: | ||
| Строка 96: | Строка 96: | ||
Таким образом мы описали алгоритм LPA*. Он неоднократно определяет путь между вершинами <tex>s_{start}</tex> и <tex>s_{goal}</tex>, используя при этом данные из предыдущих итераций. Очевидно, что в худшем случае (а именно когда все ребра вокруг текущей вершины изменили свой вес) алгоритм будет работать как последовательные вызовы алгоритма А* за <tex>O(n^2 \dot log(n))</tex>. Улучшим эту оценку с помощью алгоритма D* lite. | Таким образом мы описали алгоритм LPA*. Он неоднократно определяет путь между вершинами <tex>s_{start}</tex> и <tex>s_{goal}</tex>, используя при этом данные из предыдущих итераций. Очевидно, что в худшем случае (а именно когда все ребра вокруг текущей вершины изменили свой вес) алгоритм будет работать как последовательные вызовы алгоритма А* за <tex>O(n^2 \dot log(n))</tex>. Улучшим эту оценку с помощью алгоритма D* lite. | ||
| − | '''Примечание''': на практике же такой подход тоже имеет место на плотных графах (или матрицах), так как в среднем дает оценку О(n | + | '''Примечание''': на практике же такой подход тоже имеет место на плотных графах (или матрицах), так как в среднем дает оценку О(n \dot log(n)). |
== Алгоритм D* == | == Алгоритм D* == | ||
| + | Пока что был описан только алгоритм LPA*. Он способен неоднократно определять кратчайшее расстояние между начальной и конечной вершинами при любом изменении данного графа. Его первоначальный поиск полностью совпадает с алгоритмом A*, но последующие итерации способны использовать информацию из предыдущих поисков. | ||
| + | |||
| + | === Постановка задачи === | ||
| + | Теперь на основе LPA* опишем алгоритм D*, который способен определять расстояние между текущей вершиной <tex>s_{start}</tex>, в которой, допустим, находится курсор/робот, и конечной вершиной <tex>s_{goal}</tex> при каждом изменении графа в то время, как наш робот движется вдоль найденного пути. | ||
| + | |||
| + | === Описание === | ||
| + | Опишем первую версию алгоритма D*. Очевидно, что большинство вершин в процессе движения робота остаются неизменными, поэтому мы можем применить алгоритм LPA*. | ||
| + | |||
| + | '''Примечание''': Большинство функций переходят в данный алгоритм без изменений, поэтому опишем только измененные части. | ||
| + | |||
| + | Для начала мы поменяем направление поиска в графе. | ||
| + | |||
| + | Теперь функция g(s) хранит минимальное известное расстояние от <tex>s_{goal}</tex> до <tex>s</tex>. Свойства остаются прежними. | ||
| + | |||
| + | Эвристическая функция h(s,s') теперь должна быть неотрицательная и обратно-устойчивая, т.е. | ||
| + | <tex>h(s_{start},s_{start}) = 0</tex> и <tex>h(s_{start}, s) <= h(s_{start},s') + c(s',s)</tex> для всех <tex>s \in S</tex> и <tex>s' \in Pred(s)</tex>. Очевидно, что при движении робота <tex>s_{start}</tex> изменяется, поэтому данные свойства должны выполняться для всех <tex>s_{start} \in S</tex>. | ||
| + | |||
| + | Дополнительное условие выхода также меняется, т.е. при <tex>g(s_{start}) = +\infty</tex> путь не найден на данной итерации. Иначе путь найден и робот может проследовать по нему. | ||
=== Псевдокод (Первая версия) === | === Псевдокод (Первая версия) === | ||
| + | При такой постановке задачи псевдокод не сильно меняется. Но функция '''Main''' все-таки претерпевает значительные изменения. | ||
| + | |||
'''CalcKey'''(s): | '''CalcKey'''(s): | ||
return [<tex>\min(g(s);rhs(s)) + h(s_{start};s)</tex>;<tex>\min(g(s); rhs(s))</tex>]; | return [<tex>\min(g(s);rhs(s)) + h(s_{start};s)</tex>;<tex>\min(g(s); rhs(s))</tex>]; | ||
| Строка 112: | Строка 132: | ||
'''UpdateVertex'''(u): | '''UpdateVertex'''(u): | ||
| − | if (<tex>u | + | if (<tex>u \ne s_{goal}</tex>) |
rhs(u) = min_{s' \in Succ(u)}(c(u,s')+g(s')); | rhs(u) = min_{s' \in Succ(u)}(c(u,s')+g(s')); | ||
if (<tex>u \in U</tex>) | if (<tex>u \in U</tex>) | ||
U.Remove(u); | U.Remove(u); | ||
| − | if (<tex>g(u) | + | if (<tex>g(u) \ne rhs(u)</tex>) |
U.Insert(u; CalcKey(u)); | U.Insert(u; CalcKey(u)); | ||
| Строка 138: | Строка 158: | ||
<tex>s_{start}</tex> = такая вершина s', что <tex>min_{s' \in Succ(s_{start})}(c(s_{start}, s') + g(s'))</tex> | <tex>s_{start}</tex> = такая вершина s', что <tex>min_{s' \in Succ(s_{start})}(c(s_{start}, s') + g(s'))</tex> | ||
Передвинулись вдоль найденного пути и изменили вершину <tex>s_{start}</tex>; | Передвинулись вдоль найденного пути и изменили вершину <tex>s_{start}</tex>; | ||
| − | + | Сканируем роботом какие-либо изменения в графе или убеждаемся, что граф остается прежним. | |
if (если граф изменился) | if (если граф изменился) | ||
for всех ориентированных ребер <tex>(u; v)</tex> с измененными весами: | for всех ориентированных ребер <tex>(u; v)</tex> с измененными весами: | ||
Версия 00:42, 3 января 2014
Алгоритм D* — алгоритм поиска кратчайшего пути во взвешенном ориентированном графе, где структура графа неизвестна заранее или постоянно подвергается изменению. Разработан Свеном Кёнигом и Максимом Лихачевым в 2002 году.
Содержание
Алгоритм LPA*
Постановка задачи
Дан взвешенный ориентированный граф . Даны вершины и . Требуется после каждого изменения графа уметь вычислять функцию для каждой известной вершины
Описание
Обозначим множество как множество вершин, исходящих из вершины .
Аналогично множество как множество вершин, входящих в вершину .
Функция будет возвращать стоимость перехода из вершины в вершину . При этом .
Функция будет возвращать последнее известное (и самое минимальное) значение расстояния от вершины до .
Если
Иначе
Вершина может быть 3-х видов:
- насыщена, если
- переполнена, если
- ненасыщена, если
Очевидно, что если все вершины насыщены, то мы можем найти расстояние от стартовой вершины до любой.
Функция , где - вершина, возвращает вектор из 2-ух значений , .
- .
- .
Если в конце поиска пути , то мы не смогли найти путь от до на текущей итерации. Но после следующего изменения графа путь вполне может найтись.
Псевдокод
Основная функция, описывающая алгоритм
Main():
{
Initialize();
while (true)
{
ComputeShortestPath();
В данный момент мы знаем кратчайший путь из в .
Ждем каких-либо изменений графа.
for всех ориентированных ребер с измененными весами:
{
Обновляем результат функции ;
UpdateVertex();
}
}
}
Теперь опишем составные элементы подробнее
Initialize():
{
//Заведем приоритетную очередь , в которую будем помещать вершины. Сортировка будет производиться по функции .
for
U.Insert(; CalcKey());
}
Функция . Возвращаемые значения сортируются в лексографическом порядке, т.е. сначала , потом CalcKey(s): { return [; ]; }
UpdateVertex(): { if () if () U.Remove(u); if () U.Insert(; CalcKey()); }
ComputeShortestPath():
{
while (U.TopKey() < CalcKey() OR rhs())
u = U.Pop();
if (g(u) > rhs(u))
g(u) = rhs(u);
for
UpdateVertex(s);
else
g(u) = ;
for
UpdateVertex(s);
}
Таким образом мы описали алгоритм LPA*. Он неоднократно определяет путь между вершинами и , используя при этом данные из предыдущих итераций. Очевидно, что в худшем случае (а именно когда все ребра вокруг текущей вершины изменили свой вес) алгоритм будет работать как последовательные вызовы алгоритма А* за . Улучшим эту оценку с помощью алгоритма D* lite.
Примечание: на практике же такой подход тоже имеет место на плотных графах (или матрицах), так как в среднем дает оценку О(n \dot log(n)).
Алгоритм D*
Пока что был описан только алгоритм LPA*. Он способен неоднократно определять кратчайшее расстояние между начальной и конечной вершинами при любом изменении данного графа. Его первоначальный поиск полностью совпадает с алгоритмом A*, но последующие итерации способны использовать информацию из предыдущих поисков.
Постановка задачи
Теперь на основе LPA* опишем алгоритм D*, который способен определять расстояние между текущей вершиной , в которой, допустим, находится курсор/робот, и конечной вершиной при каждом изменении графа в то время, как наш робот движется вдоль найденного пути.
Описание
Опишем первую версию алгоритма D*. Очевидно, что большинство вершин в процессе движения робота остаются неизменными, поэтому мы можем применить алгоритм LPA*.
Примечание: Большинство функций переходят в данный алгоритм без изменений, поэтому опишем только измененные части.
Для начала мы поменяем направление поиска в графе.
Теперь функция g(s) хранит минимальное известное расстояние от до . Свойства остаются прежними.
Эвристическая функция h(s,s') теперь должна быть неотрицательная и обратно-устойчивая, т.е. и для всех и . Очевидно, что при движении робота изменяется, поэтому данные свойства должны выполняться для всех .
Дополнительное условие выхода также меняется, т.е. при путь не найден на данной итерации. Иначе путь найден и робот может проследовать по нему.
Псевдокод (Первая версия)
При такой постановке задачи псевдокод не сильно меняется. Но функция Main все-таки претерпевает значительные изменения.
CalcKey(s): return [;];
Initialize(): U = ; for U.Insert(; CalcKey());
UpdateVertex(u): if () rhs(u) = min_{s' \in Succ(u)}(c(u,s')+g(s')); if () U.Remove(u); if () U.Insert(u; CalcKey(u));
ComputeShortestPath(): while (U.TopKey() < CalcKey() OR ) u = U.Pop(); if (g(u) > rhs(u)) g(u) = rhs(u); for UpdateVertex(s); else g(u) = ; for UpdateVertex(s);
Main(): Initialize(); ComputeShortestPath(); while () // if () тогда путь на данной итерации не найден. = такая вершина s', что Передвинулись вдоль найденного пути и изменили вершину ; Сканируем роботом какие-либо изменения в графе или убеждаемся, что граф остается прежним. if (если граф изменился) for всех ориентированных ребер с измененными весами: Обновляем результат функции ; UpdateVertex(u); for U.Update(; CalcKey()); ComputeShortestPath();