网络流(Network Flow)与Edmonds-Karp算法基础及Python实现

首先感谢以下参考资料及其作者:

图论与网络流_图书搜索> (duxiu.com)
最优化方法及其MATLAB实现 (duxiu.com)
Python networkx 实现网络流_Ugly> Gardon-CSDN博客
网络流与匹配> (yuyanwz.cn)
networkx求解最小费用最大流并可视化数据_engineoid的博客-CSDN博客

下面开始正文。

生活中人们常常利用有向图来对各种网络模型创建模型,如交通网络,通信网络,输电网络﹑油气管道网络、互联网、社会网络等。这些网络的共同特点是它们都是有向图,都有出发点,接收点、中转点,每条有向图都有传输能力的限制。例如,想象一个公路交通网络,其顶点是城市(或道路的交叉点),边即公路,边上的容量是指传输能力的大小,可以是货物的最大运输量。发点是提供货物的地方,收点是需要货物的地方,它可以接收其他顶点传送过来的货物。网络流理论还是图论中极其重要的分支,并且提供了图论中多个著名结论的证明。

图1展示的就是一个有网络流,每条有向边旁边的数字表示它的容量。网络流中有几个基础的定义:

  • 结点:在该点,流入的流量与流出的相同
  • 源点(source):在该点,流出的流量大于流入的流量
  • 汇点(sink):在该点,流入大于流出
  • 容量(capacity)和流量(value of a flow):每条有向边上有两个量,容量和流量,从i到j的容量通常用c[i,j]表示,流量则通常是f[i,j]。
    网络流的形象化表示

网络的最大流问题 and The Edmonds-Karp algorithm

如果将source 比作生产工厂,最大流问题就是求从工厂最大可以发出多少货物,并使不至于超过道路的容量限制的问题。
计算最大流最基本的方法之一便是埃德蒙兹卡普算法(The Edmonds-Karp algorithm,Wikipadia)。下面就来举个栗子。

首先我们从零流(所有的流量都是0的流)开始,假如有这么一条路,这条路从源点开始一直一段一段的连到了汇点,并且,这条路上的每一段都满足流量<容量。那么,我们一定能找到这条路上的每一段的(容量-流量)的值当中的最小值delta。我们把这条路上每一段的流量都加上这个delta,一定可以保证这个流依然是可行流,这是显然的。这样我们就得到了一个更大的流,他的流量是之前的流量+delta,显然当delta=0时就是该算法能求出的最大流了。
网络流的一个例子

以图2(a)的网络流模型为例,我们第一次找到了1-2-3-4这条流,当加上这条流之后,delta就变为0了(图2(b))。但是1显然不是最大流,因为如果同时选择1-2-4和1-3-4这条路径,就可以得到流量为2的流。
上述方法的问题就在于没有"反悔"的机制,而Edmonds-Karp algorithm就通过引入“反向边”解决了这一问题。
回到图2(a),选择1-2-3-4这条路之后就变成了图2(c)。由于反向边的存在,又可以找到1-3-2-4这条路,进而得到了如图2(d)所示的正确的最大流2。

现在如何解决这个问题?

1955年,Harris在研究铁路最大通量时首先提出在一个给定的网络上寻求两点间最大运输量的问题。1956年,Ford等人给出了解决这类问题的标号法,从而建立了网络流理论。现在我们不必像他们一样复杂地求解网络流问题,通过Python的第三方库NetworkX就可以快速地建立网络并求解。
NetworkX的运行结果
图3是NetworkX的运行结果,你也可以自己尝试运行下面的代码 。我认真地写了注释,希望能够帮到你。

import networkx as nx
import matplotlib.pyplot as plt

# 创建一个空的有向图
G = nx.DiGraph()

#绘制图
G.add_edges_from([('s','v1',{'capacity': 1}),
                  ('s','v2',{'capacity': 1}),
                  ('v1','v2',{'capacity': 1}),
                  ('v1','t',{'capacity': 1}),
                  ('v2','t',{'capacity': 1})])

pos=nx.spring_layout(G)#力导向布局算法默认分配的位置
#初始化,选定节点的初始位置/固定节点
pos['t'][0]=1;pos['t'][1]=0
pos['s'][0]=-1;pos['s'][1]=0
pos['v1'][0]=0;pos['v1'][1]=1
pos['v2'][0]=0;pos['v2'][1]=-1

#显示graph
#处理边上显示的容量
edge_label = nx.get_edge_attributes(G,'capacity')

#将节点、边的容量标签,边 这三个元素加入图中
nx.draw_networkx_nodes(G,pos)
nx.draw_networkx_labels(G,pos)
nx.draw_networkx_edges(G,pos)
nx.draw_networkx_edge_labels(G, pos,edge_label,font_size=15)#显示原图像

#求解最大流
maxFlow = nx.maximum_flow(G, 's', 't')
# print(maxFlow)#可以输出流信息看看
maxFlow_const=maxFlow[1]

#取出每条边流量信息存入边显示值
for i in maxFlow_const.keys():
    for j in maxFlow_const[i].keys():
        edge_label[(i,j)] = str(edge_label[(i,j)]) + ',F=' + str(maxFlow_const[i][j])

#将新的边的标签加入图中
nx.draw_networkx_edge_labels(G, pos,edge_label,font_size=12)

#显示流量及原图
plt.axis('on')
plt.xticks([])
plt.yticks([])
plt.show()

*代码是我原创的,如果要转载的话请标明出处,谢谢!

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值