Python小白的数学建模课-19.网络流优化问题


  • 流在生活中十分常见,例如交通系统中的人流、车流、物流,供水管网中的水流,金融系统中的现金流,网络中的信息流。网络流优化问题是基本的网络优化问题,应用非常广泛。
  • 网络流优化问题最重要的指标是边的成本和容量限制,既要考虑成本最低,又要满足容量限制,由此产生了网络最大流问题、最小费用流问题、最小费用最大流问题。
  • 本文基于 NetworkX 工具包,通过例程详细介绍网络最大流问题、最小费用流问题、最小费用最大流问题的建模和编程。
  • 『Python小白的数学建模课 @ Youcans』带你从数模小白成为国赛达人。


1. 网络流优化

1.1 网络流

网络流优化问题是基本的网络优化问题,应用非常广泛,遍及通讯、运输、电力、工程规划、任务分派、设备更新以及计算机辅助设计等领域。

流从源点流出、通过路径输送、流入到汇点,从而将目标从源点输送到汇点。流在生活中十分常见,例如交通系统中的人流、车流、物流,供水管网中的水流,金融系统中的现金流,网络中的信息流。

现实中的任何路径都有最大流量(容量)的限制,在网络中也是如此,并以边的容量(Capacity)表示,一条边的流量不能超过它的容量。

把这些现实问题抽象为网络流问题,其特征是:(1)有向图上的每条边具有容量限制;(2)从源点流出的流量,等于汇点流入的流量;(3)源点和汇点之外的所有中间节点,流出的流量等于流入的流量。

注意在网络流问题中有几组概念容易混淆:

  • 源点/汇点,起点/终点,供应点/需求点:源点是只进不出的点,汇点是只出不进的点。源点/汇点 可以指定为问题的 起点/终点,但本质上源点/汇点是由网络结构特征决定的,而不是被指定的。供应点的供应量和需求点的需求量是固定/确定的,而源点/汇点的目标是发出/接收的流量最大,不是固定值。
  • 容量 与 流量:容量是路径(边、弧)允许的最大流通能力,用 c(i,j) 表示;流量则是路径(边、弧)上的实际流量,用 f(i,j) 表示。

1.2 典型的网络流优化问题

网络流优化问题最重要的指标是每条边的成本和容量限制,既要考虑成本最低(最短路径问题),又要满足容量限制(最大流问题),由此产生了网络最大流问题、最小费用流问题、最小费用最大流问题。

最大流问题(Maximun flow problem):已知每条边的容量,研究如何充分利用网络能力,使从源点到汇点的总流量最大,也即在容量网络中求流量最大的可行流。

最小费用流问题(Minimum cost problem):已知每条边的容量和单位流量的费用,对于给定的源点、汇点流量,研究如何分配流量和路径,使总费用最小,也即在容量费用网络中求成本最低的可行流。

最小费用最大流问题(Minimum cost maximum flow),已知每条边的容量和单位流量的费用,研究网络的流量最大的路径中,费用最小的路径。简单地说,就是满足最大流的路径可能有多条,需要从其中找到成本最低的路径。

Network 工具包求解网络流优化,包括最大流算法、最小割算法、最小费用流算法、最小费用最大流算法、容量缩放最小成本流算法。



2. 网络最大流问题(MFP)

2.1 网络最大流算法

网络最大流问题,是在容量网络 G(V,E) 中求流量 v(f) 达到最大的可行流 f。在最大流问题中,只能有一个源点和一个汇点。

求解网络最大流主要有增广路法和预流推进法两类方法。

增广路方法从一条可行流开始,用 BFS 或 DFS 从源到汇找到一条增广路,沿着该路径修改流量,不断重复这个过程,直到找不到增广路时停止,此时的流就是最大流。增广路方法有多种的实现算法,如 Ford Fulkerson 标号法的算法复杂度为 O(Ef)O(E f)O(Ef)(不稳定),Edmonds Karp 算法的复杂度为 O(VE2)O(V E^2)O(VE2),Dinic 算法的复杂度为 O(V2E)O(V^2 E)O(V2E),ISAP 算法的复杂度也是 O(V2E)O(V^2 E)O(V2E),但其速度是最快的。

预流推进方法也称压入与重标记方法,算法从源点开始向下推流,通过不断地寻找活结点,将流量推向以该点为起点的可推流边(压入过程);如果在该点处找不到可推流边,则将该点的高度加 1,以实现将过大的流向后推进(重标记过程)。最高标号预流推进(HLPP)算法的复杂度为 O(V2E)O(V^2 E)O(V2E),改进的 HLPP 算法的复杂度为 O(V2(E))O(V^2 \sqrt{(E)})O(V2(E))


2.2 NetworkX 求解网络最大流问题

Network 工具包提供了多种求解网络最大流问题的算法和函数。其中 maximum_flow()、maximum_flow_value()、minimum_cut()、minimum_cut_value() 是集成了多种算法的通用函数,可以设置算法选项调用对应的算法;其它函数则是具体的算法实现函数。

函数功能
maximum_flow(flowG,s,t[, capacity,…])计算最大流
maximum_flow_value(flowG,s,t[,…])计算最大的单一目标流的值
minimum_cut(flowG,s,t[, capacity,flow_func])计算最小割的值和节点分区
minimum_cut_value(flowG,s,t[,capacity,…])计算最小割的值
edmonds_karp(G,s,t[,capacity,…])Edmonds-Karp 算法求最大流
shortest_augmenting_path(G,s,t[,…])SAP算法求最大流
dinitz(G,s,t[,capacity,…])Dinitz 算法求最大流
preflow_push(G,s,t[,capacity,…])HLPP 算法求最大流
boykov_kolmogorov(G,s,t[,capacity,…])Boykov-Kolmogorov 算法求最大流

