Алгоритм D* — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 7: Строка 7:
  
 
=== Описание ===
 
=== Описание ===
Обозначим множество <tex>S</tex> как множество вершин графа.
+
Обозначим множество <tex>Succ(s) \in V</tex> как множество вершин, исходящих из вершины <tex>s</tex>.  
  
Обозначим множество <tex>Succ(s) \in S</tex> как множество вершин, исходящих из вершины <tex>s</tex>.  
+
Аналогично множество <tex>Pred(s) \in V</tex> как множество вершин, входящих в вершину <tex>s</tex>.  
  
Аналогично множество <tex>Pred(s) \in S</tex> как множество вершин, входящих в вершину <tex>s</tex>.  
+
Функция <tex>0 \le с(s, s') \le +\infty</tex> будет возвращать стоимость перехода из вершины <tex>s</tex> в вершину <tex>s'</tex>. При этом <tex>s' \in Succ(s)</tex>.
  
Функция <tex>0 \le с(s, s') \le +inf</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>.
 
  
 
Если <tex>s = s_{start}</tex>
 
Если <tex>s = s_{start}</tex>
<tex>rhs(s) = 0</tex>
+
:<tex dpi="120">rhs(s) = 0</tex>
 
Иначе  
 
Иначе  
<tex>rhs(s) = min_{s' \in pred(s)}(g(s') + c(s', s)</tex>
+
:<tex dpi="120">rhs(s) = min_{s' \in pred(s)}(g(s') + c(s', s)</tex>
 +
 
 +
Вершина <tex>s</tex> может быть 3-х видов:
 +
* насыщена, если <tex>g(s) = rhs(s)</tex>
 +
* переполнена, если <tex>g(s) > rhs(s)</tex>
 +
* ненасыщена, если <tex>g(s) < rhs(s)</tex>
  
Будем говорить, что вершина <tex>s</tex> насыщена, если <tex>g(s) = rhs(s)</tex>
 
переполнена
 
ненасыщена
 
 
Очевидно, что если все вершины насыщены, то мы можем найти расстояние от стартовой вершины до любой.
 
Очевидно, что если все вершины насыщены, то мы можем найти расстояние от стартовой вершины до любой.
  
 
Функция <tex>key(s)</tex>, где <tex>s</tex> - вершина, возвращает вектор из 2-ух значений <tex>k_1(s)</tex>, <tex>k_2(s)</tex>. <tex>k_1(s) = min(g(s), rhs(s)) + h(s, s_goal)</tex>. <tex>k_2(s) = min(g(s), rhs(s))</tex>
 
Функция <tex>key(s)</tex>, где <tex>s</tex> - вершина, возвращает вектор из 2-ух значений <tex>k_1(s)</tex>, <tex>k_2(s)</tex>. <tex>k_1(s) = min(g(s), rhs(s)) + h(s, s_goal)</tex>. <tex>k_2(s) = min(g(s), rhs(s))</tex>
  
Если в конце поиска пути <tex>g(s_{goal}) = +inf</tex>, то мы не смогли найти путь от <tex>s_{start}</tex> до <tex>s_{goal}</tex>
+
Если в конце поиска пути <tex>g(s_{goal}) = +\infty</tex>, то мы не смогли найти путь от <tex>s_{start}</tex> до <tex>s_{goal}</tex> на текущей итерации. Но после следующего изменения графа путь вполне может найтись.
  
 
=== Псевдокод ===
 
=== Псевдокод ===
  
   '''CalcKey'''(s):
+
Основная функция, описывающая алгоритм
 +
   '''Main'''():
 
   {
 
   {
     return [<tex>min(g(s); rhs(s)) + h(s;s_{goal})</tex>;<tex>min(g(s); rhs(s))</tex>];
+
     '''Initialize'''();
 +
    while (true)
 +
    {
 +
      '''ComputeShortestPath'''();
 +
      В данный момент мы знаем кратчайший путь из <tex>s_{start}</tex> в <tex>s_{goal}</tex>.
 +
      Ждем каких-либо изменений графа.
 +
      for всех ориентированных ребер <tex>(u; v)</tex> с измененными весами:
 +
      {
 +
        Обновляем результат функции <tex>c(u; v)</tex>;
 +
        '''UpdateVertex'''(<tex>v</tex>);
 +
      }
 +
    }
 
   }
 
   }
 +
 +
Теперь опишем составные элементы подробнее
  
 
   '''Initialize'''():
 
   '''Initialize'''():
 
   {
 
   {
     <tex>U = null;</tex>
+
    //Заведем приоритетную очередь <tex>U</tex>, в которую будем помещать вершины. Сортировка будет производиться по функции <tex>key(s)</tex>.
     <tex>for all s \in S</tex>
+
     <tex>U = \varnothing;</tex>
       <tex>rhs(s) = g(s) = 1;</tex>
+
     for <tex>s \in S</tex>
     <tex>rhs(s_start) = 0;</tex>
+
       <tex>rhs(s) = g(s) = \infty;</tex>
     <tex>U.Insert(s_{start};CalcKey(s_{start}));</tex>
+
     <tex>rhs(s_{start}) = 0;</tex>
 +
     U.Insert(<tex>s_{start}</tex>; CalcKey(<tex>s_{start}</tex>));
 +
  }
 +
 
 +
  Функция <tex>key(s)</tex>. Возвращаемые значения сортируются в лексографическом порядке, т.е. сначала <tex>k_1(s)</tex>, потом <tex>k_2(s)</tex>
 +
  '''CalcKey'''(s):
 +
  {
 +
    return [<tex>\min(g(s); rhs(s)) + h(s; s_{goal})</tex>;<tex>\min(g(s); rhs(s))</tex>];
 
   }
 
   }
  
 
   '''UpdateVertex'''(<tex>u</tex>):
 
   '''UpdateVertex'''(<tex>u</tex>):
 
   {
 
   {
     if (<tex>u != s_{start}</tex>)  
+
     if (<tex>u \ne s_{start}</tex>)  
       <tex>rhs(u) = min_{s' \in Pred(u)}(g(s')+c(s',u));</tex>
+
       <tex>rhs(u) = min_{s' \in Pred(u)}(g(s') + c(s',u));</tex>
 
     if (<tex>u \in U</tex>)
 
     if (<tex>u \in U</tex>)
 
       U.Remove(u);
 
       U.Remove(u);
     if (g(u) != rhs(u))
+
     if (<tex>g(u) \ne rhs(u)</tex>)
       U.Insert(<tex>u</tex>;CalcKey(<tex>u</tex>));
+
       U.Insert(<tex>u</tex>; CalcKey(<tex>u</tex>));
 
   }
 
   }
  
 
   '''ComputeShortestPath'''():
 
   '''ComputeShortestPath'''():
 
   {
 
   {
     while (U.TopKey()< CalcKey(<tex>s_{goal}</tex>) OR rhs(<tex>s_{goal}) != g(s_{goal}</tex>))
+
     while (U.TopKey() < CalcKey(<tex>s_{goal}</tex>) OR rhs(<tex>s_{goal}) \ne g(s_{goal}</tex>))
 
       u = U.Pop();
 
       u = U.Pop();
 
       if (g(u) > rhs(u))
 
       if (g(u) > rhs(u))
 
         g(u) = rhs(u);
 
         g(u) = rhs(u);
         for all s 2 Succ(u) UpdateVertex(s);
+
         for <tex>s \in Succ(u)</tex>
 +
          UpdateVertex(s);
 
       else
 
       else
         g(u) = 1;
+
         g(u) = <tex>+\infty</tex>;
      for all <tex>s \in Succ(u) perec {u}</tex>
+
        for <tex>s \in Succ(u) \cup \{u\}</tex>
        UpdateVertex(s);
+
          UpdateVertex(s);
  }
 
 
 
  '''Main'''():
 
  {
 
    Initialize();
 
    while (<tex>s_{start} != s_{goal}</tex>)
 
    {
 
      ComputeShortestPath();
 
      Wait for changes in edge costs;
 
      for all directed edges (u;v) with changed edge costs
 
      {
 
        Update the edge cost c(u;v);
 
        UpdateVertex(v);
 
      }
 
    }
 
 
   }
 
   }
  
Строка 91: Строка 98:
 
== Алгоритм D* ==
 
== Алгоритм D* ==
  
 +
=== Псевдокод (Первая версия) ===
 
   '''CalcKey'''(s):
 
   '''CalcKey'''(s):
     return [<tex>min(g(s);rhs(s)) + h(sstart;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>];
  
 
   '''Initialize'''():
 
   '''Initialize'''():
     U = null;
+
     U = <tex>\varnothing</tex>;
     for all <tex>s \in S</tex>
+
     for <tex>s \in S</tex>
       <tex>rhs(s) = g(s) = 1</tex>
+
       <tex>rhs(s) = g(s) = +\infty</tex>
 
     <tex>rhs(s_{goal}) = 0</tex>
 
     <tex>rhs(s_{goal}) = 0</tex>
     U.Insert(sgoal;CalcKey(sgoal));
+
     U.Insert(<tex>s_{goal}</tex>; CalcKey(<tex>s_{goal}</tex>));
  
 
   '''UpdateVertex'''(u):
 
   '''UpdateVertex'''(u):
     if (u 6= s_{goal}) rhs(u) = mins02Succ(u)
+
     if (<tex>u != s_{goal}</tex>)  
(c(u;s
+
      rhs(u) = min_{s' \in Succ(u)}(c(u,s')+g(s'));
0
+
    if (<tex>u \in U</tex>)  
) + g(s
+
      U.Remove(u);
0
+
    if (<tex>g(u) != rhs(u)</tex>)  
));
+
      U.Insert(u; CalcKey(u));
f07’g if (u 2 U) U.Remove(u);
 
f08’g if (g(u) 6= rhs(u)) U.Insert(u;CalcKey(u));
 
  
 
   '''ComputeShortestPath'''()
 
   '''ComputeShortestPath'''()
     while (U.TopKey()<_ CalcKey(s_{start}) OR rhs(s_{start}) 6= g(s_{start}))
+
     while (U.TopKey() <tex><=</tex> CalcKey(<tex>s_{start}</tex>) OR <tex>rhs(s_{start}) != g(s_{start})</tex>)
 
       u = U.Pop();
 
       u = U.Pop();
 
       if (g(u) > rhs(u))
 
       if (g(u) > rhs(u))
 
         g(u) = rhs(u);
 
         g(u) = rhs(u);
      for all s 2 Pred(u) UpdateVertex(s);
+
        for <tex>s \in Pred(u)</tex>
 +
          UpdateVertex(s);
 
       else
 
       else
         g(u) = 1;
+
         g(u) = <tex>+\infty</tex>;
      for all s 2 Pred(u) [ fug UpdateVertex(s);
+
        for <tex>s \in Pred(u) \cup \{u\}</tex>
 +
          UpdateVertex(s);
  
 
   '''Main'''():
 
   '''Main'''():
 
     Initialize();
 
     Initialize();
 
     ComputeShortestPath();
 
     ComputeShortestPath();
     while (s_{start} 6= s_{goal})
+
     while (<tex>s_{start} \ne s_{goal}</tex>)
       if (g(sstart) = 1) then there is no known path */
+
       // if (<tex>g(s_{start}) = \infty</tex>) тогда путь на данной итерации не найден.
       sstart = argmins02Succ(sstart)
+
       s_{start} = argmins02Succ(sstart)
(c(sstart;s
+
      Move to <tex>s_{start}</tex>;
0
+
      Scan graph for changed edge costs;
) + g(s
+
      if any edge costs changed
0
+
        for all directed edges (u; v) with changed edge costs
));
+
          Update the edge cost c(u; v);
f22’g Move to sstart;
+
          UpdateVertex(u);
f23’g Scan graph for changed edge costs;
+
        for <tex>s \in U</tex>
f24’g if any edge costs changed
+
          U.Update(<tex>s</tex>; CalcKey(<tex>s</tex>));
f25’g for all directed edges (u;v) with changed edge costs
+
        ComputeShortestPath();
f26’g Update the edge cost c(u;v);
 
f27’g UpdateVertex(u);
 
f28’g for all s 2 U
 
f29’g U.Update(s;CalcKey(s));
 
f30’g ComputeShortestPath();
 
  
 
==Ссылки==
 
==Ссылки==

Версия 23:41, 2 января 2014

Алгоритм D* — алгоритм поиска кратчайшего пути во взвешенном ориентированном графе, где структура графа неизвестна заранее или постоянно подвергается изменению. Разработан Свеном Кёнигом и Максимом Лихачевым в 2002 году.

Алгоритм LPA*

Постановка задачи

Дан взвешенный ориентированный граф [math] G(V, E) [/math]. Даны вершины [math]s_{start}[/math] и [math]s_{goal}[/math]. Требуется после каждого изменения графа [math]G[/math] уметь вычислять функцию [math]g(s)[/math] для каждой известной вершины [math]s \in V[/math]

Описание

Обозначим множество [math]Succ(s) \in V[/math] как множество вершин, исходящих из вершины [math]s[/math].

Аналогично множество [math]Pred(s) \in V[/math] как множество вершин, входящих в вершину [math]s[/math].

Функция [math]0 \le с(s, s') \le +\infty[/math] будет возвращать стоимость перехода из вершины [math]s[/math] в вершину [math]s'[/math]. При этом [math]s' \in Succ(s)[/math].

Функция [math]g(s)[/math] будет возвращать последнее известное (и самое минимальное) значение расстояния от вершины [math]s_{start}[/math] до [math]s[/math].

Если [math]s = s_{start}[/math]

[math]rhs(s) = 0[/math]

Иначе

[math]rhs(s) = min_{s' \in pred(s)}(g(s') + c(s', s)[/math]

Вершина [math]s[/math] может быть 3-х видов:

  • насыщена, если [math]g(s) = rhs(s)[/math]
  • переполнена, если [math]g(s) \gt rhs(s)[/math]
  • ненасыщена, если [math]g(s) \lt rhs(s)[/math]

Очевидно, что если все вершины насыщены, то мы можем найти расстояние от стартовой вершины до любой.

Функция [math]key(s)[/math], где [math]s[/math] - вершина, возвращает вектор из 2-ух значений [math]k_1(s)[/math], [math]k_2(s)[/math]. [math]k_1(s) = min(g(s), rhs(s)) + h(s, s_goal)[/math]. [math]k_2(s) = min(g(s), rhs(s))[/math]

Если в конце поиска пути [math]g(s_{goal}) = +\infty[/math], то мы не смогли найти путь от [math]s_{start}[/math] до [math]s_{goal}[/math] на текущей итерации. Но после следующего изменения графа путь вполне может найтись.

Псевдокод

Основная функция, описывающая алгоритм

 Main():
 {
   Initialize();
   while (true)
   {
     ComputeShortestPath();
     В данный момент мы знаем кратчайший путь из [math]s_{start}[/math] в [math]s_{goal}[/math].
     Ждем каких-либо изменений графа.
     for всех ориентированных ребер [math](u; v)[/math] с измененными весами:
     {
       Обновляем результат функции [math]c(u; v)[/math];
       UpdateVertex([math]v[/math]);
     }
   }
 }

Теперь опишем составные элементы подробнее

 Initialize():
 {
   //Заведем приоритетную очередь [math]U[/math], в которую будем помещать вершины. Сортировка будет производиться по функции [math]key(s)[/math].
   [math]U = \varnothing;[/math]
   for [math]s \in S[/math]
     [math]rhs(s) = g(s) = \infty;[/math]
   [math]rhs(s_{start}) = 0;[/math]
   U.Insert([math]s_{start}[/math]; CalcKey([math]s_{start}[/math]));
 }
 Функция [math]key(s)[/math]. Возвращаемые значения сортируются в лексографическом порядке, т.е. сначала [math]k_1(s)[/math], потом [math]k_2(s)[/math]
 CalcKey(s):
 {
   return [[math]\min(g(s); rhs(s)) + h(s; s_{goal})[/math];[math]\min(g(s); rhs(s))[/math]];
 }
 UpdateVertex([math]u[/math]):
 {
   if ([math]u \ne s_{start}[/math]) 
     [math]rhs(u) = min_{s' \in Pred(u)}(g(s') + c(s',u));[/math]
   if ([math]u \in U[/math])
     U.Remove(u);
   if ([math]g(u) \ne rhs(u)[/math])
     U.Insert([math]u[/math]; CalcKey([math]u[/math]));
 }
 ComputeShortestPath():
 {
   while (U.TopKey() < CalcKey([math]s_{goal}[/math]) OR rhs([math]s_{goal}) \ne g(s_{goal}[/math]))
     u = U.Pop();
     if (g(u) > rhs(u))
       g(u) = rhs(u);
       for [math]s \in Succ(u)[/math]
         UpdateVertex(s);
     else
       g(u) = [math]+\infty[/math];
       for [math]s \in Succ(u) \cup \{u\}[/math]
         UpdateVertex(s);
 }

Таким образом мы описали алгоритм LPA*. Он неоднократно определяет путь между вершинами [math]s_start[/math] и [math]s_goal[/math], используя при этом данные из предыдущих итераций. Очевидно, что в худшем случае (а именно когда все ребра вокруг текущей вершины изменили свой вес) алгоритм будет работать как последовательные вызовы алгоритма А* за [math]O(n^2*log(n))[/math]. Улучшим эту оценку с помощью алгоритма D* lite.

Примечание: на практике же такой подход тоже имеет место на плотных графах (или матрицах), так как в среднем дает оценку О(n*log(n)).

Алгоритм D*

Псевдокод (Первая версия)

 CalcKey(s):
   return [[math]\min(g(s);rhs(s)) + h(s_{start};s)[/math];[math]\min(g(s); rhs(s))[/math]];
 Initialize():
   U = [math]\varnothing[/math];
   for [math]s \in S[/math]
     [math]rhs(s) = g(s) = +\infty[/math]
   [math]rhs(s_{goal}) = 0[/math]
   U.Insert([math]s_{goal}[/math]; CalcKey([math]s_{goal}[/math]));
 UpdateVertex(u):
   if ([math]u != s_{goal}[/math]) 
     rhs(u) = min_{s' \in Succ(u)}(c(u,s')+g(s'));
   if ([math]u \in U[/math]) 
     U.Remove(u);
   if ([math]g(u) != rhs(u)[/math]) 
     U.Insert(u; CalcKey(u));
 ComputeShortestPath()
   while (U.TopKey() [math]\lt =[/math] CalcKey([math]s_{start}[/math]) OR [math]rhs(s_{start}) != g(s_{start})[/math])
     u = U.Pop();
     if (g(u) > rhs(u))
       g(u) = rhs(u);
       for [math]s \in Pred(u)[/math] 
         UpdateVertex(s);
     else
       g(u) = [math]+\infty[/math];
       for [math]s \in Pred(u) \cup \{u\}[/math] 
         UpdateVertex(s);
 Main():
   Initialize();
   ComputeShortestPath();
   while ([math]s_{start} \ne s_{goal}[/math])
     // if ([math]g(s_{start}) = \infty[/math]) тогда путь на данной итерации не найден.
     s_{start} = argmins02Succ(sstart)
     Move to [math]s_{start}[/math];
     Scan graph for changed edge costs;
     if any edge costs changed
       for all directed edges (u; v) with changed edge costs
         Update the edge cost c(u; v);
         UpdateVertex(u);
       for [math]s \in U[/math]
         U.Update([math]s[/math]; CalcKey([math]s[/math]));
       ComputeShortestPath();

Ссылки