Сорт | Задача нахождения кратчайшего пути для всех пар вершин (для взвешенных графов) |
---|---|
Структура данных | График |
Худший вариант производительности | |
Лучшая производительность | |
Средняя производительность | |
Наихудшая сложность пространства |
В информатике алгоритм Флойда–Уоршелла (также известный как алгоритм Флойда , алгоритм Роя–Уоршелла , алгоритм Роя–Флойда или алгоритм WFI ) — это алгоритм поиска кратчайших путей в ориентированном взвешенном графе с положительными или отрицательными весами ребер (но без отрицательных циклов). [1] [2] Однократное выполнение алгоритма найдет длины (суммарные веса) кратчайших путей между всеми парами вершин. Хотя он не возвращает детали самих путей, можно реконструировать пути с помощью простых модификаций алгоритма. Версии алгоритма также могут быть использованы для поиска транзитивного замыкания отношения или (в связи с системой голосования Шульце ) широчайших путей между всеми парами вершин во взвешенном графе.
Алгоритм Флойда–Уоршалла является примером динамического программирования и был опубликован в его нынешней признанной форме Робертом Флойдом в 1962 году. [3] Однако, по сути, он такой же, как алгоритмы, ранее опубликованные Бернардом Роем в 1959 году [4] , а также Стивеном Уоршаллом в 1962 году [5] для нахождения транзитивного замыкания графа, [6] и тесно связан с алгоритмом Клини (опубликованным в 1956 году) для преобразования детерминированного конечного автомата в регулярное выражение . [7] Современная формулировка алгоритма в виде трех вложенных циклов for была впервые описана Питером Ингерманом также в 1962 году. [8]
Алгоритм Флойда–Уоршелла сравнивает множество возможных путей по графу между каждой парой вершин. Он гарантированно находит все кратчайшие пути и может делать это с помощью сравнений в графе, [1] [9] даже если в графе могут быть ребра. Он делает это путем постепенного улучшения оценки кратчайшего пути между двумя вершинами, пока оценка не станет оптимальной.
Рассмотрим граф с вершинами, пронумерованными от 1 до . Далее рассмотрим функцию , которая возвращает длину кратчайшего возможного пути (если он существует) от до , используя вершины только из множества в качестве промежуточных точек на пути. Теперь, учитывая эту функцию, наша цель — найти длину кратчайшего пути от каждого до каждого , используя любую вершину из . По определению, это значение , которое мы найдем рекурсивно .
Обратите внимание, что должно быть меньше или равно : у нас больше гибкости, если нам разрешено использовать вершину . Если на самом деле меньше , то должен быть путь от до с использованием вершин , который короче любого такого пути, не использующего вершину . Поскольку нет отрицательных циклов, этот путь можно разложить следующим образом:
И, конечно, это должен быть кратчайший такой путь (или несколько таких), иначе мы могли бы еще больше уменьшить длину. Другими словами, мы пришли к рекурсивной формуле:
Базовый вариант определяется как
где обозначает вес ребра от до , если оно существует, и ∞ (бесконечность) в противном случае.
Эти формулы являются сердцем алгоритма Флойда–Уоршелла. Алгоритм работает, сначала вычисляя для всех пар для , затем , затем и так далее. Этот процесс продолжается до , и мы находим кратчайший путь для всех пар с использованием любых промежуточных вершин. Ниже приведен псевдокод для этой базовой версии.
пусть dist будет массивом минимальных расстояний размером |V| × |V|, инициализированным значением ∞ (бесконечность) для каждого ребра ( u , v ) do dist[ u ][ v ] ← w( u , v ) // Вес ребра ( u , v ) для каждой вершины v do dist[ v ][ v ] ← 0 для k от 1 до |V| для i от 1 до |V| для j от 1 до |V| if dist[ i ][ j ] > dist[ i ][ k ] + dist[ k ][ j ] dist[ i ][ j ] ← dist[ i ][ k ] + dist[ k ][ j ] конец если
Приведенный выше алгоритм реализован на графике слева ниже:
До первой рекурсии внешнего цикла, обозначенного выше как k = 0 , единственные известные пути соответствуют одиночным ребрам в графе. При k = 1 находятся пути, проходящие через вершину 1: в частности, находится путь [2,1,3], заменяющий путь [2,3], который имеет меньше ребер, но длиннее (с точки зрения веса). При k = 2 находятся пути, проходящие через вершины {1,2}. Красные и синие поля показывают, как путь [4,2,1,3] собирается из двух известных путей [4,2] и [2,1,3], встреченных в предыдущих итерациях, с 2 в пересечении. Путь [4,2,3] не рассматривается, поскольку [2,1,3] является кратчайшим путем, встреченным до сих пор от 2 до 3. При k = 3 находятся пути, проходящие через вершины {1,2,3}. Наконец, при k = 4 находятся все кратчайшие пути.
Матрица расстояний на каждой итерации k , где обновленные расстояния выделены жирным шрифтом , будет иметь вид:
к = 0 | дж | ||||
1 | 2 | 3 | 4 | ||
---|---|---|---|---|---|
я | 1 | 0 | ∞ | −2 | ∞ |
2 | 4 | 0 | 3 | ∞ | |
3 | ∞ | ∞ | 0 | 2 | |
4 | ∞ | −1 | ∞ | 0 |
к = 1 | дж | ||||
1 | 2 | 3 | 4 | ||
---|---|---|---|---|---|
я | 1 | 0 | ∞ | −2 | ∞ |
2 | 4 | 0 | 2 | ∞ | |
3 | ∞ | ∞ | 0 | 2 | |
4 | ∞ | −1 | ∞ | 0 |
к = 2 | дж | ||||
1 | 2 | 3 | 4 | ||
---|---|---|---|---|---|
я | 1 | 0 | ∞ | −2 | ∞ |
2 | 4 | 0 | 2 | ∞ | |
3 | ∞ | ∞ | 0 | 2 | |
4 | 3 | −1 | 1 | 0 |
к = 3 | дж | ||||
1 | 2 | 3 | 4 | ||
---|---|---|---|---|---|
я | 1 | 0 | ∞ | −2 | 0 |
2 | 4 | 0 | 2 | 4 | |
3 | ∞ | ∞ | 0 | 2 | |
4 | 3 | −1 | 1 | 0 |
к = 4 | дж | ||||
1 | 2 | 3 | 4 | ||
---|---|---|---|---|---|
я | 1 | 0 | −1 | −2 | 0 |
2 | 4 | 0 | 2 | 4 | |
3 | 5 | 1 | 0 | 2 | |
4 | 3 | −1 | 1 | 0 |
Отрицательный цикл — это цикл, сумма ребер которого дает отрицательное значение. Не существует кратчайшего пути между любой парой вершин , которые образуют часть отрицательного цикла, поскольку длины путей от до могут быть сколь угодно малыми (отрицательными). Для численно значимых выходных данных алгоритм Флойда–Уоршелла предполагает, что отрицательных циклов нет. Тем не менее, если есть отрицательные циклы, алгоритм Флойда–Уоршелла может быть использован для их обнаружения. Интуиция такова:
Следовательно, для обнаружения отрицательных циклов с помощью алгоритма Флойда–Уоршелла можно проверить диагональ матрицы пути, и наличие отрицательного числа указывает на то, что граф содержит по крайней мере один отрицательный цикл. [9] Во время выполнения алгоритма, если есть отрицательный цикл, могут появляться экспоненциально большие числа, такие как , где — наибольшее абсолютное значение отрицательного ребра в графе. Чтобы избежать проблем переполнения/незаполнения, следует проверять наличие отрицательных чисел на диагонали матрицы пути во внутреннем цикле for алгоритма. [10] Очевидно, что в неориентированном графе отрицательное ребро создает отрицательный цикл (т. е. замкнутый обход), включающий его инцидентные вершины. Рассматривая все ребра приведенного выше примера графа как неориентированные, например, последовательность вершин 4 – 2 – 4 представляет собой цикл с суммой весов −2.
Алгоритм Флойда–Уоршелла обычно предоставляет только длины путей между всеми парами вершин. С помощью простых модификаций можно создать метод для реконструкции фактического пути между любыми двумя конечными вершинами. Хотя кто-то может быть склонен хранить фактический путь от каждой вершины до каждой другой вершины, это не обязательно и, по сути, очень затратно с точки зрения памяти. Вместо этого мы можем использовать дерево кратчайших путей , которое может быть вычислено для каждого узла со временем с использованием памяти и позволяет нам эффективно реконструировать направленный путь между любыми двумя связанными вершинами.
Массив prev[u][v]
содержит предпоследнюю вершину на пути от u
до v
(за исключением случая prev[v][v]
, где он всегда содержит , v
даже если нет замкнутого цикла на v
): [11]
пусть dist будет массивом минимальных расстояний, инициализированным (бесконечностью) , пусть prev будет массивом индексов вершин, инициализированным значением nullпроцедура FloydWarshallWithPathReconstruction () для каждого ребра (u, v) do dist[u][v] ← w(u, v) // Вес ребра (u, v) предыдущая[u][v] ← u для каждой вершины v сделать расст[v][v] ← 0 предыдущая[v][v] ← v для k от 1 до |V| do // стандартная реализация Флойда-Уоршелла для i от 1 до |V| для j от 1 до |V| if dist[i][j] > dist[i][k] + dist[k][j] then расстояние[i][j] ← расстояние[i][k] + расстояние[k][j] предыдущая[i][j] ← предыдущая[k][j]
Процедура Path (u, v) — если prev[u][v] = null , то возвращается [] путь ← [v] пока u ≠ v делать v ← предыдущая[u][v] путь.prepend(v) обратный путь
Пусть будет , число вершин. Чтобы найти все ( для всех и ) из тех, что требуют операций. Поскольку мы начинаем с и вычисляем последовательность матриц , , , , каждая из которых имеет стоимость , общая временная сложность алгоритма составляет .
Алгоритм Флойда–Уоршелла можно использовать для решения, среди прочего, следующих задач:
Реализации доступны для многих языков программирования .
Для графов с неотрицательными весами ребер алгоритм Дейкстры может быть использован для поиска всех кратчайших путей из одной вершины со временем выполнения . Таким образом, запуск Дейкстры, начиная с каждой вершины, занимает время . Поскольку , это дает наихудшее время выполнения повторного Дейкстры . Хотя это соответствует асимптотическому наихудшему времени выполнения алгоритма Флойда-Уоршелла, задействованные константы имеют довольно большое значение. Когда граф плотный (т. е. ), алгоритм Флойда-Уоршелла имеет тенденцию работать лучше на практике. Когда граф разрежен (т. е. значительно меньше ), Дейкстра имеет тенденцию доминировать.
Для разреженных графов с отрицательными ребрами, но без отрицательных циклов можно использовать алгоритм Джонсона с тем же асимптотическим временем выполнения, что и повторный подход Дейкстры.
Известны также алгоритмы, использующие быстрое умножение матриц для ускорения вычисления кратчайшего пути для всех пар вершин в плотных графах, но они обычно делают дополнительные предположения о весах ребер (например, требуют, чтобы они были небольшими целыми числами). [15] [16] Кроме того, из-за высоких постоянных множителей во времени их выполнения они обеспечат ускорение по сравнению с алгоритмом Флойда–Уоршелла только для очень больших графов.
{{cite journal}}
: Цитировать журнал требует |journal=
( помощь )