2.3 maximum_flow() 函数说明

maximum_flow()maximum_flow_value() 是求解网络最大流问题的通用方法,集成了多种算法可供选择。官网介绍详见:https://networkx.org/documentation/stable/reference/algorithms/flow.html 。

maximum_flow (flowG, _s, _t, capacity=‘capacity’, flow_func=None, *kwargs)
maximum_flow_value (flowG, _s, _t, capacity=‘capacity’, flow_func=None, *kwargs)

主要参数:

  • flowG(NetworkX graph):有向图,边必须带有容量属性 capacity(不能用 ‘weight’ )。
  • _s (node):源点。
  • _t (node):汇点。
  • capacity (string):边的容量属性 capacity,缺省视为无限容量。
  • flow_func(function):调用算法的函数名,如:‘edmonds_karp’, ‘shortest_augmenting_path’, ‘dinitz’, ‘preflow_push’, ‘boykov_kolmogorov’。缺省值 ‘None’ ,选择 ‘preflow_push’(HLPP 算法)。

返回值:

  • flow_value(integer, float):网络最大流的流量值
  • flow_dict (dict):字典类型,网络最大流的流经路径及各路径的分配流量

注意:如果要选择指定算法,需要写成以下形式 flow_func=nx.algorithms.flow.edmonds_karp,而不是 flow_func=edmonds_karp。也可以写成:

from networkx.algorithms.flow import edmonds_karp
flowValue, flowDict = nx.maximum_flow(G1, 's', 't', flow_func=edmonds_karp) 

2.4 案例:输油管网的最大流量

问题描述:

在输油管网中,通过输油管连接生产石油的油井、储存石油的油库和转运的中间泵站。各站点之间的连接及管路的容量如图(参见 2.6 程序运行结果图)所示,求从油井到油库的最大流量和具体方案。

问题分析:

这是一个网络最大流问题,可以用顶点表示油井、油库和泵站,其中油井为源点 s、油库为汇点 t,用有向边表示输油管,有向边的权 capacity 表示输油管的最大流量(容量)。

用 NetworkX 的 maximum_flow() 函数即可求出从从源点 s 到汇点 t 的最大流量。

程序说明:

  1. 图的输入。本例为稀疏有向图,使用 nx.DiGraph() 定义一个有向图。通过 add_edge(‘s’, ‘a’, capacity=6) 定义有向边和属性 capacity。注意必须以关键字 ‘capacity’ 表示容量,不能用权值 ‘weight’ 或其它关键字代替。

  2. nx.maximum_flow_value() 返回网络最大流的值,nx.maximum_flow() 可以同时返回网络最大流的值和网络最大流的路径及分配的流量。

  3. maxFlowDict 为字典类型,具体格式参加 2.6 程序运行结果。为了得到最大流所流经的边的列表edgeLists,要对 maxFlowDict 进行整理和格式转换。

  4. 在网络最大流图中,以边的标签显示了边的容量 c 和流量 f。


2.5 Python 例程

