网络流(Network Flow)基础及Python实现

wuchangjian2021-11-16 17:38:46编程学习

首先感谢以下参考资料及其作者:
图论与网络流_图书搜索 (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)。下面就来举个栗子。
首先我们从零流(所有的流量都是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©。由于反向边的存在,又可以找到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()

相关文章

青海大通山洪受灾382户居民恢复正常用电

2022-08-20 11:02:49 记者从国网青海省电力公司了解到...

笨办法学Python第十三天:提示别人

当你键入 raw_input() 的时候,你需要键入 ( 和 ) 也就是“...

实验七 二叉树的创建与遍历(第10周)

实验七 二叉树的创建与遍历(第10周)

实验目的:   通过上机实验进一步掌握栈、队列、二叉树的存储结构及基本操...

发表评论    

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。