Алгоритм Левита — различия между версиями
Lehanyich (обсуждение | вклад) |
Lehanyich (обсуждение | вклад) |
||
| Строка 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>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_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>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 | + | '''for''' <tex>u : u \in V</tex> |
| − | d[ | + | <tex>d[u] = \infty</tex> |
| − | d[s] | + | <tex>d[s] = 0</tex> |
| − | <tex>M_1^{'}</tex>.push(s) | + | <tex>M_1^{'}</tex>.push(<tex>s</tex>) |
| − | '''for''' u : u | + | '''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> | ||
| − | + | <tex>u = M_1^{''}</tex>.pop() | |
| − | '''else | + | '''else''' |
| − | + | <tex>u = M_1{'}</tex>.pop() | |
| − | '''for''' v : uv | + | '''for''' <tex>v : uv \in E</tex> |
| − | '''if''' | + | '''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>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>d[v] =</tex> min(<tex>d[v], d[u] + w_{uv}</tex>) |
| − | '''if''' | + | '''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>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} = |
\begin{cases} | \begin{cases} | ||
| − | + | v - u - 1, & u > 1; u = 1 и v = n\\ | |
| − | n - | + | w_{un}*2, & u = 1, v = n - 1 \\ |
| − | \end{cases}</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) находит расстояние от заданной вершины до всех остальных. Позволяет работать с ребрами отрицательного веса при отсутствии отрицательных циклов.
Алгоритм
Пусть — текущая длина кратчайшего пути до вершины . Изначально, все элементы , кроме -го равны бесконечности; .
Разделим вершины на три множества:
- — вершины, расстояние до которых уже вычислено (возможно, не окончательно)
- — вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на две очереди:
- — основная очередь
- — срочная очередь
- — вершины, расстояние до которых еще не вычислено
Изначально все вершины, кроме помещаются в множество . Вершина помещается в множество (в любую из очередей).
Шаг алгоритма: выбирается вершина из . Если очередь не пуста, то вершина берется из нее, иначе из . Для каждого ребра возможны три случая:
- , то переводится в конец очереди . При этом (производится релаксация ребра )
- , то происходит релаксация ребра
- и , то происходит релаксация ребра и помещается в .
В конце шага помещаем вершину в множество
Алгоритм заканчивает работу, когда множество становится пустым.
Псевдокод
for .push() for and .push() while and int if .pop() else .pop() for if .push() .remove() min() else if v min() else if and .push() .remove() .push()
Доказательство
| Лемма: |
Алгоритм отработает за конечное время |
| Доказательство: |
| Не теряя общности, будем считать, что граф связен. Тогда алгоритм завершит работу, когда в окажутся все вершины. Так как в исходном графе нет отрицательных циклов, то для каждой вершины существует кратчайший путь. Тогда расстояние до каждой вершины может уменьшится только конечное число раз и, как следствие, вершина будет переведена из в тоже конечное число раз. С другой стороны, на каждом шаге текущая вершина гарантированно помещается в . Тогда за конечное число шагов все вершины окажутся в . |
| Лемма: |
В конце работы алгоритма не найдется такое ребро , что его релаксация будет успешной |
| Доказательство: |
|
Предположим обратное. Тогда рассмотрим 2 случая:
|
Из двух предыдущих лемм напрямую следует корректность алгоритма.
Сложность
В худшем случае алгоритм Левита работает за экспоненциальное время. Например, в полном графе с достаточно большим n и весами , рёбра идут в лексикографическом порядке. Добавление -ой вершины в очередь и последующая её обработка влекут добавление из в всех её предыдущих вершин, начиная со -ой; дойдя до вершины , в снова добавятся вершины меньше -ой (кроме первой). Таким образом, количество добавлений -ой вершины в составит раз. Однако, на реальных графах алгоритм Левита работает быстрее, чем алгоритм Форда Беллмана и не многим хуже алгоритма Дейкстры.
См. также
- Алгоритм A*
- Алгоритм Дейкстры
- Алгоритм Джонсона
- Алгоритм Флойда
- Алгоритм Форда-Беллмана
- Обход в ширину
Источники
- Википедия — Алгоритм Левита
- e-maxx — Алгоритм Левита
- И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., 4 издание, стр. 229-231.