# mathmodel19_v1.py
# Demo19 of mathematical modeling algorithm
# Demo of network flow problem optimization with NetworkX
# Copyright 2021 YouCans, XUPT
# Crated:2021-07-15import numpy as np
import matplotlib.pyplot as plt # 导入 Matplotlib 工具包
import networkx as nx  # 导入 NetworkX 工具包# 1. 最大流问题 (Maximum Flow Problem,MFP)
# 创建有向图
G1 = nx.DiGraph()  # 创建一个有向图 DiGraph
G1.add_edge('s', 'a', capacity=6)  # 添加边的属性 "capacity"
G1.add_edge('s', 'c', capacity=8)
G1.add_edge('a', 'b', capacity=3)
G1.add_edge('a', 'd', capacity=3)
G1.add_edge('b', 't', capacity=10)
G1.add_edge('c', 'd', capacity=4)
G1.add_edge('c', 'f', capacity=4)
G1.add_edge('d', 'e', capacity=3)
G1.add_edge('d', 'g', capacity=6)
G1.add_edge('e', 'b', capacity=7)
G1.add_edge('e', 'j', capacity=4)
G1.add_edge('f', 'h', capacity=4)
G1.add_edge('g', 'e', capacity=7)
G1.add_edge('h', 'g', capacity=1)
G1.add_edge('h', 'i', capacity=3)
G1.add_edge('i', 'j', capacity=3)
G1.add_edge('j', 't', capacity=5)# 求网络最大流
# maxFlowValue = nx.maximum_flow_value(G1, 's', 't')  # 求网络最大流的值
# maxFlowValue, maxFlowDict = nx.maximum_flow(G1, 's', 't')  # 求网络最大流
from networkx.algorithms.flow import edmonds_karp  # 导入 edmonds_karp 算法函数
maxFlowValue, maxFlowDict = nx.maximum_flow(G1, 's', 't', flow_func=edmonds_karp)  # 求网络最大流# 数据格式转换
edgeCapacity = nx.get_edge_attributes(G1, 'capacity')
edgeLabel = {}  # 边的标签
for i in edgeCapacity.keys():  # 整理边的标签,用于绘图显示edgeLabel[i] = f'c={edgeCapacity[i]:}'  # 边的容量
edgeLists = []  # 最大流的边的 list
for i in maxFlowDict.keys():for j in maxFlowDict[i].keys():edgeLabel[(i, j)] += ',f=' + str(maxFlowDict[i][j])  # 取出每条边流量信息存入边显示值if maxFlowDict[i][j] > 0:  # 网络最大流的边(流量>0)edgeLists.append((i,j))# 输出显示
print("最大流值: ", maxFlowValue)
print("最大流的途径及流量: ", maxFlowDict)  # 输出最大流的途径和各路径上的流量
print("最大流的路径:", edgeLists)  # 输出最大流的途径# 绘制有向网络图
fig, ax = plt.subplots(figsize=(8, 6))
pos = {'s': (1, 8), 'a': (6, 7.5), 'b': (9, 8), 'c': (1.5, 6), 'd': (4, 6), 'e': (8, 5.5),  # 指定顶点绘图位置'f': (2, 4), 'g': (5, 4), 'h': (1, 2), 'i': (5.5, 2.5), 'j': (9.5, 2), 't': (11, 6)}
edge_labels = nx.get_edge_attributes(G1, 'capacity')
ax.set_title("Maximum flow of petroleum network with NetworkX")  # 设置标题
nx.draw(G1, pos, with_labels=True, node_color='c', node_size=300, font_size=10)  # 绘制有向图,显示顶点标签
nx.draw_networkx_edge_labels(G1, pos, edgeLabel, font_color='navy')  # 显示边的标签:'capacity' + maxFlow
nx.draw_networkx_edges(G1, pos, edgelist=edgeLists, edge_color='m')  # 设置指定边的颜色、宽度
plt.axis('on')  # Youcans@XUPT
plt.show()

2.6 程序运行结果

最大流值:  14
最大流的途径及流量:  {'s': {'a': 6, 'c': 8}, 'a': {'b': 3, 'd': 3}, 'c': {'d': 4, 'f': 4}, 'b': {'t': 10}, 'd': {'e': 3, 'g': 4}, 't': {}, 'f': {'h': 4}, 'e': {'b': 7, 'j': 1}, 'g': {'e': 5}, 'j': {'t': 4}, 'h': {'g': 1, 'i': 3}, 'i': {'j': 3}}
最大流的路径: [('s', 'a'), ('s', 'c'), ('a', 'b'), ('a', 'd'), ('c', 'd'), ('c', 'f'), ('b', 't'), ('d', 'e'), ('d', 'g'), ('f', 'h'), ('e', 'b'), ('e', 'j'), ('g', 'e'), ('j', 't'), ('h', 'g'), ('h', 'i'), ('i', 'j')]

在这里插入图片描述



3. 最小费用流问题(MCP)

3.1 最小费用流问题的算法

在实际问题中,我们总是希望在完成运输任务的同时,寻求运输费用最低的方案。最小费用流问题,就是要以最小费用从出发点(供应点)将一定的流量输送到接收点(需求点)。在最小流问题中,供应点、需求点的数量可以是一个或多个,但每个供应点的供应量和需求点的需求量是固定的。

最小费用流问题可以用如下的线性规划问题描述
KaTeX parse error: No such environment: align* at position 8: \begin{̲a̲l̲i̲g̲n̲*̲}̲ & min\;Cost=\s…

求解最小费用流问题的方法很多,常见的如:连续最短路算法(Successive shortest path)、消圈算法(Cycle canceling)、原始对偶算法(Primal dual)、网络单纯性算法(Network simplex)和非均衡网络流算法(Out of Kilter法)等。

网络单纯形是单纯形算法的一个特殊应用,它使用生成树基来更有效地解决具有纯网络形式的线性规划问题。网络单纯性为最小费用流问题提供了标准的解决方法,可以解决数万个节点的大型问题。

最小费用流问题最重要的应用是配送网络的优化,如确定如何从出发地运送到中转站再转运到客户的配送方案。运输问题、指派问题、转运问题、最大流问题、最短路径问题,都是特殊情况下的最小费用流问题。例如,最短路径问题是流量 v=1 的最小费用流问题,最大流问题是最大流量下的最小费用流问题。只要选定合适的权重、容量、流量,解决最小费用流的方法就能用来解决上述问题。


3.2 NetworkX 求解最小费用流问题

Network 工具包提供了多个求解最小费用流问题的函数,所用的基本算法都是网络单纯性算法。

函数功能
network_simplex(G,[,demand,capacity,weight])单纯性法计算最小成本流
min_cost_flow_cost(G,[,demand,capacity,weight])计算最小成本流的成本
min_cost_flow(G,[,demand,capacity,weight])计算最小成本流
max_flow_min_cost(G,s,t[,capacity,weight])计算最小成本的最大流
capacity_scaling(G[,demand,capacity,…])计算容量缩放最小成本流

3.3 min_cost_flow() 函数说明

min_cost_flow()min_cost_flow_cost() 是求解费用最小流问题的函数,通过调用网络单纯性算法函数 network_simplex() 求解。

min_cost_flow(G, demand=‘demand’, capacity=‘capacity’, weight=‘weight’)
min_cost_flow_cost(G, demand=‘demand’, capacity=‘capacity’, weight=‘weight’)

主要参数:

  • G(NetworkX graph):有向图,边必须带有容量属性 capacity、单位成本属性 ‘weight’ 。
  • demand (string):顶点的需求量属性 demand,表示节点的净流量:负数表示供应点的净流出量,正数表示需求点的净流入量,0 表示中转节点。缺省值为 0。
  • capacity (string):边的容量,缺省视为无限容量。
  • weight (string):边的单位流量的费用,缺省值为 0。

返回值:

  • flowDict (dict):字典类型,最小费用流的流经路径及各路径的分配流量
  • flowCost(integer, float):满足需求的最小费用流的总费用
  • NetworkXUnfeasible:输入的净流量(demand)不平衡,或没有满足需求流量的可行流时,抛出异常信息。

注意:费用最小流函数 min_cost_flow() 中并没有设定供应点、需求点,而是通过设置顶点属性 ‘demand’ 确定供应点、需求点及各顶点的净流量,因而允许网络中存在多个供应点、需求点。


3.4 案例:运输费用

问题描述:

从 s 将货物运送到 t。已知与 s、t 相连各道路的最大运输能力、单位运量的费用如图所示(参见 3.6 程序运行结果图),图中边上的参数 (9,4) 表示道路的容量为 9,单位流量的费用为 4。求流量 v 的最小费用流。

问题分析:

这是一个最小费用流问题。用 NetworkX 的 nx.min_cost_flow() 函数或 nx.network_simplex() 函数即可求出从供应点到需求点的给定流量 v 的最小费用流。

程序说明:

  1. 图的输入。本例为稀疏的有向图,使用 nx.DiGraph() 定义一个有向图,用G.add_weighted_edges_from() 函数以列表向图中添加多条赋权边,每个赋权边以元组 (node1,node2,{‘capacity’:c1, ‘weight’:w1}) 定义属性 ‘capacity’ 和 ‘weight’。注意必须以关键字 ‘capacity’ 表示容量,以 ‘weight’ 表示单位流量的费用。
  2. nx.shortest_path() 用于计算最短路径,该段不是必须的。将最短路径的计算结果与最小费用流的结果进行比较,可以看到流量 v=1 时最小费用流的结果与最短路径结果是相同的。
  3. nx.cost_of_flow() 用于计算最小费用最大流,该段不是必须的。将最小费用最大流的计算结果与最小费用流的结果进行比较,可以看到在最大流量 v=14 时最小费用流的结果与最小费用最大流结果是相同的。
  4. 最小费用流是基于确定的流量 v 而言的。流量 v 可以在程序中赋值;例程中 v 从 1 逐渐递增,计算所有流量下的最小费用流,直到达到网络容量的极限(如果再增大流量将会超出网络最大容量,没有可行流,计算最小费用流失败)。
  5. NetworkX 计算最小费用流时不是在函数中指定源点、汇点和流量,而是通过向源点、汇点添加属性 demand 实现的。demand 为正值时表示净输入流量,demand 为负值时表示净输出流量,这使我们可以指定多源多汇。
  6. nx.min_cost_flow() 返回最小费用流的路径和流量分配,字典格式;nx.min_cost_flow_cost() 返回最小费用流的费用值。nx.network_simplex() 也可以求最小费用流,返回最小费用流的费用值,路径和流量分配。
  7. 在最小费用流图中(最大流量 v=14),以边的标签显示了边的容量 c、单位流量的费用 w 和流量 f,如 (8,4),f=7 表示边的容量为 8,单位流量的费用为 4,分配流量为 7。
  8. 在最小费用流图中(最大流量 v=14),以不同颜色(edge_color=‘m’)和宽度(width=2)表示最小费用流的边,未使用的流量为 0 (f=0)的边以黑色绘制。

3.5 Python 例程:

# mathmodel19_v1.py
# Demo19 of mathematical modeling algorithm
# Demo of network flow problem optimization with NetworkX
# Copyright 2021 YouCans, XUPT
# Crated:2021-07-15import numpy as np
import matplotlib.pyplot as plt # 导入 Matplotlib 工具包
import networkx as nx  # 导入 NetworkX 工具包# 2. 最小费用流问题(Minimum Cost Flow,MCF)
# 创建有向图
G2 = nx.DiGraph()  # 创建一个有向图 DiGraph
G2.add_edges_from([('s','v1',{'capacity': 7, 'weight': 4}),('s','v2',{'capacity': 8, 'weight': 4}),('v1','v3',{'capacity': 9, 'weight': 1}),('v2','v1',{'capacity': 5, 'weight': 5}),('v2','v4',{'capacity': 9, 'weight': 4}),('v3','v4',{'capacity': 6, 'weight': 2}),('v3','t',{'capacity': 10, 'weight': 6}),('v4','v1',{'capacity': 2, 'weight': 1}),('v4','t',{'capacity': 5, 'weight': 2})]) # 添加边的属性 'capacity', 'weight'
# 整理边的标签,用于绘图显示
edgeLabel1 = nx.get_edge_attributes(G2, 'capacity')
edgeLabel2 = nx.get_edge_attributes(G2, 'weight')
edgeLabel = {}
for i in edgeLabel1.keys():edgeLabel[i] = f'({edgeLabel1[i]:},{edgeLabel2[i]:})'  # 边的(容量,成本)# 计算最短路径---非必要,用于与最小费用流的结果进行比较
lenShortestPath = nx.shortest_path_length(G2, 's', 't', weight="weight")
shortestPath = nx.shortest_path(G2, 's', 't', weight="weight")
print("\n最短路径: ", shortestPath)  # 输出最短路径
print("最短路径长度: ", lenShortestPath)  # 输出最短路径长度# 计算最小费用最大流---非必要,用于与最小费用流的结果进行比较
minCostFlow = nx.max_flow_min_cost(G2, 's', 't')  # 求最小费用最大流
minCost = nx.cost_of_flow(G2, minCostFlow)  # 求最小费用的值
maxFlow = sum(minCostFlow['s'][j] for j in minCostFlow['s'].keys())  # 求最大流量的值
print("\n最大流量: {}".format(maxFlow))  # 输出最小费用的值
print("最大流量的最小费用: {}\n".format(minCost))  # 输出最小费用的值# v = input("Input flow (v>=0):")
v = 0
while True:v += 1  # 最小费用流的流量G2.add_node("s", demand=-v)  # nx.min_cost_flow() 的设置要求G2.add_node("t", demand=v)  # 设置源点/汇点的流量try: # Youcans@XUPT# 求最小费用流(demand=v)minFlowCost = nx.min_cost_flow_cost(G2)  # 求最小费用流的费用minFlowDict = nx.min_cost_flow(G2)  # 求最小费用流# minFlowCost, minFlowDict = nx.network_simplex(G2)  # 求最小费用流--与上行等效print("流量: {:2d}\t最小费用:{}".format(v, minFlowCost))  # 输出最小费用的值(demand=v)# print("最小费用流的路径及流量: ", minFlowDict)  # 输出最大流的途径和各路径上的流量except Exception as e:print("流量: {:2d}\t超出网络最大容量,没有可行流。".format(v))print("\n流量 v={:2d}:计算最小费用流失败({})。".format(v, str(e)))break  # 结束 while True 循环edgeLists = []
for i in minFlowDict.keys():for j in minFlowDict[i].keys():edgeLabel[(i, j)] += ',f=' + str(minFlowDict[i][j])  # 取出每条边流量信息存入边显示值if minFlowDict[i][j] > 0:edgeLists.append((i, j))maxFlow = sum(minFlowDict['s'][j] for j in minFlowDict['s'].keys())  # 求最大流量的值
print("\n最大流量: {:2d},\t最小费用:{}".format(maxFlow, minFlowCost))  # 输出最小费用的值
print("最小费用流的路径及流量: ", minFlowDict)  # 输出最小费用流的途径和各路径上的流量
print("最小费用流的路径:", edgeLists)  # 输出最小费用流的途径# 绘制有向网络图
pos={'s':(0,5),'v1':(4,2),'v2':(4,8),'v3':(10,2),'v4':(10,8),'t':(14,5)}  # 指定顶点绘图位置
fig, ax = plt.subplots(figsize=(8,6))
ax.text(6,2.5,"youcans-xupt",color='gainsboro')
ax.set_title("Minimum Cost Maximum Flow with NetworkX")
nx.draw(G2,pos,with_labels=True,node_color='c',node_size=300,font_size=10)   # 绘制有向图,显示顶点标签
nx.draw_networkx_edge_labels(G2,pos,edgeLabel,font_size=10)  # 显示边的标签:'capacity','weight' + minCostFlow
nx.draw_networkx_edges(G2,pos,edgelist=edgeLists,edge_color='m',width=2)  # 设置指定边的颜色、宽度
plt.axis('on')
plt.show()

3.6 程序运行结果:

最短路径:  ['s', 'v1', 'v3', 'v4', 't']
最短路径长度:  9最大流量: 14
最大流量的最小费用: 159流量:  1	最小费用:9
流量:  2	最小费用:18
流量:  3	最小费用:27
流量:  4	最小费用:36
流量:  5	最小费用:45
流量:  6	最小费用:56
流量:  7	最小费用:67
流量:  8	最小费用:79
流量:  9	最小费用:91
流量: 10	最小费用:103
流量: 11	最小费用:115
流量: 12	最小费用:127
流量: 13	最小费用:143
流量: 14	最小费用:159
流量: 15	超出网络最大容量,没有可行流。流量 v=15:计算最小费用流失败(no flow satisfies all node demands)。最大流量: 14,	最小费用:159
最小费用流的路径及流量:  {'s': {'v1': 7, 'v2': 7}, 'v1': {'v3': 9}, 'v2': {'v1': 2, 'v4': 5}, 'v3': {'v4': 0, 't': 9}, 'v4': {'v1': 0, 't': 5}, 't': {}}
最小费用流的路径: [('s', 'v1'), ('s', 'v2'), ('v1', 'v3'), ('v2', 'v1'), ('v2', 'v4'), ('v3', 't'), ('v4', 't')]

在这里插入图片描述



4. 最小费用最大流问题(MCMF)

4.1 最小费用最大流问题的算法

最小成本最大流问题可以看做是最短路径问题和最大流问题的结合,既要像最短路径问题那样考虑成本最小,又要考虑到每条边上的流量限制。最短路径问题和最大流问题在本质上也是特殊的最小成本最大流问题,是网络优化中的基本问题。

求解最小费用最大流问题的常用方法有 Bellman-Ford算法、SPFA算法、Dijkstra 改进算法。

在 NetworkX 工具包中,求解最小费用最大流问题的方法与众不同:先调用 nx.maximum_flow_value() 函数求网络最大流,再以最大流调用 min_cost_flow() 函数求网络最大流时的最小费用流。哈哈,这样的处理方式,与本系列博文的思想十分吻合:容易理解,容易实现,容易掌握。


4.2 max_flow_min_cost() 函数说明

max_flow_min_cost()是求解最小费用最大流问题的函数。

max_flow_min_cost(G, s, t, capacity=‘capacity’, weight=‘weight’)
cost_of_flow(G, flowDict, weight=‘weight’)

主要参数:

  • G(NetworkX graph):有向图,边必须带有容量属性 capacity、单位成本属性 ‘weight’ 。
  • s (node):流的源点。
  • t (node):流的汇点。
  • capacity (string):边的容量,缺省视为无限容量。
  • weight (string):边的单位流量的费用,缺省值为 0。

返回值:

  • flowDict (dict):字典类型,最小费用最大流的流经路径及各路径的分配流量。

使用 cost_of_flow() 函数,可以由流经路径及各路径的分配流量 flowDict 计算可行流的成本。


4.3 案例:输油管网的最大流量和最小费用

问题描述:

某输油网络 G 中的每段管路允许的容量和单位流量的运输费用如图所示(参见4.5 程序运行结果图),图中边上的参数 (9,5) 表示边的容量为 9,单位流量的费用为 5。求从网络源点 s 到汇点 t 的最大流量,及输送最大流量的最小费用。

问题分析:

这是一个的最小费用最大流问题。用 NetworkX 的 nx.max_flow_min_cost() 函数可以求出从网络源点到汇点的最小费用最大流。

程序说明:

  1. 图的输入。用 nx.DiGraph() 定义一个有向图。用 G.add_weighted_edges_from() 函数以列表向图中添加多条赋权边,每个赋权边以元组 (node1,node2,{‘capacity’:c1, ‘weight’:w1}) 定义属性 ‘capacity’ 和 ‘weight’。注意必须以关键字 ‘capacity’ 表示容量,以 ‘weight’ 表示单位流量的费用。
  2. nx.max_flow_min_cost(G3, ‘s’, ‘t’) 用来计算从源点 ‘s’ 到汇点 ‘t’ 的最小费用最大流,返回最大流的途径和各路径上的流量分配,字典格式。
  3. nx.cost_of_flow() 计算一个可行流的费用,例程中用来计算最小费用最大流的费用。
  4. maxFlow 计算从源点 ‘s’ 发出的所有路径上的流量总和,得到最大流量的值。
  5. 在最小费用最大流图中,以边的标签显示了边的容量 c、单位流量的费用 w 和流量 f,如 (13,7),f=11表示边的容量为 13,单位流量的费用为 7,分配流量为 11。
  6. 在最小费用最大流图中,以不同颜色(edge_color=‘m’)和宽度(width=2)表示最小费用流的边,未使用的流量为 0 (f=0)的边以黑色绘制。

4.4 Python 例程

# mathmodel19_v1.py
# Demo19 of mathematical modeling algorithm
# Demo of network flow problem optimization with NetworkX
# Copyright 2021 YouCans, XUPT
# Crated:2021-07-15import numpy as np
import matplotlib.pyplot as plt # 导入 Matplotlib 工具包
import networkx as nx  # 导入 NetworkX 工具包# 3. 最小费用最大流问题(Minimum Cost Maximum Flow,MCMF)
# 创建有向图
G3 = nx.DiGraph()  # 创建一个有向图 DiGraph
G3.add_edges_from([('s','v1',{'capacity': 13, 'weight': 7}),('s','v2',{'capacity': 9, 'weight': 9}),('v1','v3',{'capacity': 6, 'weight': 6}),('v1','v4',{'capacity': 5, 'weight': 5}),('v2','v1',{'capacity': 4, 'weight': 4}),('v2','v3',{'capacity': 5, 'weight': 2}),('v2','v5',{'capacity': 5, 'weight': 5}),('v3','v4',{'capacity': 5, 'weight': 2}),('v3','v5',{'capacity': 4, 'weight': 1}),('v3','t',{'capacity': 4, 'weight': 4}),('v4','t', {'capacity': 9, 'weight': 7}),('v5','t',{'capacity': 9, 'weight': 5})]) # 添加边的属性 'capacity', 'weight'# 求最小费用最大流
minCostFlow = nx.max_flow_min_cost(G3, 's', 't')  # 求最小费用最大流
minCost = nx.cost_of_flow(G3, minCostFlow)  # 求最小费用的值
maxFlow = sum(minCostFlow['s'][j] for j in minCostFlow['s'].keys())  # 求最大流量的值# # 数据格式转换
edgeLabel1 = nx.get_edge_attributes(G3,'capacity')  # 整理边的标签,用于绘图显示
edgeLabel2 = nx.get_edge_attributes(G3,'weight')
edgeLabel={}
for i in edgeLabel1.keys():edgeLabel[i]=f'({edgeLabel1[i]:},{edgeLabel2[i]:})'  # 边的(容量,成本)
edgeLists = []
for i in minCostFlow.keys():for j in minCostFlow[i].keys():edgeLabel[(i, j)] += ',f=' + str(minCostFlow[i][j])  # 将边的实际流量添加到 边的标签if minCostFlow[i][j]>0:edgeLists.append((i,j))print("最小费用最大流的路径及流量: ", minCostFlow)  # 输出最大流的途径和各路径上的流量
print("最小费用最大流的路径:", edgeLists)  # 输出最小费用最大流的途径
print("最大流量: ", maxFlow)  # 输出最大流量的值
print("最小费用: ", minCost)  # 输出最小费用的值# 绘制有向网络图
pos={'s':(0,5), 'v1':(3,9), 'v2':(3,1), 'v3':(6,5), 'v4':(9,9),'v5':(9,1), 't':(12,5)}  # 指定顶点绘图位置
fig, ax = plt.subplots(figsize=(8,6))
ax.text(5,1.5,"youcans-xupt",color='gainsboro')
ax.set_title("Minimum Cost Maximum Flow with NetworkX")
nx.draw(G3,pos,with_labels=True,node_color='c',node_size=300,font_size=10)   # 绘制有向图,显示顶点标签
nx.draw_networkx_edge_labels(G3,pos,edgeLabel,font_size=10)  # 显示边的标签:'capacity','weight' + minCostFlow
nx.draw_networkx_edges(G3,pos,edgelist=edgeLists,edge_color='m',width=2)  # 设置指定边的颜色、宽度
plt.axis('on') # Youcans@XUPT
plt.show()

4.5 运行结果

最小费用最大流的路径及流量:  {'s': {'v1': 11, 'v2': 9}, 'v1': {'v3': 6, 'v4': 5}, 'v2': {'v1': 0, 'v3': 4, 'v5': 5}, 'v3': {'v4': 2, 'v5': 4, 't': 4}, 'v4': {'t': 7}, 'v5': {'t': 9}, 't': {}}
最小费用最大流的路径: [('s', 'v1'), ('s', 'v2'), ('v1', 'v3'), ('v1', 'v4'), ('v2', 'v3'), ('v2', 'v5'), ('v3', 'v4'), ('v3', 'v5'), ('v3', 't'), ('v4', 't'), ('v5', 't')]
最大流量:  20
最小费用:  370

在这里插入图片描述



5. 总结

  1. 本文基于 NetworkX 工具包,通过例程详细介绍了网络最大流问题、最小费用流问题、最小费用最大流问题的建模和编程。
  2. 运输问题、指派问题、转运问题、最大流问题、最短路径问题,都是特殊情况下的最小费用流问题。通过 3.6 中最短路径、最小费用最大流的结果与 v=1、v=14 的最小费用流结果的比较,可以理解这种关系。
  3. 例程给出了对部分指定的边设置颜色,为边设置指定格式的显示内容,NetworkX 函数输出值的数据格式转换的编程方法,建议读者多加留意。
  4. 网络流优化问题还有很多变形和衍生问题,将在今后的文中进行介绍。

【本节完】



版权声明:

欢迎关注『Python小白的数学建模课 @ Youcans』 原创作品

原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/118726642)。

