Участник:Qtr — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Псевдокод)
Строка 70: Строка 70:
 
     '''if''' BellmanFord<tex>(G',\;\omega,\;s)</tex> == ''false''
 
     '''if''' BellmanFord<tex>(G',\;\omega,\;s)</tex> == ''false''
 
         print "Входной граф содержит цикл с отрицательным весом"
 
         print "Входной граф содержит цикл с отрицательным весом"
         return <tex>\varnothing</tex>
+
         '''return''' <tex>\varnothing</tex>
        '''else''' '''for''' <tex>v \in V'</tex>
+
    '''else''' '''for''' <tex>v \in V'</tex>
            <tex>\varphi(v)</tex> = <tex>\delta(s,\;v)</tex> <font color = green>// <tex>\delta(s,\;v)</tex> вычислено алгоритмом Беллмана — Форда</font>
+
        <tex>\varphi(v)</tex> = <tex>\delta(s,\;v)</tex> <font color = green>// <tex>\delta(s,\;v)</tex> вычислено алгоритмом Беллмана — Форда</font>
            '''for''' <tex>(u,\;v) \in E'</tex>
+
        '''for''' <tex>(u,\;v) \in E'</tex>
                <tex>\omega_\varphi(u,\;v)</tex> = <tex> \omega(u,\;v) + \varphi(u) - \varphi(v)</tex>
+
            <tex>\omega_\varphi(u,\;v)</tex> = <tex> \omega(u,\;v) + \varphi(u) - \varphi(v)</tex>
            '''for''' <tex>u \in V</tex>
+
        '''for''' <tex>u \in V</tex>
                Dijkstra<tex>(G,\;\omega_\varphi,\;u)</tex>
+
            Dijkstra<tex>(G,\;\omega_\varphi,\;u)</tex>
                '''for''' <tex>v \in V</tex>
+
            '''for''' <tex>v \in V</tex>
                    <tex>d_{uv} \leftarrow \delta_\varphi(u,\;v) + \varphi(v) - \varphi(u)</tex>
+
                <tex>d_{uv} \leftarrow \delta_\varphi(u,\;v) + \varphi(v) - \varphi(u)</tex>
 
         '''return''' <tex>d</tex>
 
         '''return''' <tex>d</tex>
  
Итого, в начале алгоритм Форда Беллмана либо строит потенциальную функцию такую, что после перевзвешивания все веса ребер будут неотрицательны, либо выдает сообщение о том, что в графе присутствует отрицательный цикл.
+
Итого, в начале алгоритм Форда-Беллмана либо строит потенциальную функцию такую, что после перевзвешивания все веса ребер будут неотрицательны, либо выдает сообщение о том, что в графе присутствует отрицательный цикл.
  
 
Затем из каждой вершины запускается алгоритм Дейкстры для составления искомой матрицы. Так как все веса ребер теперь неотрицательны, алгоритм Дейкстры будет работать корректно. А поскольку перевзвешивание таково, что кратчайшие пути относительно обеих весовых функций совпадают, алгоритм Джонсона в итоге корректно найдет все кратчайшие пути между всеми парами вершин.
 
Затем из каждой вершины запускается алгоритм Дейкстры для составления искомой матрицы. Так как все веса ребер теперь неотрицательны, алгоритм Дейкстры будет работать корректно. А поскольку перевзвешивание таково, что кратчайшие пути относительно обеих весовых функций совпадают, алгоритм Джонсона в итоге корректно найдет все кратчайшие пути между всеми парами вершин.

Версия 23:43, 25 января 2016

Алгоритм Джонсона (англ. Johnson's algorithm) находит кратчайшие пути между всеми парами вершин во взвешенном ориентированном графе с любыми весами ребер, но не имеющем отрицательных циклов.

Алгоритм

Описание

Алгоритм Джонсона позволяет найти кратчайшие пути между всеми парами вершин в течение времени [math] O(V^2\log(V) + VE) [/math]. Для разреженных графов этот алгоритм ведет себя асимптотически быстрее алгоритма Флойда. Этот алгоритм либо возвращает матрицу кратчайших расстояний между всеми парами вершин, либо сообщение о том, что в графе существует цикл отрицательной длины.

В этом алгоритме используется метод изменения веса (англ. reweighting). Суть его заключается в том, что для заданного графа [math] G [/math] строится новая весовая функция [math] \omega_\varphi [/math], неотрицательная для всех ребер графа [math] G [/math] и сохраняющая кратчайшие пути. Такая весовая функция строится с помощью так называемой потенциальной функции.

Пусть [math] \varphi : V \rightarrow \mathbb R [/math] — произвольное отображение из множества вершин в вещественные числа. Тогда новой весовой функцией будет [math] \omega_\varphi(u, v) = \omega(u, v) + \varphi(u) - \varphi(v) [/math].

Такая потенциальная функция строится при помощи добавлении фиктивной вершины в [math] G [/math], из которой проведены ребра нулевого веса во все остальные вершины и запуском алгоритма Форда — Беллмана из нее. На этом же этапе мы сможем обнаружить наличие отрицательного цикла в графе.

Теперь, когда мы знаем, что веса всех ребер неотрицательны, и кратчайшие пути сохранятся, можно запустить алгоритм Дейкстры из каждой вершины и таким образом найти кратчайшие расстояния между всеми парами вершин.

Сохранение кратчайших путей

Утверждается, что если какой-то путь [math] P [/math] был кратчайшим относительно весовой функции [math] \omega [/math], то он будет кратчайшим и относительно новой весовой функции [math] \omega_\varphi [/math].

Лемма:
Пусть [math]P,\; Q [/math] — два пути [math] a \rightsquigarrow b\;[/math] и [math]\omega(P) \lt \omega(Q).[/math] Тогда [math]\forall \varphi: \; \omega_\varphi(P) \lt \omega_\varphi(Q)[/math]
Доказательство:
[math]\triangleright[/math]
Рассмотрим путь [math]P: \;u_0 \rightarrow u_1 \rightarrow u_2 \rightarrow \ldots \rightarrow u_k [/math]
Его вес с новой весовой функцией равен [math]\omega_\varphi(P) = \omega_\varphi(u_0u_1) + \omega_\varphi(u_1u_2) + \ldots + \omega_\varphi(u_{k-1}u_k) [/math].
Вставим определение функции [math] \omega_\varphi : \omega_\varphi(P) = \varphi(u_0) + \omega(u_0u_1) - \varphi(u_1) + \ldots + \varphi(u_{k-1}) + \omega(u_{k-1}u_k) - \varphi(u_k) [/math]
Заметим, что потенциалы все промежуточных вершин в пути сократятся. [math] \omega_\varphi(P) = \varphi(u_0) + \omega(P) - \varphi(u_k)[/math]
По изначальному предположению: [math]\omega(P) \lt \omega(Q)[/math]. С новой весовой функцией веса соответствующих путей будут:
[math]\omega_\varphi(P) = \varphi(a) + \omega(P) - \varphi(b)[/math]
[math]\omega_\varphi(Q) = \varphi(a) + \omega(Q) - \varphi(b)[/math]
Отсюда, [math]\omega_\varphi(P) \lt \omega_\varphi(Q)[/math]
[math]\triangleleft[/math]

Теорема о существовании потенциальной функции

Теорема:
В графе [math]G[/math] нет отрицательных циклов [math]\Leftrightarrow[/math] существует потенциальная функция [math] \phi:\; \forall uv \in E\; \omega_\varphi(uv) \geqslant 0 [/math]
Доказательство:
[math]\triangleright[/math]

[math]\Leftarrow [/math]: Рассмотрим произвольный [math]C[/math] — цикл в графе [math]G[/math]

По лемме, его вес равен [math] \omega(C) = \omega_\varphi(C) - \varphi(u_0) - \varphi(u_0) = \omega_\varphi(C) \geqslant 0[/math]

[math]\Rightarrow [/math]: Добавим фиктивную вершину [math]s[/math] в граф, а также ребра [math] s \to u [/math] весом [math] 0 [/math] для всех [math] u [/math].

Обозначим [math]\delta(u,v)[/math] как минимальное расстояние между вершинами [math]u,\; v[/math], введем потенциальную функцию [math] \phi [/math]
[math]\phi(u) = \delta(s,u)[/math]
Рассмотрим вес произвольного ребра [math] uv \in E [/math]: [math]\omega_\phi(uv) = \phi(u) + \omega(uv) - \phi(v) = \delta(s, u) + \omega(uv) - \delta(s, v)[/math].
Поскольку [math]\delta(s, u) + \omega(uv) [/math] — вес какого-то пути [math] s \rightsquigarrow v [/math], а [math] \delta(s, v) [/math] — вес кратчайшего пути [math] s \rightsquigarrow v[/math], то [math] \delta(s, u) + \omega(uv) \geqslant \delta(s, v) \Rightarrow \delta(s, u) + \omega(uv) - \delta(s, v) = \omega_\varphi(uv) \geqslant 0 [/math].
[math]\triangleleft[/math]

Псевдокод

Предварительно построим граф [math]G' = (V',\;E')[/math], где [math]V' = V \cup \{s\}[/math], [math]s \not\in V[/math], а [math]E' = E \cup \{(s,\;v): \omega(s, v) = 0,\ v \in V \}[/math]

function Johnson(G): int[][]
    if BellmanFord[math](G',\;\omega,\;s)[/math] == false
        print "Входной граф содержит цикл с отрицательным весом"
        return [math]\varnothing[/math]
    else for [math]v \in V'[/math]
        [math]\varphi(v)[/math] = [math]\delta(s,\;v)[/math] // [math]\delta(s,\;v)[/math] вычислено алгоритмом Беллмана — Форда
        for [math](u,\;v) \in E'[/math]
            [math]\omega_\varphi(u,\;v)[/math] = [math] \omega(u,\;v) + \varphi(u) - \varphi(v)[/math]
        for [math]u \in V[/math]
            Dijkstra[math](G,\;\omega_\varphi,\;u)[/math]
            for [math]v \in V[/math]
                [math]d_{uv} \leftarrow \delta_\varphi(u,\;v) + \varphi(v) - \varphi(u)[/math]
        return [math]d[/math]

Итого, в начале алгоритм Форда-Беллмана либо строит потенциальную функцию такую, что после перевзвешивания все веса ребер будут неотрицательны, либо выдает сообщение о том, что в графе присутствует отрицательный цикл.

Затем из каждой вершины запускается алгоритм Дейкстры для составления искомой матрицы. Так как все веса ребер теперь неотрицательны, алгоритм Дейкстры будет работать корректно. А поскольку перевзвешивание таково, что кратчайшие пути относительно обеих весовых функций совпадают, алгоритм Джонсона в итоге корректно найдет все кратчайшие пути между всеми парами вершин.

Сложность

Алгоритм Джонсона работает за [math]O(VE + VD)[/math], где [math]O(D)[/math] — время работы алгоритма Дейкстры. Если в алгоритме Дейкстры неубывающая очередь с приоритетами реализована в виде фибоначчиевой кучи, то время работы алгоритма Джонсона есть [math]O(V^2\log V + V E)[/math]. В случае реализации очереди с приоритетами в виде двоичной кучи время работы равно [math]O(V E \log V)[/math].

См. также

Источники информации

  • Кормен Т., Лейзерсон Ч., Ривест Р. Алгоритмы: построение и анализ. 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.
  • Визуализатор алгоритма