Изменения

Перейти к: навигация, поиск

Алгоритм D*

1518 байт добавлено, 17:54, 16 сентября 2015
м
Ссылки
* Обозначим множество <tex>Pred(s) \subseteq V</tex> как множество вершин, входящих в вершину <tex>s</tex>.
Функция <tex>0 \leqslant c(s, s') \leqslant +\infty</tex> будет возвращать стоимость перехода из вершины ребра <tex>(s, s')</tex> в вершину . При этом <tex>c(s, s') = +\infty</tex>. При этом будет тогда и только тогда, когда ребра <tex>(s, s' \in Succ(s)</tex>не существует.
{{Определение
|definition=Будем называть '''rhs-значением''' (англ. ''right-hand side value'') такую функцию <tex>rhs(s)</tex>, которая будет возвращать потенциальное минимальное расстояние от <tex>f</tex> до <tex>s</tex> по следующим правилам:
<tex>rhs(s) =
\begin{cases}
{{Определение
|definition=Вершина <tex>s</tex> называется '''насыщенной''' (англ. ''locally consistent''), если <tex>g(s) = rhs(s)</tex>
}}
{{Определение
|definition=Вершина <tex>s</tex> называется '''переполненной''' (англ. ''locally overconsistent''), если <tex>g(s) > rhs(s)</tex>
}}
{{Определение
|definition=Вершина <tex>s</tex> называется '''ненасыщенной''' (англ. ''locally underconsistent''), если <tex>g(s) < rhs(s)</tex>
}}
Основная функция, описывающая алгоритм
'''Mainfunction'''main(): '''Initialize'''initialize(); '''while (true) '''ComputeShortestPath''true'' computeShortestPath(); <font color="green">//В данный момент мы знаем кратчайший путь из f в t.</font>
Ждем каких-либо изменений графа.
'''for ''' всех ориентированных ребер (u; , v) с измененными весами: Обновляем результат функции c(u; , v); '''UpdateVertex'''updateVertex(v);
Теперь опишем составные элементы подробнееФункция инициализации исходного графа устанавливает для всех вершин кроме стартовой (вершины <tex>f</tex>) значения <tex>g(s)</tex> и <tex>rhs(s)</tex> равными бесконечности. Для стартовой <tex>rhs(f)=0</tex>. Очевидно, что минимальное расстояние от стартовой вершины до самой себя должно быть равным 0, но <tex>g(f)=+\infty</tex>. Это сделано для того, чтобы стартовая вершина была ненасыщенной и имела право попасть в приоритетную очередь. '''Initializefunction'''initialize(): <font color="green">// Заведем [[Двоичная куча|приоритетную очередь]] U, в которую будем помещать вершины. </font> <font color="green">// Сортировка будет производиться по функции key(s).</font> U = <tex>\varnothing</tex>; '''for ''' s <tex>\in</tex> V rhs(s) = g(s) = <tex>+\infty;</tex> rhs(f) = 0; U.Insertinsert(f; CalcKey, calcKey(f));
//Функция <tex>key(s)</tex>. Возвращаемые значения сортируются в лексографическом порядке, // т.е. то есть сначала сортируется по <tex>k_1(s)</tex>, потом по <tex>k_2(s)</tex> '''CalcKeyfunction'''calcKey(s): '''return ''' [min(g(s); , rhs(s)) + h(s; , t); , min(g(s); , rhs(s))];
// Обновляет данные вершины в соответствие с данными выше определениями. // Также поддерживает инвариант того, что в очереди U лежат только ненасыщенные вершины. '''UpdateVertexfunction'''updateVertex(u): '''if ''' u <tex>\ne</tex> f <tex>rhs(u) = \min\limits_{s' \in Pred(u)}(g(s') + c(s',u));</tex> '''if ''' u <tex>\in</tex> U U.Removeremove(u); '''if ''' g(u) <tex>\ne</tex> rhs(u) U.Insertinsert(u; CalcKey, calcKey(u));
// Функция несколько раз перерасчитывает значение <tex>g(s)</tex> у ненасыщенных вершин в неубывающем порядке их ключей. // Такой перерасчет значения <tex>g(s)</tex> будем называть ''расширением'' вершины. '''ComputeShortestPathfunction'''computeShortestPath(): '''while (''' U.TopKeytopKey() < CalcKeycalcKey(t) OR '''or''' rhs(t) <tex>\ne</tex> g(t)) u = U.Poppop(); '''if ''' g(u) > rhs(u) g(u) = rhs(u); '''for ''' <tex>s </tex> <tex>\in</tex> Succ(u) UpdateVertex(s); '''else''' g(u) = <tex>+\infty</tex>; '''for ''' s <tex>\in</tex> Succ(u) <tex>\cup</tex> <tex>\{u\}</tex> UpdateVertexupdateVertex(s);
=== Асимптотика ===
{{Теорема
|statement=После выполнения функции '''ComputeShortestPath''' можно проследить восстановить путь из <tex>f</tex> в <tex>t</tex>. Для этого, начиная с вершины <tex>t</tex>, нужно постоянно передвигаться к такой вершине <tex>s'</tex>, входящей в <tex>t</tex>, чтобы <tex>g(s') + c(s',s)</tex> было минимальным, до тех пора, пока не будет достигнута вершина <tex>f</tex>.
|proof=Доказательство [http://www.cs.cmu.edu/~maxim/files/aij04.pdf]
}}
Таким образом мы описали алгоритм LPA*. Он неоднократно определяет путь вычисляет длину кратчайшего пути между вершинами <tex>f</tex> и <tex>t</tex>, используя при этом данные из предыдущих итераций. Очевидно, что в худшем случае (а именно когда все ребра вокруг текущей вершины изменили свой вес) алгоритм будет работать как последовательные вызовы алгоритма А* за <tex>O(n \cdot m \cdot \log(n))</tex>. Улучшим эту оценку с помощью алгоритма D* lite.
'''Примечание''': на практике же такой подход тоже имеет место на плотных графах (или матрицах), так как в среднем дает оценку <tex>O(n \cdot \log(n))</tex>.
== Алгоритм D* (Первая версия) ==
Пока что был описан только алгоритм LPA*. Он способен неоднократно определять находит кратчайшее расстояние между начальной и конечной вершинами при любом изменении данного графа. Его первоначальный поиск полностью совпадает с алгоритмом A*, но последующие итерации способны использовать информацию из предыдущих поисков.
=== Постановка задачи ===
Теперь на основе LPA* опишем алгоритм D*, который способен определять расстояние между текущей вершиной <tex>f</tex>, в которой, допустим, находится способный к сканированию местности "робот", и конечной вершиной <tex>t</tex> при каждом изменении графа в то время, как наш "робот" движется вдоль найденного пути.
[[Файл:Схема_движения_робота_Dstar.png|350px|thumb|right|Схема движения "робота" в процессе работы алгоритма D*. Информация о серых клетках ему неизвестна до определенной итерациитех пор, пока они не попадут в его зону обзора. В данном примере зона обзора составляет 1 клетку в 8-ми направлениях.]]
=== Описание ===
При такой постановке задачи псевдокод не сильно меняется, но функция '''Main''' все-таки претерпевает значительные изменения.
'''CalcKeyfunction'''calcKey(s): '''return ''' [min(g(s),rhs(s)) + h(f,s); , min(g(s),rhs(s))];
'''Initializefunction'''initialize(): U = <tex>\varnothing</tex>; '''for ''' s <tex>\in</tex> V
rhs(s) = g(s) = <tex>+\infty</tex>
rhs(t) = 0
U.Insertinsert(t; CalcKey, calcKey(t));
'''UpdateVertexfunction'''UpdateVertex(u): '''if ''' u <tex>\ne</tex> t rhs(u) = <tex>\min\limits_{s' \in Succ(u)}(c(u,s')+g(s'));</tex> '''if ''' u <tex>\in</tex> U U.Removeremove(u); '''if ''' g(u) <tex>\ne</tex> rhs(u) U.Insertinsert(u; CalcKey, calcKey(u));
'''ComputeShortestPathfunction'''ComputeShortestPath(): '''while (''' U.TopKeytopKey() < CalcKeycalcKey(f) OR or rhs(f) <tex>\ne</tex> g(f)) u = U.Poppop(); '''if ''' (g(u) > rhs(u)) g(u) = rhs(u); '''for ''' s <tex>\in</tex> Pred(u) UpdateVertexupdateVertex(s); '''else''' g(u) = <tex>+\infty</tex>; '''for ''' <tex>s </tex> <tex>\in</tex> Pred(u) <tex>\cup</tex> {u} UpdateVertexupdateVertex(s);
'''Mainfunction'''main(): '''Initialize'''initialize() computeShortestPath(); '''ComputeShortestPathwhile'''(); while (<tex>f \ne t</tex>) <font color="green">// '''if ''' (g(f) = <tex>+\infty</tex>) тогда путь на данной итерации не найден.</font>
<tex>f</tex> = такая вершина s', что <tex>\min\limits_{s' \in Succ(f)}(c(f, s') + g(s'))</tex>
Передвинулись вдоль найденного пути и изменили вершину <tex>f</tex>;
Сканируем роботом какие-либо изменения в графе или убеждаемся, что граф остается прежним.
'''if (''' граф изменился) '''for ''' всех ориентированных ребер <tex>(u; , v) </tex> с измененными весами: Обновляем результат функции <tex>c(u; , v);</tex> updateVertex(u) '''UpdateVertexfor'''(u); for <tex>s </tex> <tex>\in</tex> U U.Updateupdate(s; ''', CalcKey'''(s)); '''ComputeShortestPath'''computeShortestPath();
=== Асимптотика ===
Теперь эвристическая функция должна поддерживать неравенство треугольника для всех вершин <tex>s,s',s'' \in V</tex>, т.е. <tex>h(s,s'') \leqslant h(s, s') + h(s',s'')</tex>. Так же должно выполняться свойство <tex>h(s,s') \leqslant c^*(s,s')</tex>, где <tex>c^*(s,s')</tex> - стоимость перехода по кратчайшему пути из <tex>s</tex> в <tex>s'</tex>, при этом <tex>s</tex> и <tex>s'</tex> не должны быть обязательно смежными. Такие свойства не противоречат свойствами из первой версии, а лишь усиливают их.
Допустим, что после того, как робот продвинется вдоль найденного пути на предыдущих итерациях, из вершины <tex>s </tex> в <tex>s'</tex>, он обнаружит изменения в графе. Первая компонента ключей <tex>k_1(s')</tex> может уменьшится максимум на <tex>h(s,s')</tex> (по определению ключа). Вторая компонента не зависит от функции h. Аналогично первой версии алгоритма, мы должны уменьшить первую компоненту ключа у всех вершин в очереди U. Очевидно, что <tex>h(s,s')</tex> будет одинаковым для всех вершин из U. Порядок в очереди не изменится, если произвести уменьшение. Следовательно уменьшение можно отложить, тем самым очередь не придется перестраивать на каждой итерации. Так же исходя из нового определения функции <tex>h</tex>, её значение будет всегда меньше, чем разность первых компонент ключей у соседних по приоритету вершин. Таким образом мы можем добавлять h(s,s') ко всем <tex>k_1(s')</tex> у ключей вершин из U.
Будем называть <tex>K_m</tex> ключевым модификатором. В нем мы и будет хранить сумму <tex>h(s,s')</tex>, которые нужно добавить ко всем вершинам из U.
=== Псевдокод ===
'''CalcKeyfunction'''calcKey(s): '''return ''' [min(g(s);, rhs(s)) + h(f;, s) + <tex>K_m</tex>;, min(g(s); , rhs(s))];
'''Initializefunction'''initialize(): U = <tex>\varnothing</tex>;
<tex>K_m = 0</tex>
'''for ''' s <tex>\in</tex> V
rhs(s) = g(s) = <tex>+\infty</tex>
rhs(t) = 0
U.Insertinsert(t; , CalcKey(t));
'''UpdateVertexfunction'''updateVertex(u): '''if (''' u <tex>\ne</tex> t) rhs(u) = <tex>\min\limits_{s' \in Succ(u)}(c(u,s')+g(s'));</tex> '''if (''' u <tex>\in</tex> U) U.Removeremove(u); '''if (''' g(u) <tex>\ne</tex> rhs(u)) U.Insertinsert(u; CalcKey, calcKey(u));
'''ComputeShortestPathfunction'''computeShortestPath(): '''while (''' U.TopKeytopKey() < CalcKeycalcKey(f) OR '''or''' rhs(f) <tex>\ne</tex> g(f)) <tex>K_{old}</tex> = U.TopKeytopKey(); u = U.Poppop();
'''if (''' <tex>K_{old}</tex> < '''CalcKey'''calcKey(u)) U.Insertinsert(u; '''CalcKey''', calcKey(u));
'''if ''' (g(u) > rhs(u)) g(u) = rhs(u); '''for ''' <tex>s </tex> <tex>\in</tex> Pred(u) UpdateVertexupdateVertex(s); '''else''' g(u) = <tex>+\infty</tex>; '''for ''' <tex>s </tex> <tex>\in</tex> Pred(u) <tex>\cup</tex> <tex>\{u\} </tex> '''UpdateVertex'''updateVertex(s);
'''Mainfunction'''main():
<tex>s_{last} = f</tex>
'''Initialize'''initialize() computeShortestPath(); '''ComputeShortestPathwhile'''(); while (f <tex>\ne</tex> t) <font color="green">// if (g(f) = <tex>+\infty</tex>) тогда путь на данной итерации не найден.</font>
<tex>f</tex> = такая вершина s', что <tex>\min\limits_{s' \in Succ(f)}(c(f, s') + g(s'))</tex>
Передвинулись вдоль найденного пути и изменили вершину <tex>f</tex>;.
Сканируем роботом какие-либо изменения в графе или убеждаемся, что граф остается прежним.
'''if (''' граф изменился) <tex>K_m = K_m + h(s_{last}, h_{start})</tex>; <tex>s_{last} = f</tex>; '''for ''' всех ориентированных ребер (u; , v) с измененными весами: Обновляем результат функции c(u; , v); '''UpdateVertex'''updateVertex(u); '''ComputeShortestPath'''computeShortestPath() === Асимптотика === С помощью введения ключевого модификатора <tex>K_m</tex> и отложенного обновления ключей вершин получилось убрать из итерации алгоритма <tex>O(n \cdot \log(n))</tex> операций, которые тратились на обновление очереди <tex>U</tex>. Очевидно, что на основе теорем, приведенных выше, алгоритм использовал <tex>O(2 \cdot n \cdot \log(n);)</tex> операций. Итак, нам удалось уменьшить константу в 2 раза, что дает существенный рост производительности на практических задачах.
=== Пример работы ===
|}
==СсылкиИсточники информации==
* [http://en.wikipedia.org/wiki/D* Wikipedia:D*]
* [http://idm-lab.org/project-a.html Sven Koenig` web page]

Навигация