Copyright 2021 Youcans, XUPT

Crated:2021-07-16


欢迎关注 『Python小白的数学建模课 @ Youcans』 系列,持续更新
Python小白的数学建模课-01.新手必读
Python小白的数学建模课-02.数据导入
Python小白的数学建模课-03.线性规划
Python小白的数学建模课-04.整数规划
Python小白的数学建模课-05.0-1规划
Python小白的数学建模课-06.固定费用问题
Python小白的数学建模课-07.选址问题
Python小白的数学建模课-09.微分方程模型
Python小白的数学建模课-10.微分方程边值问题
Python小白的数学建模课-12.非线性规划
Python小白的数学建模课-15.图论的基本概念
Python小白的数学建模课-16.最短路径算法
Python小白的数学建模课-17.条件最短路径算法
Python小白的数学建模课-18.最小生成树问题
Python小白的数学建模课-19.网络流优化问题
Python小白的数学建模课-A1.国赛赛题类型分析
Python小白的数学建模课-A2.2021年数维杯C题探讨
Python小白的数学建模课-A3.12个新冠疫情数模竞赛赛题及短评
Python小白的数学建模课-B2. 新冠疫情 SI模型
Python小白的数学建模课-B3. 新冠疫情 SIS模型
Python小白的数学建模课-B4. 新冠疫情 SIR模型
Python小白的数学建模课-B5. 新冠疫情 SEIR模型
Python小白的数学建模课-B6. 新冠疫情 SEIR改进模型
Python数模笔记-PuLP库
Python数模笔记-StatsModels统计回归
Python数模笔记-Sklearn
Python数模笔记-NetworkX
Python数模笔记-模拟退火算法

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/566107.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python小白的数学建模课-20.网络流优化案例

