福特-富尔克森算法

福特-富尔克森方法（英語：Ford–Fulkerson method），又稱福特-富尔克森算法（Ford–Fulkerson algorithm），是一类计算网络流的最大流的贪心算法。之所以称之为“方法”而不是“算法”，是因为它寻找增广路径的方式并不是完全确定的，而是有几种不同时间复杂度的实现方式。它在1956年由小萊斯特·倫道夫·福特及德爾伯特·雷·富爾克森发表。“福特-富尔克森”这个名词通常也指代埃德蒙兹-卡普算法，这是一个特殊的福特-富尔克森算法实现。 算法的思想如下：只要有一条从源点（开始节点）到汇点（结束节点）的路径，在路径的所有边上都有可用容量，就沿着这条路径发送一个流，流量由路径上的最小容量限制。 然后再找到另一条路径，一直到网络中不存在这种路径为止。 一条有可用容量的路径被称为一条增广路径。 设 G ( V , E ) {\displaystyle G(V,E)} 为一个图，并且为每条从 u {\displaystyle u} 到 v {\displaystyle v} 的边 ( u , v ) {\displaystyle (u,v)} 设置一个最大流量 c ( u , v ) {\displaystyle c(u,v)} ，并且初始化当前流量 f ( u , v ) = 0 {\displaystyle f(u,v)=0} 。下面是该算法每一步的实现： 这意味着每轮计算之后通过网络的都是一个流。我们定义残留网络 G f ( V , E f ) {\displaystyle G_{f}(V,E_{f})} 是一个网络的剩余流量 c f ( u , v ) = c ( u , v ) − f ( u , v ) {\displaystyle c_{f}(u,v)=c(u,v)-f(u,v)} 。注意残留网络可以设置从 v {\displaystyle v} 到 u {\displaystyle u} 的流量，即使在原先的网络中不允许这种情况产生：如果 c ( v , u ) = 0 {\displaystyle c(v,u)=0} 但 f ( u , v ) > 0 {\displaystyle f(u,v)>0} ，那么 c f ( v , u ) = c ( v , u ) − f ( v , u ) = f ( u , v ) > 0 {\displaystyle c_{f}(v,u)=c(v,u)-f(v,u)=f(u,v)>0} ：也即，从 u {\displaystyle u} 到 v {\displaystyle v} 的流量给从 v {\displaystyle v} 到 u {\displaystyle u} 的流量提供了额外的剩余量。 算法 福特-富尔克森 输入 给定一张边的容量为 c {\displaystyle c} 的图 G = ( V , E ) {\displaystyle G=(V,E)} ，源点 s {\displaystyle s} 以及汇点 t {\displaystyle t} 。 输出 在网络 G {\displaystyle G} 中，从 s {\displaystyle s} 到 t {\displaystyle t} 的最大流 f {\displaystyle f} 。 初始化网络流量 f ← 0 {\displaystyle f\leftarrow 0} 、残留网络 G f ← G {\displaystyle G_{f}\leftarrow G} 。对于图的每一条边 ( u , v ) {\displaystyle (u,v)} ，初始化流量 f ( u , v ) ← 0 {\displaystyle f(u,v)\leftarrow 0} 。 只要 G f {\displaystyle G_{f}} 中还存在一条从 s {\displaystyle s} 到 t {\displaystyle t} 的路径 p {\displaystyle p} ，使对于每一条边 ( u , v ) ∈ p {\displaystyle (u,v)\in p} ，都有 c f ( u , v ) > 0 {\displaystyle c_{f}(u,v)>0} ： 设置路径 p {\displaystyle p} 本次应发送的流量为路径最小剩余流量： c f ( p ) ← min ( u , v ) ∈ p c f ( u , v ) {\displaystyle c_{f}(p)\leftarrow \min _{(u,v)\in p}c_{f}(u,v)} 。 更新网络流量 f ← f + c f ( p ) {\displaystyle f\leftarrow f+c_{f}(p)} 。 对于每一条边 ( u , v ) ∈ p {\displaystyle (u,v)\in p} ，更新 G f {\displaystyle G_{f}} 的剩余流量： f ( u , v ) ← f ( u , v ) + c f ( p ) {\displaystyle f(u,v)\leftarrow f(u,v)+c_{f}(p)} （在路径中“发送”流） f ( v , u ) ← f ( v , u ) − c f ( p ) {\displaystyle f(v,u)\leftarrow f(v,u)-c_{f}(p)} （这个流在之后可以被“发送”回来） 步骤2中的路径 p {\displaystyle p} 可以用广度优先搜索或深度优先搜索在 G f ( V , E f ) {\displaystyle G_{f}(V,E_{f})} 中找到。如果使用了广度优先搜索，这个算法就是Edmonds–Karp算法。 当步骤2中找不到可行路径时， s {\displaystyle s} 就无法在残留网络中到达 t {\displaystyle t} 。设 S {\displaystyle S} 是在残留网络中 s {\displaystyle s} 可以到达的节点的集合，然后从 S {\displaystyle S} 到 V {\displaystyle V} 的其余部分的网络一方面等于我们找到的从 s {\displaystyle s} 到 t {\displaystyle t} 的所有流的总流量，另一方面所有这样的流量组成了一个上限。这说明我们找到的流是最大的。参见最大流最小割定理。 如果图 G ( V , E ) {\displaystyle G(V,E)} 有多个源点和汇点，可以按如下方法处理：设 T = { t | t 为 目 标 点  } {\displaystyle T=\{t|t{\text{为 目 标 点 }}\}} ， S = { s | s 为 源 点  } {\displaystyle S=\{s|s{\text{为 源 点 }}\}} 。 添加一个新源点 s ∗ {\displaystyle s^{*}} 与所有源点有一条边 ( s ∗ , s ) {\displaystyle (s^{*},s)} 相连，容量 c ( s ∗ , s ) = d s ( d s = ∑ ( s , u ) ∈ E c ( s , u ) ) {\displaystyle c(s^{*},s)=d_{s}\;(d_{s}=\sum _{(s,u)\in E}c(s,u))} 。添加一个新汇点 t ∗ {\displaystyle t^{*}} 与所有汇点 ( t , t ∗ ) {\displaystyle (t,t^{*})} 有一条边相连，容量 c ( t , t ∗ ) = d t ( d t = ∑ ( v , t ) ∈ E c ( v , t ) ) {\displaystyle c(t,t^{*})=d_{t}\;(d_{t}=\sum _{(v,t)\in E}c(v,t))} 。然后执行福特-富尔克森算法。 同样的，如果节点 u {\displaystyle u} 有通过限制 d u {\displaystyle d_{u}} ，可将这个节点用两个节点 u i n , u o u t {\displaystyle u_{in},u_{out}} 替换，用一条边 ( u i n , u o u t ) {\displaystyle (u_{in},u_{out})} 相连，容量为 c ( u i n , u o u t ) = d u {\displaystyle c(u_{in},u_{out})=d_{u}} 。然后执行福特-富尔克森算法。 算法通过添加找到的增广路径的流量增加总流量，当无法找到增广路径时，总流量就达到了最大。当流量是整数时，福特-富尔克森算法的运行时间为 O ( E f ) {\displaystyle O(Ef)} （参见大O符号）, E {\displaystyle E} 图中的边数， f {\displaystyle f} 为最大流。 这是因为一条增广路径可以在 O ( E ) {\displaystyle O(E)} 的时间复杂度内找到，每轮算法执行后流量的增长至少为 1 {\displaystyle 1} 。但是在极端情况下，算法有可能永远不会停止。 福特-富尔克森算法的一个特例是埃德蒙兹-卡普算法，时间复杂度为 O ( V E 2 ) {\displaystyle O(VE^{2})} 。 下面的样例演示了福特-富尔克森在一张有4个节点，源点为 A {\displaystyle A} ，汇点为 D {\displaystyle D} 的图中的第一轮计算。 这个例子显示了算法在最坏情况下的行为。在每一轮算法中，只有 1 {\displaystyle 1} 的流量被发送至网络中。如果算法改用宽度优先搜索，那么只需要两轮计算。 注意当找到路径 A , C , B , D {\displaystyle A,C,B,D} 时，流是如何从 C {\displaystyle C} 发送至 B {\displaystyle B} 的。 右图所示的网络中源点为 s {\displaystyle s} ，汇点为 t {\displaystyle t} 边 e 1 {\displaystyle e_{1}} 、 e 2 {\displaystyle e_{2}} 、 e 3 {\displaystyle e_{3}} 的容量为 1 {\displaystyle 1} , r = ( 5 − 1 ) / 2 {\displaystyle r=({\sqrt {5}}-1)/2} 和 1 {\displaystyle 1} ，使 r 2 = 1 − r {\displaystyle r^{2}=1-r} 。其它所有边的容量 M ≥ 2 {\displaystyle M\geq 2} 。 使用福特-富尔克森算法可找到三条增广路径，分别为 p 1 = { s , v 4 , v 3 , v 2 , v 1 , t } {\displaystyle p_{1}=\{s,v_{4},v_{3},v_{2},v_{1},t\}} 、 p 2 = { s , v 2 , v 3 , v 4 , t } {\displaystyle p_{2}=\{s,v_{2},v_{3},v_{4},t\}} 、 p 3 = { s , v 1 , v 2 , v 3 , t } {\displaystyle p_{3}=\{s,v_{1},v_{2},v_{3},t\}} . 注意在步骤1和步骤5之后,边 e 1 {\displaystyle e_{1}} 、 e 2 {\displaystyle e_{2}} 、 e 3 {\displaystyle e_{3}} 的残留容量都可以表示为 r n {\displaystyle r^{n}} 、 r n + 1 {\displaystyle r^{n+1}} 或 0 {\displaystyle 0} ，同时，对于一些特殊的 n ∈ N {\displaystyle n\in \mathbb {N} } 这意味着算法可以通过 p 1 {\displaystyle p_{1}} 、 p 2 {\displaystyle p_{2}} 、 p 1 {\displaystyle p_{1}} 与 p 3 {\displaystyle p_{3}} 无限增广，并且残留容量总处于一个循环。在步骤5之后网络的流为 1 + 2 ( r 1 + r 2 ) {\displaystyle 1+2(r^{1}+r^{2})} ，如果继续用以上的算法增广，总的流将向 1 + 2 ∑ i = 1 ∞ r i = 3 + 2 r {\displaystyle \textstyle 1+2\sum _{i=1}^{\infty }r^{i}=3+2r} 趋近，但最大流为 2 M + 1 {\displaystyle 2M+1} 。在这个样例中，算法将永远不会停止，且结果也不会向实际的最大流趋近。 class Edge(object): def __init__(self, u, v, w): self.source = u self.sink = v self.capacity = w def __repr__(self): return "%s->%s:%s" % (self.source, self.sink, self.capacity) class FlowNetwork(object): def __init__(self): self.adj = {} self.flow = {} def add_vertex(self, vertex): self.adj[vertex] = [] def get_edges(self, v): return self.adj[v] def add_edge(self, u, v, w=0): if u == v: raise ValueError("u == v") edge = Edge(u,v,w) redge = Edge(v,u,0) edge.redge = redge redge.redge = edge self.adj[u].append(edge) self.adj[v].append(redge) self.flow[edge] = 0 self.flow[redge] = 0 def find_path(self, source, sink, path): if source == sink: return path for edge in self.get_edges(source): residual = edge.capacity - self.flow[edge] if residual > 0 and edge not in path: result = self.find_path( edge.sink, sink, path + [edge]) if result != None: return result def max_flow(self, source, sink): path = self.find_path(source, sink, []) while path != None: residuals = [edge.capacity - self.flow[edge] for edge in path] flow = min(residuals) for edge in path: self.flow[edge] += flow self.flow[edge.redge] -= flow path = self.find_path(source, sink, []) return sum(self.flow[edge] for edge in self.get_edges(source)) >>> g = FlowNetwork() >>> [g.add_vertex(v) for v in "sopqrt"] [None, None, None, None, None, None] >>> >>> g.add_edge('s','o',3) >>> g.add_edge('s','p',3) >>> g.add_edge('o','p',2) >>> g.add_edge('o','q',3) >>> g.add_edge('p','r',2) >>> g.add_edge('r','t',3) >>> g.add_edge('q','r',4) >>> g.add_edge('q','t',2) >>> print (g.max_flow('s','t')) 5 二分图的最大匹配 最大不相交路径 Laung-Terng Wang, Yao-Wen Chang, Kwang-Ting (Tim) Cheng. Electronic Design Automation: Synthesis, Verification, and Test. Morgan Kaufmann. 2009: 204. ISBN 0080922007.  Thomas H. Cormen; Charles E. Leiserson; Ronald L. Rivest; Clifford Stein. Introduction to Algorithms. MIT Press. 2009: 714. ISBN 0262258102.  Ford, L. R.; Fulkerson, D. R. Maximal flow through a network. Canadian Journal of Mathematics. 1956, 8: 399. doi:10.4153/CJM-1956-045-5.  Zwick, Uri. The smallest networks on which the Ford–Fulkerson maximum flow procedure may fail to terminate. Theoretical Computer Science. 21 August 1995, 148 (1): 165–170. doi:10.1016/0304-3975(95)00022-O.  Cormen, Thomas H.; Leiserson, Charles E.; Rivest, Ronald L.; Stein, Clifford. Section 26.2: The Ford–Fulkerson method. Introduction to Algorithms Second. MIT Press and McGraw–Hill. 2001: 651–664. ISBN 0-262-03293-7.  George T. Heineman; Gary Pollice; Stanley Selkow. Chapter 8:Network Flow Algorithms. Algorithms in a Nutshell. Oreilly Media. 2008: 226–250. ISBN 978-0-596-51624-6.  Jon Kleinberg; Éva Tardos. Chapter 7:Extensions to the Maximum-Flow Problem. Algorithm Design. Pearson Education. 2006: 378–384. ISBN 0-321-29535-8.