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

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 7: Строка 7:
 
Разделим вершины на три множества:
 
Разделим вершины на три множества:
 
* <tex>M_0</tex> {{---}} вершины, расстояние до которых уже вычислено (возможно, не окончательно)
 
* <tex>M_0</tex> {{---}} вершины, расстояние до которых уже вычислено (возможно, не окончательно)
* <tex>M_1</tex> {{---}} вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на две очереди:
+
* <tex>M_1</tex> {{---}} вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на две [[Очередь|очереди]]:
 
# <tex>M_1^{'}</tex> {{---}} основная очередь
 
# <tex>M_1^{'}</tex> {{---}} основная очередь
 
# <tex>M_1^{''}</tex> {{---}} срочная очередь
 
# <tex>M_1^{''}</tex> {{---}} срочная очередь
Строка 14: Строка 14:
 
Изначально все вершины, кроме <tex>s</tex> помещаются в множество <tex>M_2</tex>. Вершина <tex>s</tex> помещается в множество <tex>M_1</tex> (в любую из очередей).
 
Изначально все вершины, кроме <tex>s</tex> помещаются в множество <tex>M_2</tex>. Вершина <tex>s</tex> помещается в множество <tex>M_1</tex> (в любую из очередей).
  
'''Шаг алгоритма:''' выбирается вершина <tex>u</tex> из <tex>M_1</tex>. Если очередь <tex>M_1^{''}</tex> не пуста, то вершина берется из нее, иначе из <tex>M_1^{'}</tex>. Далее, для каждого ребра <tex>uv \in E</tex>:
+
'''Шаг алгоритма:''' выбирается вершина <tex>u</tex> из <tex>M_1</tex>. Если очередь <tex>M_1^{''}</tex> не пуста, то вершина берется из нее, иначе из <tex>M_1^{'}</tex>. Для каждого ребра <tex>uv \in E</tex> возможны три случая:
* если <tex>v \in M_2</tex>, то <tex>v</tex> переводится в конец очереди <tex>M_1^{'}</tex>. При этом <tex>d_v \gets d_u + w_{uv}</tex> (производится релаксация ребра <tex>uv</tex>)
+
* <tex>v \in M_2</tex>, то <tex>v</tex> переводится в конец очереди <tex>M_1^{'}</tex>. При этом <tex>d_v \gets d_u + w_{uv}</tex> (производится релаксация ребра <tex>uv</tex>)
* если <tex>v \in M_1</tex>, то происходит релаксация ребра <tex>uv</tex>
+
* <tex>v \in M_1</tex>, то происходит релаксация ребра <tex>uv</tex>
* если <tex>v \in M_0</tex> и <tex>d_v > d_u + w_{uv}</tex>, то происходит релаксация ребра <tex>uv</tex> и <tex>v</tex> помещается в <tex>M_1^{''}</tex>
+
* <tex>v \in M_0</tex> и <tex>d_v > d_u + w_{uv}</tex>, то происходит релаксация ребра <tex>uv</tex> и <tex>v</tex> помещается в <tex>M_1^{''}</tex>.
В конце шага помещаем вершину <tex>u</tex> в множество <tex>M_0</tex>.
+
В конце шага помещаем вершину <tex>u</tex> в множество <tex>M_0</tex>
  
 
Алгоритм заканчивает работу, когда множество <tex>M_1</tex> становится пустым.
 
Алгоритм заканчивает работу, когда множество <tex>M_1</tex> становится пустым.
Строка 24: Строка 24:
 
== Псевдокод ==
 
== Псевдокод ==
  
  '''for''' u : u <tex>\in V</tex>
+
  '''for''' <tex>u : u \in V</tex>
     d[v] <tex>\leftarrow \infty</tex>
+
     <tex>d[u] = \infty</tex>
  d[s] <tex>\leftarrow 0</tex>
+
  <tex>d[s] = 0</tex>
  <tex>M_1^{'}</tex>.push(s)
+
  <tex>M_1^{'}</tex>.push(<tex>s</tex>)
  '''for''' u : u <tex>\neq</tex> '''and''' uv <tex>\in V</tex>
+
  '''for''' <tex>u : u \neq s</tex> '''and''' <tex>u \in V</tex>
     <tex>M_2</tex>.push(u)
+
     <tex>M_2</tex>.push(<tex>u</tex>)
 
  '''while''' <tex>M_1^{'} \neq \varnothing</tex> '''and''' <tex>M_1^{''} \neq \varnothing</tex>
 
  '''while''' <tex>M_1^{'} \neq \varnothing</tex> '''and''' <tex>M_1^{''} \neq \varnothing</tex>
 +
    '''int''' <tex>u</tex>
 
     '''if''' <tex>M_1^{''} \neq \varnothing</tex>
 
     '''if''' <tex>M_1^{''} \neq \varnothing</tex>
       '''int''' u <tex>=</tex> <tex>M_1^{''}</tex>.pop()
+
       <tex>u = M_1^{''}</tex>.pop()
     '''else :'''
+
     '''else'''
       '''int''' u <tex>=</tex> <tex>M_1{'}</tex>.pop()
+
       <tex>u = M_1{'}</tex>.pop()
     '''for''' v : uv <tex>\in E</tex>
+
     '''for''' <tex>v : uv \in E</tex>
       '''if''' v <tex>\in M_2</tex>
+
       '''if''' <tex>v \in M_2</tex>
           <tex>M_1^{'}</tex>.push(v)
+
           <tex>M_1^{'}</tex>.push(<tex>v</tex>)
           <tex>M_2</tex>.remove(v)
+
           <tex>M_2</tex>.remove(<tex>v</tex>)
           d[v] <tex>=</tex> min(d[v], d[u] <tex>+</tex> <tex>w_{uv}</tex>)
+
           <tex>d[v] =</tex> min(<tex>d[v], d[u] + w_{uv}</tex>)
       '''if''' v <tex>\in M_1</tex>
+
       '''else if''' v <tex>\in M_1</tex>
           d[v] = min(d[v], d[u] <tex>+</tex> <tex>w_{uv}</tex>)
+
           <tex>d[v] =</tex> min(<tex>d[v], d[u] + w_{uv}</tex>)
       '''if''' v <tex>\in M_0</tex> '''and''' d[v] <tex>></tex> d[u] <tex>+</tex> <tex>w_{uv}</tex>
+
       '''else if''' <tex>v \in M_0</tex> '''and''' <tex>d[v] > d[u] + w_{uv}</tex>
           <tex>M_1^{''}</tex>.push(v)
+
           <tex>M_1^{''}</tex>.push(<tex>v</tex>)
           <tex>M_0</tex>.remove(v)
+
           <tex>M_0</tex>.remove(<tex>v</tex>)
           d[v] <tex>=</tex> d[u] <tex>+</tex> <tex>w_{uv}</tex>
+
           <tex>d[v] = d[u] + w_{uv}</tex>
     <tex>M_0</tex>.push(u)
+
     <tex>M_0</tex>.push(<tex>u</tex>)
  
 
== Доказательство ==
 
== Доказательство ==
Строка 67: Строка 68:
 
== Сложность ==
 
== Сложность ==
  
В худшем случае алгоритм Левита работает за экспоненциальное время, например в полном графе <tex>K_n</tex> с достаточно большим n и весами <tex>w_{uv} =
+
В худшем случае алгоритм Левита работает за экспоненциальное время. Например, в полном графе <tex>K_n</tex> с достаточно большим n и весами <tex>w_{uv} =
 
\begin{cases}
 
\begin{cases}
  0, & u > 1 \\
+
  v - u - 1, & u > 1; u = 1 и v = n\\
  n - v + 1, & u = 1 \\
+
  w_{un}*2, & u = 1, v = n - 1 \\
\end{cases}</tex> при обработке <tex>i</tex>-ой вершины из множества <tex>M_2</tex> будет происходить релаксация <tex>i-1</tex> рёбер и их добавление в срочную <tex>M_1{''}</tex> очередь.Однако, на реальных графах алгоритм Левита работает быстрее, чем алгоритм [[Алгоритм Форда-Беллмана|Форда Беллмана]] и не многим хуже алгоритма [[Алгоритм Дейкстры|Дейкстры]].
+
w_{uv+1} + v - u, & u = 1, v < n-1
 +
\end{cases}</tex>, рёбра идут в [[Лексикографический порядок|лексикографическом порядке]]. Добавление <tex>i</tex>-ой вершины в очередь и последующая её обработка влекут добавление из <tex>M_0</tex> в <tex>M_1{''}</tex> всех её предыдущих вершин, начиная со <tex>2</tex>-ой; дойдя до вершины <tex>i-1</tex>, в <tex>M_1{''}</tex> снова добавятся вершины меньше <tex>i-1</tex>-ой (кроме первой). Таким образом, количество добавлений <tex>i</tex>-ой вершины в <tex>M_1{''}</tex> составит <tex dpi="130">\sum\limits_{k=1}^{n-i} k</tex> раз. Однако, на реальных графах алгоритм Левита работает быстрее, чем алгоритм [[Алгоритм Форда-Беллмана|Форда Беллмана]] и не многим хуже алгоритма [[Алгоритм Дейкстры|Дейкстры]].
 +
 
 +
==См. также==
 +
* [[Алгоритм A*]]
 +
* [[Алгоритм Дейкстры]]
 +
* [[Алгоритм Джонсона]]
 +
* [[Алгоритм Флойда]]
 +
* [[Алгоритм Форда-Беллмана]]
 +
* [[Обход в ширину]]
  
 
== Источники ==
 
== Источники ==
* [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0 Алгоритм Левита - Википедия]
+
* [http://ru.wikipedia.org/wiki/%D0%90%D0%BB%D0%B3%D0%BE%D1%80%D0%B8%D1%82%D0%BC_%D0%9B%D0%B5%D0%B2%D0%B8%D1%82%D0%B0 Википедия — Алгоритм Левита]
* [http://e-maxx.ru/algo/levit_algorithm Алгоритм Левита]
+
* [http://e-maxx.ru/algo/levit_algorithm e-maxx — Алгоритм Левита]
 
* И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., 4 издание, стр. 229-231.
 
* И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., 4 издание, стр. 229-231.
  
 
[[Категория: Алгоритмы и структуры данных]]
 
[[Категория: Алгоритмы и структуры данных]]
 
[[Категория: Кратчайшие пути в графах]]
 
[[Категория: Кратчайшие пути в графах]]

Версия 22:44, 6 ноября 2015

Алгоритм Левита (англ. Levit's algorithm) находит расстояние от заданной вершины [math]s[/math] до всех остальных. Позволяет работать с ребрами отрицательного веса при отсутствии отрицательных циклов.

Алгоритм

Пусть [math]d_i[/math] — текущая длина кратчайшего пути до вершины [math]i[/math]. Изначально, все элементы [math]d[/math], кроме [math]s[/math]-го равны бесконечности; [math]d[s] = 0[/math].

Разделим вершины на три множества:

  • [math]M_0[/math] — вершины, расстояние до которых уже вычислено (возможно, не окончательно)
  • [math]M_1[/math] — вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на две очереди:
  1. [math]M_1^{'}[/math] — основная очередь
  2. [math]M_1^{''}[/math] — срочная очередь
  • [math]M_2[/math] — вершины, расстояние до которых еще не вычислено

Изначально все вершины, кроме [math]s[/math] помещаются в множество [math]M_2[/math]. Вершина [math]s[/math] помещается в множество [math]M_1[/math] (в любую из очередей).

Шаг алгоритма: выбирается вершина [math]u[/math] из [math]M_1[/math]. Если очередь [math]M_1^{''}[/math] не пуста, то вершина берется из нее, иначе из [math]M_1^{'}[/math]. Для каждого ребра [math]uv \in E[/math] возможны три случая:

  • [math]v \in M_2[/math], то [math]v[/math] переводится в конец очереди [math]M_1^{'}[/math]. При этом [math]d_v \gets d_u + w_{uv}[/math] (производится релаксация ребра [math]uv[/math])
  • [math]v \in M_1[/math], то происходит релаксация ребра [math]uv[/math]
  • [math]v \in M_0[/math] и [math]d_v \gt d_u + w_{uv}[/math], то происходит релаксация ребра [math]uv[/math] и [math]v[/math] помещается в [math]M_1^{''}[/math].

В конце шага помещаем вершину [math]u[/math] в множество [math]M_0[/math]

Алгоритм заканчивает работу, когда множество [math]M_1[/math] становится пустым.

Псевдокод

for [math]u : u \in V[/math]
   [math]d[u] = \infty[/math]
[math]d[s] = 0[/math]
[math]M_1^{'}[/math].push([math]s[/math])
for [math]u : u \neq s[/math] and [math]u \in V[/math]
   [math]M_2[/math].push([math]u[/math])
while [math]M_1^{'} \neq \varnothing[/math] and [math]M_1^{''} \neq \varnothing[/math]
   int [math]u[/math]
   if [math]M_1^{''} \neq \varnothing[/math]
      [math]u = M_1^{''}[/math].pop()
   else
      [math]u = M_1{'}[/math].pop()
   for [math]v : uv \in E[/math]
      if [math]v \in M_2[/math]
         [math]M_1^{'}[/math].push([math]v[/math])
         [math]M_2[/math].remove([math]v[/math])
         [math]d[v] =[/math] min([math]d[v], d[u] + w_{uv}[/math])
      else if v [math]\in M_1[/math]
         [math]d[v] =[/math] min([math]d[v], d[u] + w_{uv}[/math])
      else if [math]v \in M_0[/math] and [math]d[v] \gt  d[u] + w_{uv}[/math]
         [math]M_1^{''}[/math].push([math]v[/math])
         [math]M_0[/math].remove([math]v[/math])
         [math]d[v] = d[u] + w_{uv}[/math]
   [math]M_0[/math].push([math]u[/math])

Доказательство

Лемма:
Алгоритм отработает за конечное время
Доказательство:
[math]\triangleright[/math]
Не теряя общности, будем считать, что граф связен. Тогда алгоритм завершит работу, когда в [math]M_0[/math] окажутся все вершины. Так как в исходном графе нет отрицательных циклов, то для каждой вершины существует кратчайший путь. Тогда расстояние до каждой вершины может уменьшится только конечное число раз и, как следствие, вершина будет переведена из [math]M_0[/math] в [math]M_1[/math] тоже конечное число раз. С другой стороны, на каждом шаге текущая вершина гарантированно помещается в [math]M_0[/math]. Тогда за конечное число шагов все вершины окажутся в [math]M_0[/math].
[math]\triangleleft[/math]
Лемма:
В конце работы алгоритма не найдется такое ребро [math]uv[/math], что его релаксация будет успешной
Доказательство:
[math]\triangleright[/math]

Предположим обратное. Тогда рассмотрим 2 случая:

  1. Вершина [math]u[/math] попала в [math]M_0[/math] позже [math]v[/math]. Тогда должна была произойти релаксация ребра [math]uv[/math] и она была неуспешной. Значит, такого варианта не может быть
  2. Вершина [math]u[/math] попала в [math]M_0[/math] раньше [math]v[/math]. Заметим, что с момента последнего попадания [math]u[/math] в [math]M_0[/math] расстояние до нее не менялось (иначе, вершина была бы извлечена из [math]M_0[/math]). Вес ребра [math]uv[/math] тоже не меняется. Значит, и релаксация ребра [math]uv[/math] ничего не даст
Противоречие.
[math]\triangleleft[/math]

Из двух предыдущих лемм напрямую следует корректность алгоритма.

Сложность

В худшем случае алгоритм Левита работает за экспоненциальное время. Например, в полном графе [math]K_n[/math] с достаточно большим n и весами [math]w_{uv} = \begin{cases} v - u - 1, & u \gt 1; u = 1 и v = n\\ w_{un}*2, & u = 1, v = n - 1 \\ w_{uv+1} + v - u, & u = 1, v \lt n-1 \end{cases}[/math], рёбра идут в лексикографическом порядке. Добавление [math]i[/math]-ой вершины в очередь и последующая её обработка влекут добавление из [math]M_0[/math] в [math]M_1{''}[/math] всех её предыдущих вершин, начиная со [math]2[/math]-ой; дойдя до вершины [math]i-1[/math], в [math]M_1{''}[/math] снова добавятся вершины меньше [math]i-1[/math]-ой (кроме первой). Таким образом, количество добавлений [math]i[/math]-ой вершины в [math]M_1{''}[/math] составит [math]\sum\limits_{k=1}^{n-i} k[/math] раз. Однако, на реальных графах алгоритм Левита работает быстрее, чем алгоритм Форда Беллмана и не многим хуже алгоритма Дейкстры.

См. также

Источники