<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
		<id>http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=95.55.188.216&amp;*</id>
		<title>Викиконспекты - Вклад участника [ru]</title>
		<link rel="self" type="application/atom+xml" href="http://neerc.ifmo.ru/wiki/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=95.55.188.216&amp;*"/>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%92%D0%BA%D0%BB%D0%B0%D0%B4/95.55.188.216"/>
		<updated>2026-07-08T03:16:15Z</updated>
		<subtitle>Вклад участника</subtitle>
		<generator>MediaWiki 1.30.0</generator>

	<entry>
		<id>http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_D*&amp;diff=35033</id>
		<title>Алгоритм D*</title>
		<link rel="alternate" type="text/html" href="http://neerc.ifmo.ru/wiki/index.php?title=%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_D*&amp;diff=35033"/>
				<updated>2014-01-04T14:04:46Z</updated>
		
		<summary type="html">&lt;p&gt;95.55.188.216: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;'''Алгоритм D*''' {{---}} алгоритм поиска кратчайшего пути во взвешенном ориентированном графе, где структура графа неизвестна заранее или постоянно подвергается изменению. Разработан Свеном Кёнигом и Максимом Лихачевым в 2002 году.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм LPA* ==&lt;br /&gt;
&lt;br /&gt;
=== Постановка задачи ===&lt;br /&gt;
Дан [[Основные определения теории графов|взвешенный ориентированный граф]] &amp;lt;tex&amp;gt; G(V, E) &amp;lt;/tex&amp;gt;. Даны вершины &amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;s_{goal}&amp;lt;/tex&amp;gt;. Требуется после каждого изменения графа &amp;lt;tex&amp;gt;G&amp;lt;/tex&amp;gt; уметь вычислять функцию &amp;lt;tex&amp;gt;g(s)&amp;lt;/tex&amp;gt; для каждой известной вершины &amp;lt;tex&amp;gt;s \in V&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Описание ===&lt;br /&gt;
Функция &amp;lt;tex&amp;gt;g(s)&amp;lt;/tex&amp;gt; будет возвращать последнее известное (и самое минимальное) значение расстояния от вершины &amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Обозначим множество &amp;lt;tex&amp;gt;Succ(s) \in V&amp;lt;/tex&amp;gt; как множество вершин, исходящих из вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Аналогично множество &amp;lt;tex&amp;gt;Pred(s) \in V&amp;lt;/tex&amp;gt; как множество вершин, входящих в вершину &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
Функция &amp;lt;tex&amp;gt;0 \leqslant c(s, s') \leqslant +\infty&amp;lt;/tex&amp;gt; будет возвращать стоимость перехода из вершины &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; в вершину &amp;lt;tex&amp;gt;s'&amp;lt;/tex&amp;gt;. При этом &amp;lt;tex&amp;gt;s' \in Succ(s)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если &amp;lt;tex&amp;gt;s = s_{start}&amp;lt;/tex&amp;gt;&lt;br /&gt;
:&amp;lt;tex dpi=&amp;quot;120&amp;quot;&amp;gt;rhs(s) = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
Иначе &lt;br /&gt;
:&amp;lt;tex dpi=&amp;quot;120&amp;quot;&amp;gt;rhs(s) = min_{s' \in Pred(s)}(g(s') + c(s', s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Вершина &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; может быть 3-х видов:&lt;br /&gt;
* насыщена, если &amp;lt;tex&amp;gt;g(s) = rhs(s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
* переполнена, если &amp;lt;tex&amp;gt;g(s) &amp;gt; rhs(s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
* ненасыщена, если &amp;lt;tex&amp;gt;g(s) &amp;lt; rhs(s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Очевидно, что если все вершины насыщены, то мы можем найти расстояние от стартовой вершины до любой. Такой граф будем называть устойчивым (насыщенным).&lt;br /&gt;
&lt;br /&gt;
Функция &amp;lt;tex&amp;gt;key(s)&amp;lt;/tex&amp;gt;, где &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt; - вершина, возвращает вектор из 2-ух значений &amp;lt;tex&amp;gt;k_1(s)&amp;lt;/tex&amp;gt;, &amp;lt;tex&amp;gt;k_2(s)&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* &amp;lt;tex&amp;gt;k_1(s) = min(g(s), rhs(s)) + h(s, s_{goal})&amp;lt;/tex&amp;gt;. &lt;br /&gt;
* &amp;lt;tex&amp;gt;k_2(s) = min(g(s), rhs(s))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Если в конце поиска пути &amp;lt;tex&amp;gt;g(s_{goal}) = +\infty&amp;lt;/tex&amp;gt;, то мы не смогли найти путь от &amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;s_{goal}&amp;lt;/tex&amp;gt; на текущей итерации. Но после следующего изменения графа путь вполне может найтись.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод ===&lt;br /&gt;
&lt;br /&gt;
Основная функция, описывающая алгоритм&lt;br /&gt;
  '''Main'''():&lt;br /&gt;
  {&lt;br /&gt;
    '''Initialize'''();&lt;br /&gt;
    while (true)&lt;br /&gt;
    {&lt;br /&gt;
      '''ComputeShortestPath'''();&lt;br /&gt;
      В данный момент мы знаем кратчайший путь из &amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt; в &amp;lt;tex&amp;gt;s_{goal}&amp;lt;/tex&amp;gt;.&lt;br /&gt;
      Ждем каких-либо изменений графа.&lt;br /&gt;
      for всех ориентированных ребер &amp;lt;tex&amp;gt;(u; v)&amp;lt;/tex&amp;gt; с измененными весами:&lt;br /&gt;
      {&lt;br /&gt;
        Обновляем результат функции &amp;lt;tex&amp;gt;c(u; v)&amp;lt;/tex&amp;gt;;&lt;br /&gt;
        '''UpdateVertex'''(&amp;lt;tex&amp;gt;v&amp;lt;/tex&amp;gt;);&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Теперь опишем составные элементы подробнее&lt;br /&gt;
&lt;br /&gt;
  '''Initialize'''():&lt;br /&gt;
  {&lt;br /&gt;
    //Заведем приоритетную очередь &amp;lt;tex&amp;gt;U&amp;lt;/tex&amp;gt;, в которую будем помещать вершины. Сортировка будет производиться по функции &amp;lt;tex&amp;gt;key(s)&amp;lt;/tex&amp;gt;.&lt;br /&gt;
    &amp;lt;tex&amp;gt;U = \varnothing;&amp;lt;/tex&amp;gt;&lt;br /&gt;
    for &amp;lt;tex&amp;gt;s \in S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;rhs(s) = g(s) = \infty;&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;rhs(s_{start}) = 0;&amp;lt;/tex&amp;gt;&lt;br /&gt;
    U.Insert(&amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt;; CalcKey(&amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt;));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  //Функция &amp;lt;tex&amp;gt;key(s)&amp;lt;/tex&amp;gt;. Возвращаемые значения сортируются в лексографическом порядке, т.е. сначала &amp;lt;tex&amp;gt;k_1(s)&amp;lt;/tex&amp;gt;, потом &amp;lt;tex&amp;gt;k_2(s)&amp;lt;/tex&amp;gt;&lt;br /&gt;
  '''CalcKey'''(s):&lt;br /&gt;
  {&lt;br /&gt;
    return [&amp;lt;tex&amp;gt;\min(g(s); rhs(s)) + h(s; s_{goal})&amp;lt;/tex&amp;gt;; &amp;lt;tex&amp;gt;\min(g(s); rhs(s))&amp;lt;/tex&amp;gt;];&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  '''UpdateVertex'''(&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;):&lt;br /&gt;
  {&lt;br /&gt;
    if (&amp;lt;tex&amp;gt;u \ne s_{start}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
      &amp;lt;tex&amp;gt;rhs(u) = min_{s' \in Pred(u)}(g(s') + c(s',u));&amp;lt;/tex&amp;gt;&lt;br /&gt;
    if (&amp;lt;tex&amp;gt;u \in U&amp;lt;/tex&amp;gt;)&lt;br /&gt;
      U.Remove(u);&lt;br /&gt;
    if (&amp;lt;tex&amp;gt;g(u) \ne rhs(u)&amp;lt;/tex&amp;gt;)&lt;br /&gt;
      U.Insert(&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;; CalcKey(&amp;lt;tex&amp;gt;u&amp;lt;/tex&amp;gt;));&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
  // Функция неоднократно перерасчитывает значение &amp;lt;tex&amp;gt;g(s)&amp;lt;/tex&amp;gt; у ненасыщенных вершин. Такой перерасчет значения &amp;lt;tex&amp;gt;g(s)&amp;lt;/tex&amp;gt; будем называть ''расширением'' вершины.&lt;br /&gt;
  '''ComputeShortestPath'''():&lt;br /&gt;
  {&lt;br /&gt;
    while (U.TopKey() &amp;lt; CalcKey(&amp;lt;tex&amp;gt;s_{goal}&amp;lt;/tex&amp;gt;) OR rhs(&amp;lt;tex&amp;gt;s_{goal}) \ne g(s_{goal}&amp;lt;/tex&amp;gt;))&lt;br /&gt;
      u = U.Pop();&lt;br /&gt;
      if (g(u) &amp;gt; rhs(u))&lt;br /&gt;
        g(u) = rhs(u);&lt;br /&gt;
        for &amp;lt;tex&amp;gt;s \in Succ(u)&amp;lt;/tex&amp;gt;&lt;br /&gt;
          UpdateVertex(s);&lt;br /&gt;
      else&lt;br /&gt;
        g(u) = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;;&lt;br /&gt;
        for &amp;lt;tex&amp;gt;s \in Succ(u) \cup \{u\}&amp;lt;/tex&amp;gt;&lt;br /&gt;
          UpdateVertex(s);&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
Таким образом мы описали алгоритм LPA*. Он неоднократно определяет путь между вершинами &amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;s_{goal}&amp;lt;/tex&amp;gt;, используя при этом данные из предыдущих итераций. Очевидно, что в худшем случае (а именно когда все ребра вокруг текущей вершины изменили свой вес) алгоритм будет работать как последовательные вызовы алгоритма А* за &amp;lt;tex&amp;gt;O(n^2 \cdot log(n))&amp;lt;/tex&amp;gt;. Улучшим эту оценку с помощью алгоритма D* lite.&lt;br /&gt;
&lt;br /&gt;
'''Примечание''': на практике же такой подход тоже имеет место на плотных графах (или матрицах), так как в среднем дает оценку &amp;lt;tex&amp;gt;O(n \cdot log(n))&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
== Алгоритм D* ==&lt;br /&gt;
Пока что был описан только алгоритм LPA*. Он способен неоднократно определять кратчайшее расстояние между начальной и конечной вершинами при любом изменении данного графа. Его первоначальный поиск полностью совпадает с алгоритмом A*, но последующие итерации способны использовать информацию из предыдущих поисков.&lt;br /&gt;
&lt;br /&gt;
=== Постановка задачи ===&lt;br /&gt;
Теперь на основе LPA* опишем алгоритм D*, который способен определять расстояние между текущей вершиной &amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt;, в которой, допустим, находится курсор/робот, и конечной вершиной &amp;lt;tex&amp;gt;s_{goal}&amp;lt;/tex&amp;gt; при каждом изменении графа в то время, как наш робот движется вдоль найденного пути.&lt;br /&gt;
&lt;br /&gt;
=== Описание ===&lt;br /&gt;
Опишем первую версию алгоритма D*. Очевидно, что большинство вершин в процессе движения робота остаются неизменными, поэтому мы можем применить алгоритм LPA*. &lt;br /&gt;
&lt;br /&gt;
'''Примечание''': Большинство функций переходят в данный алгоритм без изменений, поэтому опишем только измененные части.&lt;br /&gt;
&lt;br /&gt;
Для начала мы поменяем направление поиска в графе. &lt;br /&gt;
&lt;br /&gt;
Теперь функция g(s) хранит минимальное известное расстояние от &amp;lt;tex&amp;gt;s_{goal}&amp;lt;/tex&amp;gt; до &amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;. Свойства остаются прежними.&lt;br /&gt;
&lt;br /&gt;
Эвристическая функция h(s,s') теперь должна быть неотрицательная и обратно-устойчивая, т.е. &lt;br /&gt;
&amp;lt;tex&amp;gt;h(s_{start},s_{start}) = 0&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;h(s_{start}, s) &amp;lt;= h(s_{start},s') + c(s',s)&amp;lt;/tex&amp;gt; для всех &amp;lt;tex&amp;gt;s \in S&amp;lt;/tex&amp;gt; и &amp;lt;tex&amp;gt;s' \in Pred(s)&amp;lt;/tex&amp;gt;. Очевидно, что при движении робота &amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt; изменяется, поэтому данные свойства должны выполняться для всех &amp;lt;tex&amp;gt;s_{start} \in S&amp;lt;/tex&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Дополнительное условие выхода также меняется, т.е. при &amp;lt;tex&amp;gt;g(s_{start}) = +\infty&amp;lt;/tex&amp;gt; путь не найден на данной итерации. Иначе путь найден и робот может проследовать по нему.&lt;br /&gt;
&lt;br /&gt;
'''Примечание''':  Так же следует отметить, что функция '''Initialize''' не обязана инициализировать абсолютно все вершины перед стартом алгоритма. Это важно, так как в на практике число вершин может быть огромным и только немногие будут пройдены робот в процессе движения. Так же это дает возможность добавления/удаления ребер без потери устойчивости всех подграфов данного графа.&lt;br /&gt;
&lt;br /&gt;
=== Псевдокод (Первая версия) ===&lt;br /&gt;
При такой постановке задачи псевдокод не сильно меняется. Но функция '''Main''' все-таки претерпевает значительные изменения.&lt;br /&gt;
&lt;br /&gt;
  '''CalcKey'''(s):&lt;br /&gt;
    return [&amp;lt;tex&amp;gt;\min(g(s);rhs(s)) + h(s_{start};s)&amp;lt;/tex&amp;gt;;&amp;lt;tex&amp;gt;\min(g(s); rhs(s))&amp;lt;/tex&amp;gt;];&lt;br /&gt;
&lt;br /&gt;
  '''Initialize'''():&lt;br /&gt;
    U = &amp;lt;tex&amp;gt;\varnothing&amp;lt;/tex&amp;gt;;&lt;br /&gt;
    for &amp;lt;tex&amp;gt;s \in S&amp;lt;/tex&amp;gt;&lt;br /&gt;
      &amp;lt;tex&amp;gt;rhs(s) = g(s) = +\infty&amp;lt;/tex&amp;gt;&lt;br /&gt;
    &amp;lt;tex&amp;gt;rhs(s_{goal}) = 0&amp;lt;/tex&amp;gt;&lt;br /&gt;
    U.Insert(&amp;lt;tex&amp;gt;s_{goal}&amp;lt;/tex&amp;gt;; CalcKey(&amp;lt;tex&amp;gt;s_{goal}&amp;lt;/tex&amp;gt;));&lt;br /&gt;
&lt;br /&gt;
  '''UpdateVertex'''(u):&lt;br /&gt;
    if (&amp;lt;tex&amp;gt;u \ne s_{goal}&amp;lt;/tex&amp;gt;) &lt;br /&gt;
      rhs(u) = &amp;lt;tex&amp;gt;min_{s' \in Succ(u)}(c(u,s')+g(s'));&amp;lt;/tex&amp;gt;&lt;br /&gt;
    if (&amp;lt;tex&amp;gt;u \in U&amp;lt;/tex&amp;gt;) &lt;br /&gt;
      U.Remove(u);&lt;br /&gt;
    if (&amp;lt;tex&amp;gt;g(u) \ne rhs(u)&amp;lt;/tex&amp;gt;) &lt;br /&gt;
      U.Insert(u; CalcKey(u));&lt;br /&gt;
&lt;br /&gt;
  '''ComputeShortestPath'''():&lt;br /&gt;
    while (U.TopKey() &amp;lt; CalcKey(&amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt;) OR &amp;lt;tex&amp;gt;rhs(s_{start}) \ne g(s_{start})&amp;lt;/tex&amp;gt;)&lt;br /&gt;
      u = U.Pop();&lt;br /&gt;
      if (g(u) &amp;gt; rhs(u))&lt;br /&gt;
        g(u) = rhs(u);&lt;br /&gt;
        for &amp;lt;tex&amp;gt;s \in Pred(u)&amp;lt;/tex&amp;gt; &lt;br /&gt;
          UpdateVertex(s);&lt;br /&gt;
      else&lt;br /&gt;
        g(u) = &amp;lt;tex&amp;gt;+\infty&amp;lt;/tex&amp;gt;;&lt;br /&gt;
        for &amp;lt;tex&amp;gt;s \in Pred(u) \cup \{u\}&amp;lt;/tex&amp;gt; &lt;br /&gt;
          UpdateVertex(s);&lt;br /&gt;
&lt;br /&gt;
  '''Main'''():&lt;br /&gt;
    '''Initialize'''();&lt;br /&gt;
    '''ComputeShortestPath'''();&lt;br /&gt;
    while (&amp;lt;tex&amp;gt;s_{start} \ne s_{goal}&amp;lt;/tex&amp;gt;)&lt;br /&gt;
      // if (&amp;lt;tex&amp;gt;g(s_{start}) = \infty&amp;lt;/tex&amp;gt;) тогда путь на данной итерации не найден.&lt;br /&gt;
      &amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt; = такая вершина s', что &amp;lt;tex&amp;gt;min_{s' \in Succ(s_{start})}(c(s_{start}, s') + g(s'))&amp;lt;/tex&amp;gt;&lt;br /&gt;
      Передвинулись вдоль найденного пути и изменили вершину &amp;lt;tex&amp;gt;s_{start}&amp;lt;/tex&amp;gt;;&lt;br /&gt;
      Сканируем роботом какие-либо изменения в графе или убеждаемся, что граф остается прежним.&lt;br /&gt;
      if (если граф изменился)&lt;br /&gt;
        for всех ориентированных ребер &amp;lt;tex&amp;gt;(u; v)&amp;lt;/tex&amp;gt; с измененными весами:&lt;br /&gt;
          Обновляем результат функции &amp;lt;tex&amp;gt;c(u; v)&amp;lt;/tex&amp;gt;;&lt;br /&gt;
          '''UpdateVertex'''(u);&lt;br /&gt;
        for &amp;lt;tex&amp;gt;s \in U&amp;lt;/tex&amp;gt;&lt;br /&gt;
          U.Update(&amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;; '''CalcKey'''(&amp;lt;tex&amp;gt;s&amp;lt;/tex&amp;gt;));&lt;br /&gt;
        ComputeShortestPath();&lt;br /&gt;
&lt;br /&gt;
{{Теорема&lt;br /&gt;
|author=Свен Кёниг&lt;br /&gt;
|about=Об устойчивой насыщенности вершин&lt;br /&gt;
|statement=Функция '''ComputeShortestPath''' в данной версии алгоритма ''расширяет'' вершину максимум 2 раза, а именно 1 раз, если вершина ненасыщена, и максимум 1 раз, если она перенасыщена.&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Ссылки==&lt;br /&gt;
* [http://en.wikipedia.org/wiki/D* Wikipedia:D*]&lt;br /&gt;
* [http://idm-lab.org/project-a.html Sven Koenig` web page]&lt;br /&gt;
* [http://pub1.willowgarage.com/~konolige/cs225b/dlite_tro05.pdf]&lt;br /&gt;
&lt;br /&gt;
[[Категория: Алгоритмы и структуры данных]]&lt;br /&gt;
[[Категория: Кратчайшие пути в графах ]]&lt;/div&gt;</summary>
		<author><name>95.55.188.216</name></author>	</entry>

	</feed>