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

Материал из Викиконспекты
Перейти к: навигация, поиск
(Описание)
Строка 1: Строка 1:
'''Алгоритм Джонсона''' находит кратчайшие пути между всеми парами вершин во взвешенном ориентированном графе с любыми весами ребер, но не имеющем отрицательных циклов.
+
'''Алгоритм Джонсона''' находит кратчайшие пути между всеми парами вершин (англ. ''All Pairs Shortest Path'') во взвешенном ориентированном графе с любыми весами ребер, но не имеющем отрицательных циклов.
  
 
== Алгоритм ==
 
== Алгоритм ==
Строка 5: Строка 5:
 
=== Описание ===
 
=== Описание ===
  
Алгоритм Джонсона позволяет найти кратчайшие пути между всеми парами вершин в течение времени <tex> O(V^2\log(V) + VE) </tex>. Для разреженных графов этот алгоритм ведет себя асимптотически быстрее алгоритма Флойда. Этот алгоритм либо возвращает матрицу кратчайших расстояний между всеми парами вершин, либо сообщение о том, что в графе существует цикл отрицательной длины.
+
Алгоритм Джонсона позволяет найти кратчайшие пути между всеми парами вершин в течение времени <tex> O(V^2\log(V) + VE) </tex>. Для разреженных графов этот алгоритм ведет себя асимптотически быстрее [[Алгоритм Флойда|алгоритма Флойда]]. Этот алгоритм либо возвращает матрицу кратчайших расстояний между всеми парами вершин, либо сообщение о том, что в графе существует цикл отрицательной длины.
  
 
В этом алгоритме используется метод '''изменения веса''' (англ. ''reweighting''). Суть его заключается в том, что для заданного графа <tex> G </tex> строится новая весовая функция <tex> \omega_\varphi </tex>, неотрицательная для всех ребер графа <tex> G </tex> и сохраняющая кратчайшие пути. Такая весовая функция строится с помощью так называемой '''потенциальной''' функции.
 
В этом алгоритме используется метод '''изменения веса''' (англ. ''reweighting''). Суть его заключается в том, что для заданного графа <tex> G </tex> строится новая весовая функция <tex> \omega_\varphi </tex>, неотрицательная для всех ребер графа <tex> G </tex> и сохраняющая кратчайшие пути. Такая весовая функция строится с помощью так называемой '''потенциальной''' функции.
Строка 11: Строка 11:
 
Пусть <tex> \varphi : V \rightarrow \mathbb R </tex> — произвольное отображение из множества вершин в вещественные числа. Тогда новой весовой функцией будет <tex> \omega_\varphi(u, v) = \omega(u, v) + \varphi(u) - \varphi(v) </tex>.
 
Пусть <tex> \varphi : V \rightarrow \mathbb R </tex> — произвольное отображение из множества вершин в вещественные числа. Тогда новой весовой функцией будет <tex> \omega_\varphi(u, v) = \omega(u, v) + \varphi(u) - \varphi(v) </tex>.
  
Такая потенциальная функция строится при помощи добавлении фиктивной вершины в <tex> G </tex>, из которой проведены ребра нулевого веса во все остальные вершины и запуском алгоритма Форда-Беллмана из нее. На этом же этапе мы сможем обнаружить наличие отрицательного цикла в графе.
+
Такая потенциальная функция строится при помощи добавлении фиктивной вершины в <tex> G </tex>, из которой проведены ребра нулевого веса во все остальные вершины и запуском [[Алгоритм Форда-Беллмана|алгоритма Форда Беллмана]] из нее. На этом же этапе мы сможем обнаружить наличие отрицательного цикла в графе.
  
Теперь, когда мы знаем, что веса всех ребер неотрицательны, и кратчайшие пути сохранятся, можно запустить алгоритм Дейкстры из каждой вершины и таким образом найти кратчайшие расстояния между всеми парами вершин.
+
Теперь, когда мы знаем, что веса всех ребер неотрицательны, и кратчайшие пути сохранятся, можно запустить [[Алгоритм Дейкстры|алгоритм Дейкстры]] из каждой вершины и таким образом найти кратчайшие расстояния между всеми парами вершин.
  
 
=== Сохранение кратчайших путей ===
 
=== Сохранение кратчайших путей ===
Строка 23: Строка 23:
 
|proof=
 
|proof=
  
:Рассмотрим путь <tex>P: \;u_0 \rightarrow u_1 \rightarrow u_2 \rightarrow ... \rightarrow u_k </tex>
+
:Рассмотрим путь <tex>P: \;u_0 \rightarrow u_1 \rightarrow u_2 \rightarrow \ldots \rightarrow u_k </tex>
  
:Его вес с новой весовой функцией равен <tex>\omega_\varphi(P) = \omega_\varphi(u_0u_1) + \omega_\varphi(u_1u_2) + ... + \omega_\varphi(u_{k-1}u_k) </tex>.
+
:Его вес с новой весовой функцией равен <tex>\omega_\varphi(P) = \omega_\varphi(u_0u_1) + \omega_\varphi(u_1u_2) + \ldots + \omega_\varphi(u_{k-1}u_k) </tex>.
  
