Алгоритм Левита — различия между версиями
Lehanyich (обсуждение | вклад) |
Lehanyich (обсуждение | вклад) |
||
| Строка 11: | Строка 11: | ||
# <tex>M_1^{''}</tex> {{---}} срочная очередь; | # <tex>M_1^{''}</tex> {{---}} срочная очередь; | ||
* <tex>M_2</tex> {{---}} вершины, расстояние до которых еще не вычислено. | * <tex>M_2</tex> {{---}} вершины, расстояние до которых еще не вычислено. | ||
| + | |||
| + | Для хранения вершин используем следующие структуры данных: | ||
| + | * <tex>M_0</tex> {{---}} хэш-сет, | ||
| + | * <tex>M_1</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> (в любую из очередей). | ||
| Строка 64: | Строка 69: | ||
== Сложность == | == Сложность == | ||
| − | В плохих случаях алгоритм Левита работает за экспоненциальное время. Рассмотрим граф с <tex> | + | В плохих случаях алгоритм Левита работает за экспоненциальное время. Рассмотрим граф с <tex>3n</tex> вершинами и такими рёбрами: |
| + | * ребро <tex>(1,</tex> <tex>2n + 1)</tex> веса <tex>2^{n/2}</tex>, | ||
| + | * для нечётных вершин <tex>i</tex> : <tex>2 \cdot n+1 \leqslant i \leqslant 3n</tex> идёт ребро <tex>(i,i+2)</tex> веса <tex>2^{(3n-i)/2}</tex>, | ||
| + | * для вершин <tex>i</tex>: <tex>1 \leqslant i < 3n</tex> идёт ребро <tex>(i,i+1)</tex> веса <tex>0</tex>. | ||
| + | Ясно, что кратчайший путь до каждой вершины равен <tex>0</tex>, но в плохом случае алгоритм будет часто пересчитывать последние вершины. На 1 шаге в очередь положат две вершины: с номером <tex>2</tex> (после нескольких шагов вершинам от <tex>2</tex> до <tex>2n + 1</tex> алгоритм сделает веса равными <tex>0</tex>) и с номером <tex>2n + 1</tex> (через такое же число шагов вершинам от <tex>2n + 2</tex> до <tex>3n</tex> будет задан вес <tex>2^{n/2}</tex>). Оставшиеся <tex>n-2</tex> вершины находятся в очереди <tex>M_0</tex>, алгоритм начнёт часто пересчитывать их вес, что приведёт к их многократному добавлению в <tex>M_1{''}</tex>, что даёт экспоненциальное время. Однако, на реальных графах алгоритм Левита работает быстрее, чем алгоритм [[Алгоритм Форда-Беллмана|Форда Беллмана]] и не многим уступает алгоритму [[Алгоритм Дейкстры|Дейкстры]]. | ||
==См. также== | ==См. также== | ||
Версия 17:10, 8 ноября 2015
Алгоритм Левита (англ. Levit's algorithm) находит расстояние от заданной вершины до всех остальных. Позволяет работать с ребрами отрицательного веса при отсутствии отрицательных циклов.
Алгоритм
Пусть — текущая длина кратчайшего пути до вершины . Изначально, все элементы , кроме -го равны бесконечности; .
Разделим вершины на три множества:
- — вершины, расстояние до которых уже вычислено (возможно, не окончательно),
- — вершины, расстояние до которых вычисляется. Это множество в свою очередь делится на две очереди:
- — основная очередь,
- — срочная очередь;
- — вершины, расстояние до которых еще не вычислено.
Для хранения вершин используем следующие структуры данных:
- — хэш-сет,
- — основная и срочная очереди,
- — хэш-сет.
Изначально все вершины, кроме помещаются в множество . Вершина помещается в множество (в любую из очередей).
Шаг алгоритма: выбирается вершина из . Если очередь не пуста, то вершина берется из нее, иначе из . Для каждого ребра возможны три случая:
- , то переводится в конец очереди . При этом (производится релаксация ребра ),
- , то происходит релаксация ребра ,
- . Если при этом , то происходит релаксация ребра и вершина помещается в ; иначе ничего не делаем.
В конце шага помещаем вершину в множество .
Алгоритм заканчивает работу, когда множество становится пустым.
Псевдокод
for .push() for and .add() while and .pop() .pop() for if .push() .remove() min() else if min() else if and .push() .remove() .add()
Доказательство
| Лемма: |
Алгоритм отработает за конечное время |
| Доказательство: |
| Не теряя общности, будем считать, что граф связен. Тогда алгоритм завершит работу, когда в окажутся все вершины. Так как в исходном графе нет отрицательных циклов, то для каждой вершины существует кратчайший путь. Тогда расстояние до каждой вершины может уменьшится только конечное число раз и, как следствие, вершина будет переведена из в тоже конечное число раз. С другой стороны, на каждом шаге текущая вершина гарантированно помещается в . Тогда за конечное число шагов все вершины окажутся в . |
| Лемма: |
В конце работы алгоритма не найдется такое ребро , что его релаксация будет успешной |
| Доказательство: |
|
Предположим обратное. Тогда рассмотрим 2 случая:
|
Из двух предыдущих лемм напрямую следует корректность алгоритма.
Сложность
В плохих случаях алгоритм Левита работает за экспоненциальное время. Рассмотрим граф с вершинами и такими рёбрами:
- ребро веса ,
- для нечётных вершин : идёт ребро веса ,
- для вершин : идёт ребро веса .
Ясно, что кратчайший путь до каждой вершины равен , но в плохом случае алгоритм будет часто пересчитывать последние вершины. На 1 шаге в очередь положат две вершины: с номером (после нескольких шагов вершинам от до алгоритм сделает веса равными ) и с номером (через такое же число шагов вершинам от до будет задан вес ). Оставшиеся вершины находятся в очереди , алгоритм начнёт часто пересчитывать их вес, что приведёт к их многократному добавлению в , что даёт экспоненциальное время. Однако, на реальных графах алгоритм Левита работает быстрее, чем алгоритм Форда Беллмана и не многим уступает алгоритму Дейкстры.
См. также
- Алгоритм A*
- Алгоритм Дейкстры
- Алгоритм Джонсона
- Алгоритм Флойда
- Алгоритм Форда-Беллмана
- Обход в ширину
Источники информации
- Википедия — Алгоритм Левита
- MAXimal :: algo :: Алгоритм Левита
- И. В. Романовский, Дискретный анализ, ISBN 5-7940-0138-0; 2008 г., 4 издание, стр. 229-231.