在实际工作和数模竞赛中,网络最大流问题、最小费用流问题和最小费用最大流问题都有很多延伸和推广的应用。本文介绍了常见的网络最大流问题、最小费用流问题的应用与推广,及其解题方法。本文选择多源多汇物流转运问题、多商品流问题案例,详细…

HTML文档类型

HTML文档类型 HTML5 <!DOCTYPE html> HTML4.01 <!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01 Transitional//EN” “http://www.w3.org/TR/html4/loose.dtd”> XHTML1.0 <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w…

Python小白的数学建模课-21.关键路径法

Python小白的数学建模课-21.关键路径法 关键路径法是基于进度网络模型的方法&#xff0c;用网络图表示各项活动之间的相互关系&#xff0c;获得在一定工期、成本、资源约束条件下的最优进度安排。NetworkX 提供了拓扑序列和关键路径的函数&#xff0c;但没有给出计划网络分析的…

swift面向对象之多态与继承

swift面向对象之多态与继承 1.多态 运行时多态 运行时多态的要点 1.要有继承关系的存在 2.子类要重写父类的方法 3.父类类型的引用指向子类实例 2.继承 1.继承的注意 继承可以用于类而不能用于结构体和枚举&#xff0c;继承也可用于协议 swift中不能使用多继承&#xff0c;oc中…