:Вставим определение функции <tex> \omega_\varphi : \omega_\varphi(P) = \varphi(u_0) + \omega(u_0u_1) - \varphi(u_1) + ... + \varphi(u_{k-1}) + \omega(u_{k-1}u_k) - \varphi(u_k) </tex>
+
:Вставим определение функции <tex> \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) </tex>
  
 
:Заметим, что потенциалы все промежуточных вершин в пути сократятся. <tex> \omega_\varphi(P) = \varphi(u_0) + \omega(P) - \varphi(u_k)</tex>
 
:Заметим, что потенциалы все промежуточных вершин в пути сократятся. <tex> \omega_\varphi(P) = \varphi(u_0) + \omega(P) - \varphi(u_k)</tex>
Строка 48: Строка 48:
 
|proof=
 
|proof=
  
<tex>\Leftarrow </tex>: Рассмотрим произвольный <tex>C</tex> - цикл в графе <tex>G</tex>
+
<tex>\Leftarrow </tex>: Рассмотрим произвольный <tex>C</tex> цикл в графе <tex>G</tex>
  
 
:По лемме, его вес равен <tex> \omega(C) = \omega_\varphi(C) - \varphi(u_0) - \varphi(u_0) = \omega_\varphi(C) \geqslant 0</tex>
 
:По лемме, его вес равен <tex> \omega(C) = \omega_\varphi(C) - \varphi(u_0) - \varphi(u_0) = \omega_\varphi(C) \geqslant 0</tex>
Строка 67: Строка 67:
  
 
Предварительно построим граф  <tex>G' = (V',\;E')</tex>, где <tex>V' = V \cup \{s\}</tex>,  <tex>s \not\in V</tex>, а <tex>E' = E \cup \{(s,\;v): \omega(s, v) = 0,\ v \in V \}</tex>
 
Предварительно построим граф  <tex>G' = (V',\;E')</tex>, где <tex>V' = V \cup \{s\}</tex>,  <tex>s \not\in V</tex>, а <tex>E' = E \cup \{(s,\;v): \omega(s, v) = 0,\ v \in V \}</tex>
 
+
'''function''' Johnson(G): '''int[][]'''
'''if''' Bellman_Ford<tex>(G',\;\omega,\;s)</tex> == FALSE
+
    '''if''' Bellman_Ford<tex>(G',\;\omega,\;s)</tex> == FALSE
    '''then''' print "Входной граф содержит цикл с отрицательным весом"
+
        '''then''' print "Входной граф содержит цикл с отрицательным весом"
    '''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>
  
Итого, в начале алгоритм Форда-Беллмана либо строит потенциальную функцию такую, что после перевзвешивания все веса ребер будут неотрицательны, либо выдает сообщение о том, что в графе присутствует отрицательный цикл.
+
Итого, в начале алгоритм Форда Беллмана либо строит потенциальную функцию такую, что после перевзвешивания все веса ребер будут неотрицательны, либо выдает сообщение о том, что в графе присутствует отрицательный цикл.
  
 
Затем из каждой вершины запускается алгоритм Дейкстры для составления искомой матрицы. Так как все веса ребер теперь неотрицательны, алгоритм Дейкстры будет работать корректно. А поскольку перевзвешивание таково, что кратчайшие пути относительно обеих весовых функций совпадают, алгоритм Джонсона в итоге корректно найдет все кратчайшие пути между всеми парами вершин.
 
Затем из каждой вершины запускается алгоритм Дейкстры для составления искомой матрицы. Так как все веса ребер теперь неотрицательны, алгоритм Дейкстры будет работать корректно. А поскольку перевзвешивание таково, что кратчайшие пути относительно обеих весовых функций совпадают, алгоритм Джонсона в итоге корректно найдет все кратчайшие пути между всеми парами вершин.
Строка 91: Строка 91:
 
* [[Алгоритм Форда-Беллмана]]
 
* [[Алгоритм Форда-Беллмана]]
 
* [[Алгоритм Флойда]]
 
* [[Алгоритм Флойда]]
* [http://rain.ifmo.ru/cat/view.php/vis/graph-paths/johnson-2001 Визуализатор алгоритма]
 
  
 
== Источники информации ==
 
== Источники информации ==
 
* ''Кормен Т., Лейзерсон Ч., Ривест Р.'' Алгоритмы: построение и анализ. 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.
 
* ''Кормен Т., Лейзерсон Ч., Ривест Р.'' Алгоритмы: построение и анализ. 2-е изд. — М.: Издательский дом «Вильямс», 2007. — С. 1296.
 +
* [http://rain.ifmo.ru/cat/view.php/vis/graph-paths/johnson-2001 Визуализатор алгоритма]
 +
 +
[[Категория: Алгоритмы и структуры данных]]
 +
[[Категория: Кратчайшие пути в графах ]]

Версия 22:50, 25 января 2016

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

Алгоритм

Описание

Алгоритм Джонсона позволяет найти кратчайшие пути между всеми парами вершин в течение времени [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 Bellman_Ford[math](G',\;\omega,\;s)[/math] == FALSE
        then print "Входной граф содержит цикл с отрицательным весом"
        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.
  • Визуализатор алгоритма