Алгоритм Флойда — различия между версиями
м (rollbackEdits.php mass rollback) |
|||
(не показаны 2 промежуточные версии 2 участников) | |||
Строка 114: | Строка 114: | ||
'''if''' d[u][i] + d[i][v] < d[u][v] | '''if''' d[u][i] + d[i][v] < d[u][v] | ||
d[u][v] = d[u][i] + d[i][v] | d[u][v] = d[u][i] + d[i][v] | ||
− | next[u][v] = i | + | next[u][v] = next[u][i] |
'''func''' getShortestPath(u, v)''':''' | '''func''' getShortestPath(u, v)''':''' |
Текущая версия на 19:39, 4 сентября 2022
Алгоритм Флойда (алгоритм Флойда–Уоршелла) — алгоритм нахождения длин кратчайших путей между всеми парами вершин во взвешенном ориентированном графе. Работает корректно, если в графе нет циклов отрицательной величины, а в случае, когда такой цикл есть, позволяет найти хотя бы один такой цикл. Алгоритм работает за
времени и использует памяти. Разработан в 1962 году.Содержание
Алгоритм
Постановка задачи
Дан взвешенный ориентированный граф , в котором вершины пронумерованы от до .
Требуется найти матрицу кратчайших расстояний , в которой элемент либо равен длине кратчайшего пути из в , либо равен , если вершина не достижима из .
Описание
Обозначим длину кратчайшего пути между вершинами
и , содержащего, помимо и , только вершины из множества как , .На каждом шаге алгоритма, мы будем брать очередную вершину (пусть её номер —
) и для всех пар вершин и вычислять . То есть, если кратчайший путь из в , содержащий только вершины из множества , проходит через вершину , то кратчайшим путем из в является кратчайший путь из в , объединенный с кратчайшим путем из в . В противном случае, когда этот путь не содержит вершины , кратчайший путь из в , содержащий только вершины из множества является кратчайшим путем из в , содержащим только вершины из множества .Код (в первом приближении)
for for for
В итоге получаем, что матрица
и является искомой матрицей кратчайших путей, поскольку содержит в себе длины кратчайших путей между всеми парами вершин, имеющих в качестве промежуточных вершин вершины из множества , что есть попросту все вершины графа. Такая реализация работает за времени и использует памяти.Код (окончательный)
Утверждается, что можно избавиться от одной размерности в массиве
, т.е. использовать двумерный массив . В процессе работы алгоритма поддерживается инвариант , а, поскольку, после выполнения работы алгоритма , то тогда будет выполняться и .Утверждение: |
В течение работы алгоритма Флойда выполняются неравенства: . |
После инициализации все неравенства, очевидно, выполняются. Далее, массив может измениться только в строчке 5.Докажем второе неравенство индукцией по итерациям алгоритма. Пусть также — значение сразу после итерации.Покажем, что , зная, что .Рассмотрим два случая:
Пусть неравенство было нарушено, рассмотрим момент, когда оно было нарушено впервые. Пусть это была Итак -ая итерация и в этот момент изменилось значение и выполнилось . Так как изменилось, то (так как ранее ) (по неравенству треугольника) . — противоречие. |
func floyd(w): d =// изначально for for for d[u][v] = min(d[u][v], d[u][i] + d[i][v])
Данная реализация работает за время
, но требует уже памяти. В целом, алгоритм Флойда очень прост, и, поскольку в нем используются только простые операции, константа, скрытая в определении весьма мала.Пример работы
![]() |
![]() |
![]() |
![]() |
![]() |
Вывод кратчайшего пути
Алгоритм Флойда легко модифицировать таким образом, чтобы он возвращал не только длину кратчайшего пути, но и сам путь. Для этого достаточно завести дополнительный массив
, в котором будет храниться номер вершины, в которую надо пойти следующей, чтобы дойти из в по кратчайшему пути.Модифицированный алгоритм
func floyd(w): d =// изначально for for for if d[u][i] + d[i][v] < d[u][v] d[u][v] = d[u][i] + d[i][v] next[u][v] = next[u][i]
func getShortestPath(u, v):
if d[u][v] ==
print "No path found" // между вершинами u и v нет пути
c = u
while c != v
print c
c = next[c][v]
print v
Нахождение отрицательного цикла
Утверждение: |
При наличии цикла отрицательного веса в матрице появятся отрицательные числа на главной диагонали. |
Так как алгоритм Флойда последовательно релаксирует расстояния между всеми парами вершин | , в том числе и теми, у которых , а начальное расстояние между парой вершин равно нулю, то релаксация может произойти только при наличии вершины такой, что , что эквивалентно наличию отрицательного цикла, проходящего через вершину .
Из доказательства следует, что для поиска цикла отрицательного веса необходимо, после завершения работы алгоритма, найти вершину
, для которой , и вывести кратчайший путь между парой вершин . При этом стоит учитывать, что при наличии отрицательного цикла расстояния могут уменьшаться экспоненциально. Для предотвращения переполнения все вычисления стоит ограничивать снизу величиной , либо проверять наличие отрицательных чисел на главной диагонали во время подсчета.Построение транзитивного замыкания
Сформулируем нашу задачу в терминах графов: рассмотрим граф
, соответствующий отношению . Тогда необходимо найти все пары вершин , соединенных некоторым путем. Иными словами, требуется построить новое отношение , которое будет состоять из всех пар таких, что найдется последовательность , где .Псевдокод
Изначально матрица
заполняется соответственно отношению , то есть . Затем внешним циклом перебираются все элементы множества и для каждого из них, если он может использоваться, как промежуточный для соединения двух элементов и , отношение расширяется добавлением в него пары .for k = 1 to n for i = 1 to n for j = 1 to n W[i][j] = W[i][j] or (W[i][k] and W[k][j])
Доказательство
<wikitex>Назовем промежуточной вершину некоторого пути $p = \left \langle v_0, v_1, \dots, v_k \right \rangle$, принадлежащую множеству вершин этого пути и отличающуюся от начальной и конечной вершин, то есть принадлежащую $\left \{ v_1, v_2, \dots, v_{k-1} \right \}$. Рассмотрим произвольную пару вершин $i, j \in V$ и все пути между ними, промежуточные вершины которых принадлежат множеству вершин с номерами $\left \{ 1, 2, \dots, k \right \}$. Пусть $p$ — некоторый из этих путей. Докажем по индукции (по числу промежуточных вершин в пути), что после $i$-ой итерации внешнего цикла будет верно утверждение — если в построенном графе между выбранной парой вершин есть путь, содержащий в качестве промежуточных только вершины из множества вершин с номерами $\left \{ v_1, v_2, \dots, v_{i} \right \}$, то между ними будет ребро.
- База индукции. Если у нас нет промежуточных вершин, что соответствует начальной матрице смежности, то утверждение выполнено: либо есть ребро (путь не содержит промежуточных вершин), либо его нет.
- Индуктивный переход. Пусть предположение выполнено для $i = k - 1$. Докажем, что оно верно и для $i = k$ Рассмотрим случаи (далее под вершиной будем понимать ее номер для простоты изложения):
- $k$ не является промежуточной вершиной пути $p$. Тогда все его промежуточные пути принадлежат множеству вершин с номерами $\left \{ 1, 2, \dots, k-1 \right \} \subset \left \{ 1, 2, \dots, k \right \}$, то есть существует путь с промежуточными вершинами в исходном множестве. Это значит $W[i][j]$ будет истиной. В противном случае $W[i][j]$ будет ложью и на k-ом шаге ею и останется.
- $k$ является промежуточной вершиной предполагаемого пути $p$. Тогда этот путь можно разбить на два пути: $i \xrightarrow{p_1} k \xrightarrow{p_2} j$. Пусть как $p_1$, так и $p_2$ существуют. Тогда они содержат в качестве промежуточных вершины из множества $\left \{ 1, 2, \dots, k-1 \right \} \subset \left \{ 1, 2, \dots, k \right \}$ (так как вершина $k$ — либо конечная, либо начальная, то она не может быть в множестве по нашему определению). Тогда $W[i][k]$ и $W[k][j]$ истинны и по индуктивному предположению посчитаны верно. Тогда и $W[i][j]$ тоже истина. Пусть какого-то пути не существует. Тогда пути $p$ тоже не может существовать, так как добраться, например, от вершины $i$ до $k$ по вершинам из множества $\left \{ 1, 2, \dots, k \right \}$ невозможно по индуктивному предположению. Тогда вся конъюнкция будет ложной, то есть такого пути нет, откуда $W[i][j]$ после итерации будет ложью.
Таким образом, после завершения внешнего цикла у нас будет $W[i][j] = true$, если между этими вершинами есть путь, содержащий в качестве промежуточных вершин из множества всех остальных вершин графа, что и есть транзитивное замыкание. </wikitex>
Оптимизация с помощью битовых масок
Строки матрицы
Пример реализации оптимизации с помощью битмасок:
unsigned int W[N][N / 32 + 1] func transitiveClosure(W): for k = 1 to n for i = 1 to n if бит с номером (k % 32) в маске a[i][k / 32] единичный for j = 1 to n / 32 + 1 W[i][j] = W[i][j] or W[k][j]
В данной реализации длина битовой маски
равна битам. Последний цикл делает в раза меньше операций — сложность алгоритма .Источники информации
- Томас Х. Кормен, Чарльз И. Лейзерсон, Рональд Л. Ривест, Клиффорд Штайн Алгоритмы: построение и анализ — 2-е изд — М.: Издательский дом «Вильямс», 2009. — ISBN 978-5-8459-0857-5.
- Романовский И. В. Дискретный анализ: Учебное пособие для студентов, специализирующихся по прикладной математике и информатике. Изд. 3-е. — СПб.: Невский диалект, 2003. — 320 с. — ISBN 5-7940-0114-3.
- Википедия - Алгоритм Флойда — Уоршелла
- Wikipedia - Floyd–Warshall algorithm