Python小白的数学建模课-22.插值方法

Python小白的数学建模课-22.插值方法 插值、拟合、回归和预测&#xff0c;都是数学建模中经常提到的概念&#xff0c;也经常被混淆。插值&#xff0c;是在离散数据的基础上补插连续函数&#xff0c;使得插值函数通过全部给定的离散数据点&#xff0c;多用于图像处理和缺失数据处…

Python小白的数学建模课-23.数据拟合全集

拟合是用一个连续函数&#xff08;曲线&#xff09;靠近给定的离散数据&#xff0c;使其与给定的数据相吻合。数据拟合的算法相对比较简单&#xff0c;但调用不同工具和方法时的函数定义和参数设置有所差异&#xff0c;往往使小白感到困惑。本文基于 Scipy 工具包&#xff0c;对…

Css背景

Css背景 1.背景图像&#xff1a;background-image 要把图像放入背景&#xff0c;需要使用background-image 属性。background-image属性的默认值是 none&#xff0c;表示背景上没有放置任何图像。 如果需要设置一个背景图像&#xff0c;必须为这个属性设置一个 URL值&#xff…

Python小白的数学建模课-11.偏微分方程数值解法

偏微分方程可以描述各种自然和工程现象&#xff0c; 是构建科学、工程学和其他领域的数学模型主要手段。 偏微分方程主要有三类&#xff1a;椭圆方程&#xff0c;抛物方程和双曲方程。 本文采用有限差分法求解偏微分方程&#xff0c;通过案例讲解一维平流方程、一维热传导方程…

