Алгоритм Левита — различия между версиями
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.