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