Css语法

Css语法 如果值为若干个单词&#xff0c;加引号 p {font-family: “sans serif”;} 多重声明&#xff1a;分号隔开 如果要定义不止一个声明&#xff0c;则需要用分号把声明隔开。如 p {text-align:center; color:red;} 你应该在每行只描述一个属性&#xff0c;这样可以增强…

A4.2021年全国数学建模竞赛A题-赛题分析与评阅要点(FAST主动反射面的形状调节)

Python小白的数学建模课-A4.2021年全国数学建模竞赛A题&#xff08;FAST主动反射面的形状调节&#xff09;&#xff0c;本文转载竞赛赛题、评阅要点&#xff0c;进行赛题解读和分析。 评阅要点为竞赛组委会官方公布&#xff0c;完整体现了解题思路。 『Python小白的数学建模课…

Css字体

Css字体 使用像素 Firefox&#xff0c;Chrome&#xff0c;and Safari中文本大小可调节&#xff0c;而ie不行 使用em em可以解决在ie中浏览器显示文本的问题。w3c推荐用em作为尺寸单位。 1em等于当前的字体尺寸&#xff0c;如果一个元素的font-size为16像素&#xff0c;那么对…

A5.2021年全国数学建模竞赛B题-赛题分析与评阅要点(乙醇偶合制备C4烯烃分析)

