Алгоритм Форда-Фалкерсона, реализация с помощью поиска в глубину — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(int -> auto у ребра)
(не показано 28 промежуточных версий 4 участников)
Строка 1: Строка 1:
Алгоритм Форда-Фалкерсона — алгоритм, решающий задачу нахождения максимального потока в транспортной сети.
+
'''Алгоритм Форда-Фалкерсона''' — алгоритм, решающий задачу нахождения максимального [[Определение сети, потока #Определение потока | потока]] в транспортной сети.
  
 
== Идея ==
 
== Идея ==
Идея алгоритма заключается в следующем. Изначально величине потока присваивается значение <tex>0</tex>: <tex> f(u,v) = 0 </tex> для всех <tex> u, v </tex> из <tex> V </tex>. Затем величина потока итеративно увеличивается посредством поиска увеличивающего пути (путь от источника s к стоку t, вдоль которого можно послать ненулевой поток). В данной статье рассматривается алгоритм, осуществляющий этот поиск с помощью [[Обход в глубину, цвета вершин|обхода в глубину (dfs)]]. Процесс повторяется, пока можно найти увеличивающий путь.
+
Идея алгоритма заключается в следующем. Изначально величине потока присваивается значение <tex>0</tex>: <tex> f(u,v) = 0 </tex> для всех <tex> u, v </tex> из <tex> V </tex>. Затем величина потока итеративно увеличивается посредством поиска увеличивающего пути (путь от источника <tex>s</tex> к стоку <tex>t</tex>, вдоль которого можно послать ненулевой поток). В данной статье рассматривается алгоритм, осуществляющий этот поиск с помощью [[Обход в глубину, цвета вершин|обхода в глубину (dfs)]]. Процесс повторяется, пока можно найти увеличивающий путь.
  
 
== Реализация ==
 
== Реализация ==
  dfs(u, Cmin) {
+
  '''int''' dfs('''int''' u, '''int''' Cmin):        <span style="color:Green">// Cmin {{---}} пропускная способность в текущем подпотоке</span>
     if (u = t)
+
     '''if''' u = t
         return Cmin
+
         '''return''' Cmin
     u.vis <- true
+
     visited[u] = ''true''                 
     for (uv in E)
+
     '''for''' v '''in''' u.children
         if (!v.vis) && (uv.f < uv.c)
+
        '''auto''' uv = edge(u, v)
             дельта <- dfs(v, min(Cmin, uv.c - uv.f))
+
         '''if''' '''not''' visited[v] '''and''' uv.f < uv.c
             if (дельта > 0) {
+
             '''int''' delta = dfs(v, min(Cmin, uv.c - uv.f))
                 uv.f += дельта
+
             '''if''' delta > 0
                 uv.backEdge.f -= дельта
+
                 uv.f += delta
                 return дельта
+
                 uv.backEdge.f -= delta
            }
+
                 '''return''' delta
     return 0
+
     '''return''' 0
}
 
  
 
== Оценка производительности ==
 
== Оценка производительности ==
Добавляя поток увеличивающего пути к уже имеющемуся потоку, максимальный поток будет получен, когда нельзя будет найти увеличивающий путь. Тем не менее, если величина пропускной способности — иррациональное число, то алгоритм может работать бесконечно. В целых числах таких проблем не возникает и время работы ограничено <tex>O(E|f|)</tex>, где <tex>E</tex> — число рёбер в графе, <tex>f</tex> — максимальный поток в графе, так как каждый увеличивающий путь может быть найден за <tex>O(E)</tex> и увеличивает поток как минимум на <tex>1</tex>.
+
Добавляя поток увеличивающего пути к уже имеющемуся потоку, максимальный поток будет получен, когда нельзя будет найти увеличивающий путь. Тем не менее, если величина пропускной способности — иррациональное число, то алгоритм может работать бесконечно. В целых числах таких проблем не возникает и время работы ограничено <tex>O(|E|f)</tex>, где <tex>E</tex> — число рёбер в графе, <tex>f</tex> — максимальный поток в графе, так как каждый увеличивающий путь может быть найден за <tex>O(E)</tex> и увеличивает поток как минимум на <tex>1</tex>.
  
  
 
=== Пример несходящегося алгоритма ===
 
=== Пример несходящегося алгоритма ===
[[Файл:F-f.5.png|thumb|right|рис. 1]]
+
[[Файл:F-f.5.png|300px|thumb|right|Рис. 1]]
Рассмотрим приведённую справа сеть, с источником <tex>\ s</tex>, стоком <tex>\ t</tex>, пропускными способностями рёбер <tex>\ e_1</tex>, <tex>\ e_2</tex> и <tex>\ e_3</tex> соответственно <tex>\ 1</tex>, <tex>r=(\sqrt{5}-1)/2</tex> и <tex>\ 1</tex> и пропускной способностью всех остальных рёбер, равной целому числу <tex>M \ge 2</tex>. Константа <tex>\ r</tex> выбрана так, что <tex>\ r^2 = 1 - r</tex>. Мы используем пути из остаточного графа, приведённые в таблице, причём <tex>\ p_1 = \{ s, v_4, v_3, v_2, v_1, t \}</tex>, <tex>\ p_2 = \{ s, v_2, v_3, v_4, t \}</tex> и <tex>\ p_3 = \{ s, v_1, v_2, v_3, t \}</tex>.
+
Рассмотрим приведённую справа сеть с источником <tex>\ s</tex>, стоком <tex>\ t</tex>, пропускными способностями рёбер <tex>\ e_1</tex>, <tex>\ e_2</tex> и <tex>\ e_3</tex> соответственно <tex>\ 1</tex>, <tex>r=\dfrac{\sqrt{5}-1}{2}</tex> и <tex>\ 1</tex> и пропускной способностью всех остальных рёбер, равной целому числу <tex>M \geqslant 2</tex>. Константа <tex>\ r</tex> выбрана так, что <tex>\ r^2 = 1 - r</tex>. Мы используем пути из остаточного графа, приведённые в таблице, причём <tex>\ p_1 = \{ s, v_4, v_3, v_2, v_1, t \}</tex>, <tex>\ p_2 = \{ s, v_2, v_3, v_4, t \}</tex> и <tex>\ p_3 = \{ s, v_1, v_2, v_3, t \}</tex>.
  
 
{| class="wikitable" style="text-align: center"
 
{| class="wikitable" style="text-align: center"
Строка 46: Строка 45:
 
|}
 
|}
  
Заметим, что после шага <tex>1</tex>, как и после шага <tex>5</tex>, остаточные способности рёбер <tex>e_1</tex>, <tex>e_2</tex> и <tex>e_3</tex> имеют форму <tex>r^n</tex>, <tex>r^{n+1}</tex> и <tex>0</tex>, соответственно, для какого-то натурального <tex>n</tex>. Это значит, что мы можем использовать увеличивающие пути <tex>p_1</tex>, <tex>p_2</tex>, <tex>p_1</tex> и <tex>p_3</tex> бесконечно много раз, и остаточные пропускные способности этих рёбер всегда будут в той же форме. Полный поток после шага <tex>5</tex> равен <tex>1 + 2(r^1 + r^2)</tex>. За бесконечное время полный поток сойдётся к <tex>\textstyle 1 + 2\sum_{i=1}^\infty r^i = 3 + 2r</tex>, тогда как максимальный поток равен <tex>2M + 1</tex>. Таким образом, алгоритм не только работает бесконечно долго, но даже и не сходится к оптимальному решению.
+
Заметим, что после шага <tex>1</tex>, как и после шага <tex>5</tex>, остаточные способности рёбер <tex>e_1</tex>, <tex>e_2</tex> и <tex>e_3</tex> имеют форму <tex>r^n</tex>, <tex>r^{n+1}</tex> и <tex>0</tex>, соответственно, для какого-то натурального <tex>n</tex>. Это значит, что мы можем использовать увеличивающие пути <tex>p_1</tex>, <tex>p_2</tex>, <tex>p_1</tex> и <tex>p_3</tex> бесконечно много раз, и остаточные пропускные способности этих рёбер всегда будут в той же форме. Полный поток после шага <tex>5</tex> равен <tex>1 + 2(r^1 + r^2)</tex>. За бесконечное время полный поток сойдётся к <tex>\textstyle 1 + 2\sum\limits_{i=1}^\infty r^i = 3 + 2r</tex>, тогда как максимальный поток равен <tex>2M + 1</tex>. Таким образом, алгоритм не только работает бесконечно долго, но даже и не сходится к оптимальному решению.
  
 
=== Пример медленной работы алгоритма Форда-Фалкерсона с использованием поиска в глубину по сравнению с реализацией, использующей поиск в ширину ===
 
=== Пример медленной работы алгоритма Форда-Фалкерсона с использованием поиска в глубину по сравнению с реализацией, использующей поиск в ширину ===
 
При использовании поиска в ширину алгоритму потребуется всего лишь два шага.
 
При использовании поиска в ширину алгоритму потребуется всего лишь два шага.
Дана сеть (рис. 2).
+
Дана сеть (Рис. 2).
[[Файл:F-f.1.png|thumb|300px|center|рис. 2]]
+
[[Файл:F-f.1.png|thumb|300px|center|Рис. 2]]
Благодаря двум итерациям (рис. 3 и рис. 4)
+
Благодаря двум итерациям (Рис. 3 и Рис. 4)
[[Файл:F-f.2.png|thumb|300px|center|рис. 3]]
+
[[Файл:F-f.2.png|thumb|300px|center|Рис. 3]]
[[Файл:F-f.3.png|thumb|300px|center|рис. 4]]
+
[[Файл:F-f.3.png|thumb|300px|center|Рис. 4]]
 
рёбра <tex>AB, AC, BD, CD</tex> насытились лишь на <tex>1</tex>.
 
рёбра <tex>AB, AC, BD, CD</tex> насытились лишь на <tex>1</tex>.
Конечная сеть будет получена ещё через 1998 итераций (рис. 5).
+
Конечная сеть будет получена ещё через 1998 итераций (Рис. 5).
[[Файл:F-f.4.png|thumb|300px|center|рис. 5]]
+
[[Файл:F-f.4.png|thumb|300px|center|Рис. 5]]
  
 
== См. также ==
 
== См. также ==
 
* [[Теорема Форда-Фалкерсона]]
 
* [[Теорема Форда-Фалкерсона]]
 +
* [[Алгоритм Эдмондса-Карпа]]
  
 
== Источники информации ==
 
== Источники информации ==

Версия 15:45, 12 июня 2019

Алгоритм Форда-Фалкерсона — алгоритм, решающий задачу нахождения максимального потока в транспортной сети.

Идея

Идея алгоритма заключается в следующем. Изначально величине потока присваивается значение [math]0[/math]: [math] f(u,v) = 0 [/math] для всех [math] u, v [/math] из [math] V [/math]. Затем величина потока итеративно увеличивается посредством поиска увеличивающего пути (путь от источника [math]s[/math] к стоку [math]t[/math], вдоль которого можно послать ненулевой поток). В данной статье рассматривается алгоритм, осуществляющий этот поиск с помощью обхода в глубину (dfs). Процесс повторяется, пока можно найти увеличивающий путь.

Реализация

int dfs(int u, int Cmin):         // Cmin — пропускная способность в текущем подпотоке
   if u = t
       return Cmin
   visited[u] = true                  
   for v in u.children
       auto uv = edge(u, v)
       if not visited[v] and uv.f < uv.c
           int delta = dfs(v, min(Cmin, uv.c - uv.f))
           if delta > 0
               uv.f += delta
               uv.backEdge.f -= delta
               return delta
   return 0

Оценка производительности

Добавляя поток увеличивающего пути к уже имеющемуся потоку, максимальный поток будет получен, когда нельзя будет найти увеличивающий путь. Тем не менее, если величина пропускной способности — иррациональное число, то алгоритм может работать бесконечно. В целых числах таких проблем не возникает и время работы ограничено [math]O(|E|f)[/math], где [math]E[/math] — число рёбер в графе, [math]f[/math] — максимальный поток в графе, так как каждый увеличивающий путь может быть найден за [math]O(E)[/math] и увеличивает поток как минимум на [math]1[/math].


Пример несходящегося алгоритма

Рис. 1

Рассмотрим приведённую справа сеть с источником [math]\ s[/math], стоком [math]\ t[/math], пропускными способностями рёбер [math]\ e_1[/math], [math]\ e_2[/math] и [math]\ e_3[/math] соответственно [math]\ 1[/math], [math]r=\dfrac{\sqrt{5}-1}{2}[/math] и [math]\ 1[/math] и пропускной способностью всех остальных рёбер, равной целому числу [math]M \geqslant 2[/math]. Константа [math]\ r[/math] выбрана так, что [math]\ r^2 = 1 - r[/math]. Мы используем пути из остаточного графа, приведённые в таблице, причём [math]\ p_1 = \{ s, v_4, v_3, v_2, v_1, t \}[/math], [math]\ p_2 = \{ s, v_2, v_3, v_4, t \}[/math] и [math]\ p_3 = \{ s, v_1, v_2, v_3, t \}[/math].

Шаг Найденный путь Добавленный поток Остаточные пропускные способности
[math]e_1[/math] [math]e_2[/math] [math]e_3[/math]
[math]0[/math] [math]-[/math] [math]-[/math] [math]r^0=1[/math] [math]r[/math] [math]1[/math]
[math]1[/math] [math]\{ s, v_2, v_3, t \}[/math] [math]1[/math] [math]r^0[/math] [math]r^1[/math] [math]0[/math]
[math]2[/math] [math]p_1[/math] [math]r^1[/math] [math]r^2[/math] [math]0[/math] [math]r^1[/math]
[math]3[/math] [math]p_2[/math] [math]r^1[/math] [math]r^2[/math] [math]r^1[/math] [math]0[/math]
[math]4[/math] [math]p_1[/math] [math]r^2[/math] [math]0[/math] [math]r^3[/math] [math]r^2[/math]
[math]5[/math] [math]p_3[/math] [math]r^2[/math] [math]r^2[/math] [math]r^3[/math] [math]0[/math]

Заметим, что после шага [math]1[/math], как и после шага [math]5[/math], остаточные способности рёбер [math]e_1[/math], [math]e_2[/math] и [math]e_3[/math] имеют форму [math]r^n[/math], [math]r^{n+1}[/math] и [math]0[/math], соответственно, для какого-то натурального [math]n[/math]. Это значит, что мы можем использовать увеличивающие пути [math]p_1[/math], [math]p_2[/math], [math]p_1[/math] и [math]p_3[/math] бесконечно много раз, и остаточные пропускные способности этих рёбер всегда будут в той же форме. Полный поток после шага [math]5[/math] равен [math]1 + 2(r^1 + r^2)[/math]. За бесконечное время полный поток сойдётся к [math]\textstyle 1 + 2\sum\limits_{i=1}^\infty r^i = 3 + 2r[/math], тогда как максимальный поток равен [math]2M + 1[/math]. Таким образом, алгоритм не только работает бесконечно долго, но даже и не сходится к оптимальному решению.

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

При использовании поиска в ширину алгоритму потребуется всего лишь два шага. Дана сеть (Рис. 2).

Рис. 2

Благодаря двум итерациям (Рис. 3 и Рис. 4)

Рис. 3
Рис. 4

рёбра [math]AB, AC, BD, CD[/math] насытились лишь на [math]1[/math]. Конечная сеть будет получена ещё через 1998 итераций (Рис. 5).

Рис. 5

См. также

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