Алгоритм Левита — различия между версиями
| Lehanyich (обсуждение | вклад) | Lehanyich (обсуждение | вклад)  | ||
| Строка 11: | Строка 11: | ||
| # <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> (в любую из очередей). | ||
| Строка 28: | Строка 23: | ||
| == Псевдокод == | == Псевдокод == | ||
| + | |||
| + | Для хранения вершин используем следующие структуры данных: | ||
| + | * <tex>M_0</tex> {{---}} хэш-сет, | ||
| + | * <tex>M_1</tex> {{---}} основная и срочная [[Очередь|очереди]], | ||
| + | * <tex>M_2</tex> {{---}} хэш-сет. | ||
|   '''for''' <tex>u : u \in V</tex> |   '''for''' <tex>u : u \in V</tex> | ||
| Строка 71: | Строка 71: | ||
| В плохих случаях алгоритм Левита работает за экспоненциальное время. Рассмотрим граф с <tex>3n</tex> вершинами и такими рёбрами: | В плохих случаях алгоритм Левита работает за экспоненциальное время. Рассмотрим граф с <tex>3n</tex> вершинами и такими рёбрами: | ||
| * ребро <tex>(1,</tex> <tex>2n + 1)</tex> веса <tex>2^{n/2}</tex>, | * ребро <tex>(1,</tex> <tex>2n + 1)</tex> веса <tex>2^{n/2}</tex>, | ||
| − | * для нечётных вершин <tex>i</tex> : <tex> | + | * для нечётных вершин <tex>i</tex> : <tex>2n+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>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>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>3</tex>, в которых два ребра нулевого веса. Каждый такой цикл увеличит количество добавлений следующих двух вершин в <tex>M_1{''}</tex> на <tex>1</tex>. Алгоритм будет часто пересчитывать расстояние до последних вершин, так как их часто добавляли в <tex>M_1{''}</tex>, что даёт экспоненциальное время. Однако, на реальных графах алгоритм Левита работает быстрее, чем алгоритм [[Алгоритм Форда-Беллмана|Форда Беллмана]] и не многим уступает алгоритму [[Алгоритм Дейкстры|Дейкстры]]. | 
| ==См. также== | ==См. также== | ||
Версия 22:28, 12 ноября 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.