A5.2021年全国数学建模竞赛B题-赛题分析与评阅要点&#xff08;乙醇偶合制备C4烯烃分析&#xff09;&#xff0c;本文转载竞赛赛题、评阅要点&#xff0c;进行赛题解读和分析。 评阅要点为竞赛组委会官方公布&#xff0c;完整体现了解题思路。 本文首发于 2021年9月8日&#…

emmet插件使用(Css)

emmet插件使用(Css) 渐变 输入lg(left,#fff50%,#000),会生成如下代码 background-image: -webkit-gradient(linear, 0 0, 100% 0, color-stop(0.5, #fff), to(#000)); background-image: -webkit-linear-gradient(left, #fff 50%, #000); background-image: -moz-linear-gradie…

Python 小白从零开始 PyQt5 项目实战(1)安装与环境配置

本系列面向 Python 小白&#xff0c;从零开始实战解说应用 QtDesigner 进行 PyQt5 的项目实战。 什么叫从零开始&#xff1f;从软件安装、环境配置开始。 不跳过一个细节&#xff0c;不漏掉一行代码&#xff0c;不省略一个例图。 欢迎关注『Python 小白从零开始 PyQt5 项目实战…

emmet使用(HTML)

emmet使用(HTML) htmlTab直接生成固定标签 <!DOCTYPE html> <html> <head> <title></title> </head> <body> </body> 分组&#xff1a;可以通过嵌套来快速生成一些代码块 (.foo>h1)(.bar>h2) 隐式标签 在过去的版本中省…

Python 小白从零开始 PyQt5 项目实战(2)菜单和工具栏

本系列面向 Python 小白&#xff0c;从零开始实战解说应用 QtDesigner 进行 PyQt5 的项目实战。 什么叫从零开始&#xff1f;从软件安装、环境配置开始。不跳过一个细节&#xff0c;不漏掉一行代码&#xff0c;不省略一个例图。 本文详细解读通过 QtDesigner 创建主窗口、菜单栏…

Python 小白从零开始 PyQt5 项目实战(3)信号与槽的连接

本系列面向 Python 小白&#xff0c;从零开始实战解说应用 QtDesigner 进行 PyQt5 的项目实战。 什么叫从零开始&#xff1f;从软件安装、环境配置开始。不跳过一个细节&#xff0c;不漏掉一行代码&#xff0c;不省略一个例图。 本文讲解信号与槽的连接机制&#xff0c;详细示范…

Css内边距与外边距

Css内边距与外边距 Css内边距 Css外边距margin Css外边距margin 设置外边距最简单的方法就是margin属性。margin属性接受任何长度单位&#xff0c;可以是像素&#xff0c;英寸&#xff0c;毫米或em margin可以设置为auto。更常见的做法就是为外边距设置长度值。下面的声明在h1…

Python 小白从零开始 PyQt5 项目实战(4)基本控件

本系列面向 Python 小白&#xff0c;从零开始实战解说应用 QtDesigner 进行 PyQt5 的项目实战。 什么叫从零开始&#xff1f;从软件安装、环境配置开始。不跳过一个细节&#xff0c;不漏掉一行代码&#xff0c;不省略一个例图。 PyQt5 提供了丰富的输入输出控件。本文介绍通过 …

Python 小白从零开始 PyQt5 项目实战(5)布局管理

本系列面向 Python 小白&#xff0c;从零开始实战解说应用 QtDesigner 进行 PyQt5 的项目实战。 什么叫从零开始&#xff1f;从软件安装、环境配置开始。不跳过一个细节&#xff0c;不漏掉一行代码&#xff0c;不省略一个例图。 布局管理就是管理图形窗口中各个部件的位置和排列…