Edmonds-Karp算法是一个具体的实现<一个href="//www.parkandroid.com/wiki/ford-fulkerson-algorithm/" class="wiki_link" title="Ford-Fulkerson算法" target="_blank">Ford-Fulkerson算法.和福特-福克森一样,埃德蒙斯-卡普也是一种处理<一个href="//www.parkandroid.com/wiki/max-flow-min-cut-algorithm/" class="wiki_link" title="最大流min-cut问题" target="_blank">最大流min-cut问题.
Ford-Fulkerson有时被称为方法,因为它的协议的某些部分没有指定。另一方面,Edmonds-Karp提供了一个完整的规范。最重要的是,它指定了这一点<一个href="//www.parkandroid.com/wiki/breadth-first-search/" class="wiki_link" title="广度优先搜索" target="_blank">广度优先搜索应用于在程序的中间阶段找到最短路径。
Edmonds-Karp改进了Ford-Fulkerson的运行时间<年代pan class="katex"> ,<年代pan class="katex"> .这一改进非常重要,因为它使Edmonds-Karp的运行时间与网络的最大流量无关,<年代pan class="katex"> .
为网络寻找最大流量首先由<一个href="//www.parkandroid.com/wiki/ford-fulkerson-algorithm/" class="wiki_link" title="Ford-Fulkerson算法" target="_blank">Ford-Fulkerson算法.网络通常被抽象地定义为图,<年代pan class="katex"> ,它有一组顶点,<年代pan class="katex"> ,由一组边连接,<年代pan class="katex"> .有一个来源,<年代pan class="katex"> ,还有水槽,<年代pan class="katex"> ,表示气流从哪里来,往哪里去。通过的方法求出网络的最大流量<一个href="//www.parkandroid.com/wiki/max-flow-min-cut-algorithm/" class="wiki_link" title="最大流min-cut定理" target="_blank">最大流min-cut定理,然后被用来创建Ford-Fulkerson算法。
埃德蒙斯-卡普和<一个href="//www.parkandroid.com/wiki/ford-fulkerson-algorithm/" class="wiki_link" title="Ford-Fulkerson" target="_blank">Ford-Fulkerson除了一个非常重要的特征。明确了增广路径的搜索顺序。作为Ford-Fulkerson wiki的复习,当发现网络的最大流量时,扩展路径和剩余图是两个需要理解的重要概念。
增加路径是指从源到汇的任何路径,这些路径目前可以承受更多的流。在算法的过程中,流单调地增加。所以,有时候从源头到水槽的路径会有更多的流量,这是一条增大的路径。
残差图,记为<年代pan class="katex"> ,对于一个图,<年代pan class="katex"> ,共享相同的顶点集。不同的是边缘。在算法的每一轮中,找到一条增广路径。找到这条路径后,新残差图的边缘集就会发生变化。沿着小路,每条边<年代pan class="katex"> 将会失去与增加路径中那条边相对应的重量。每一个反向边<年代pan class="katex"> 会增加体重。原图中不在增广路径中的边不受影响。一旦在残差图中没有从源到汇聚的路径,算法就终止。
Edmonds-Karp与Ford-Fulkerson的不同之处在于它选择了下一个增强路径<一个href="//www.parkandroid.com/wiki/breadth-first-search-bfs/" class="wiki_link" title="广度优先搜索(bfs)" target="_blank">广度优先搜索(bfs).因此,如果有多个增加路径可供选择,Edmonds-Karp肯定会选择从源到汇的最短增加路径。
下面是Edmonds-Karp的一个基本的、非语言特定的伪代码示例:
12 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
在这个伪代码中,流的初始值为零,初始剩余容量数组全部为零。然后,执行外部循环,直到残差图中没有从源到汇聚的路径为止(这些循环的数量在<一个t一个rget="_blank" rel="nofollow" href="#complexity-proof">下面的部分).
在这个循环中,我们正在执行<一个href="//www.parkandroid.com/wiki/breadth-first-search-bfs/" class="wiki_link" title="广度优先搜索" target="_blank">广度优先搜索查找从源到具有可用容量的接收器的最短路径。对BFS的这个小小的改变是相当容易的,因为我们使用的是剩余容量矩阵F
这基本上告诉我们在给定的边缘上是否有可用容量。
一旦我们找到了一条有剩余容量的路径,米
,我们把这个容量加到目前的最大流量上。然后,代码通过网络回溯,从接收器开始t
.当它回溯时,它会找到定义为的当前顶点的父顶点u
.然后,代码更新剩余流矩阵F
以反映新发现的增强路径容量米
.v
然后设置为父节点,然后重复内部循环,直到它到达源顶点。
埃德蒙斯-卡普根据许多证据<一个href="//www.parkandroid.com/wiki/big-o-notation/" class="wiki_link" title="复杂性" target="_blank">复杂性都被描述为<一个href="//www.parkandroid.com/wiki/ford-fulkerson-algorithm/" class="wiki_link" title="Ford-Fulkerson" target="_blank">Ford-Fulkerson.来证明这个实现运行在<年代pan class="katex">
,必须证明两种说法是正确的。首先,在算法的每次迭代中,源与残差图中所有其他顶点之间的最短路径必须增加<一个href="//www.parkandroid.com/wiki/monotonically/?wiki_title=monotonic" class="wiki_link new" title="单调" target="_blank" rel="nofollow">单调.也就是说,它是
单调递增路径长度
为了证明残差图中所有顶点的最短路径都是递增的,可以考虑一个矛盾。因此,考虑流量增加导致最短路径减少。想象一下,<年代pan class="katex"> 流之前是这样的强化吗<年代pan class="katex"> 是之后的流动。有一个顶点,<年代pan class="katex"> ,这样<年代pan class="katex"> 因为我们的矛盾流小于原始流。定义一个顶点,<年代pan class="katex"> ,这样就有了一条路径<年代pan class="katex"> 来<年代pan class="katex"> 通过<年代pan class="katex"> 这是最短路径<年代pan class="katex"> 来<年代pan class="katex"> 在<年代pan class="katex"> .也就是说最短路径<年代pan class="katex"> 来<年代pan class="katex"> 1比最短路径大吗<年代pan class="katex"> 来<年代pan class="katex"> 为简单起见,所以
最短路径<年代pan class="katex"> 来<年代pan class="katex"> 没有随着新的流量而减少吗<年代pan class="katex"> 因为我们的选择<年代pan class="katex"> ,所以
然而,边缘<年代pan class="katex"> 不能在残差图中,<年代pan class="katex"> .如果是,就会有以下关系(注意我们使用的是整型边长):
最后一个等式违背了这种关系
换句话说,最短路径实际上并没有随着流量而减少<年代pan class="katex"> .这个矛盾证明告诉我们最短路径在残差图上单调增长,它限定了Edmonds-Karp的一次迭代的长度为<年代pan class="katex"> .
的迭代次数
下一个证明涉及到Edmonds-Karp为了找到网络的最大流量而必须进行的迭代次数。我们的目标是证明有<年代pan class="katex"> Edmonds-Karp迭代。
边定义为<年代trong>至关重要的在一条增广路径上,当且仅当边的剩余容量等于路径的剩余容量。换句话说,临界边的容量将被这条扩展路径所填满。一旦我们增广了这条路径,这条有问题的边就会从残差网络中消失。我们必须证明每一个<年代pan class="katex"> 边缘最多是临界的<年代pan class="katex"> 次了。
让<年代pan class="katex"> 和<年代pan class="katex"> 为图中由一条边连接的顶点,并设<年代pan class="katex"> 是源。当这条边是关键的时候,距离关系就会
一旦这个流动扩大,正如我们已经看到的,边缘<年代pan class="katex"> 将从残差网络中消失。这条边不会再出现,除非水流从<年代pan class="katex"> 来<年代pan class="katex"> 是减小的,这只发生在边<年代pan class="katex"> 出现在扩展路径上。如果<年代pan class="katex"> 当这种情况发生时,心流是什么
回顾本节的第一个证明<年代pan class="katex"> ,我们有
最后一个方程非常重要。据说在这段时间里<年代pan class="katex">
第一次成为关键<年代pan class="katex">
当流<年代pan class="katex">
到下一个关键时刻<年代pan class="katex">
当流<年代pan class="katex">
光源与<年代pan class="katex">
增加了
最短路径上的中间顶点<年代pan class="katex"> 来<年代pan class="katex"> 不能包含<年代pan class="katex"> ,<年代pan class="katex"> ,或<年代pan class="katex"> ,也就是到顶点的距离<年代pan class="katex"> 最多是<年代pan class="katex"> .这个分数的分母是2,因为每次有一条边变得临界时,到源的距离至少增加2。所以,<年代pan class="katex"> 可以成为最关键的吗<年代pan class="katex"> 次数,总共为<年代pan class="katex"> 次了。
因为有<年代pan class="katex"> 这条边的顶点对总数<年代pan class="katex"> ,成为关键<年代pan class="katex"> 乘以,Edmonds-Karp可以经历的迭代总数是<年代pan class="katex">
埃德蒙斯-卡普在<一个href="//www.parkandroid.com/wiki/ford-fulkerson-algorithm/" class="wiki_link" title="Ford-Fulkerson算法" target="_blank">Ford-Fulkerson算法.Ford-Fulkerson简单地指出,流程在每次迭代中至少增加1。每次迭代需要<年代pan class="katex"> 时间。当我们看到<一个t一个rget="_blank" rel="nofollow" href="#complexity-proof">以上.所以,福特-福克森只能保证最大流量是在<年代pan class="katex"> ,在那里<年代pan class="katex"> 是最大流量本身。
Edmonds-Karp消除了最大流量对复杂性的依赖,使得它在具有较大最大流量的图上表现得更好,比如下面这个:
通过演示Edmonds-Karp在<年代pan class="katex"> 时间和那是最多的<年代pan class="katex"> 不断的重复,埃德蒙斯-卡普的边界<年代pan class="katex"> .
下面是Edmonds-Karp的Python实现:
12 34 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
|
它紧跟伪代码。唯一的大变化是,由于Python,广度优先搜索被抽象到它自己的函数中<一个href="//www.parkandroid.com/wiki/variables-and-scope/" class="wiki_link" title="范围" target="_blank">范围的原因。
如果我们从<一个t一个rget="_blank" rel="nofollow" href="#complexity">复杂的部分,我们可以用这个代码来求解它的最大流量。首先我们需要定义输入,邻接矩阵E
,容量矩阵C
,源年代
,还有水槽t
.
1 2 3 4 |
|
这里所发生的就是我们对图中的4个顶点进行了编号。源是0,A是1,B是2,汇是3。在邻接矩阵中[1,2]
是<年代pan class="katex">
列表的索引意味着有一条边从0到1,从0到2。在容量矩阵中C
,事实是[0, 1000000, 1000000, 0]
是在<年代pan class="katex">
Index表示从0到0和从0到3有0个容量,从0到1和从0到2有1000000个容量。
现在我们可以运行代码了。
1 2 |
|
如预期的那样,从源头到汇的最大流量是200万。