Алгоритм Левита — различия между версиями
Lehanyich (обсуждение | вклад) |
Lehanyich (обсуждение | вклад) |
||
Строка 6: | Строка 6: | ||
Разделим вершины на три множества: | Разделим вершины на три множества: | ||
− | * <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> {{---}} срочная очередь; |
− | * <tex>M_2</tex> {{---}} вершины, расстояние до которых еще не вычислено | + | * <tex>M_2</tex> {{---}} вершины, расстояние до которых еще не вычислено. |
Изначально все вершины, кроме <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> становится пустым. | ||
Строка 68: | Строка 68: | ||
== Сложность == | == Сложность == | ||
− | В | + | В плохих случаях алгоритм Левита работает очень долго. Например, в полном графе <tex>K_n</tex> с достаточно большим <tex>n</tex> и весами <tex>w_{uv} = |
\begin{cases} | \begin{cases} | ||
− | v - u - 1, & u > 1 | + | v - u - 1, & u > 1\\ |
− | + | w_{un}\cdot2, & u = 1, v = n - 1 \\ | |
+ | n-2, & u = 1, v = n\\ | ||
w_{uv+1} + v - u, & u = 1, v < n-1 | w_{uv+1} + v - u, & u = 1, v < n-1 | ||
− | \end{cases}</tex>, рёбра идут в [[Лексикографический порядок|лексикографическом порядке]]. Добавление <tex>i</tex> | + | \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 = \dfrac 12\cdot(n-i)\cdot(n-i+1)</tex> раз, всего в <tex>M_1{''}</tex> будет <tex>\dfrac 16\cdot n\cdot(n^2-3n+2)</tex> добавлений. В очень плохих случаях вершина <tex>i</tex> может помещаться в очередь <tex>M_1{''}</tex> до <tex>2^{n-i}</tex> раз, что даёт экспоненциальное время. Однако, на реальных графах алгоритм Левита работает быстрее, чем алгоритм [[Алгоритм Форда-Беллмана|Форда Беллмана]] и не многим уступает алгоритму [[Алгоритм Дейкстры|Дейкстры]]. |
==См. также== | ==См. также== |
Версия 18:52, 7 ноября 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 случая:
|
Из двух предыдущих лемм напрямую следует корректность алгоритма.
Сложность
В плохих случаях алгоритм Левита работает очень долго. Например, в полном графе лексикографическом порядке. Добавление вершины в очередь и последующая её обработка влекут добавление из в всех её предыдущих вершин, начиная со -ой; дойдя до вершины , в снова добавятся вершины меньше (кроме первой). Таким образом, вершину добавят в раз, всего в будет добавлений. В очень плохих случаях вершина может помещаться в очередь до раз, что даёт экспоненциальное время. Однако, на реальных графах алгоритм Левита работает быстрее, чем алгоритм Форда Беллмана и не многим уступает алгоритму Дейкстры.
с достаточно большим и весами , рёбра идут вСм. также
- Алгоритм A*
- Алгоритм Дейкстры
- Алгоритм Джонсона
- Алгоритм Флойда
- Алгоритм Форда-Беллмана
- Обход в ширину
Источники
- Википедия — Алгоритм Левита
- e-maxx — Алгоритм Левита
- И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., 4 издание, стр. 229-231.