Python小白的数学建模课-20.网络流优化案例
Posted youcans
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python小白的数学建模课-20.网络流优化案例相关的知识,希望对你有一定的参考价值。
- 在实际工作和数模竞赛中,网络最大流问题、最小费用流问题和最小费用最大流问题都有很多延伸和推广的应用。
- 本文介绍了常见的网络最大流问题、最小费用流问题的应用与推广,及其解题方法。
- 本文选择多源多汇物流转运问题、多商品流问题案例,详细介绍网络流问题的分析方法和解决方案,并使用线性规划方法建模和编程。
- 『Python小白的数学建模课 @ Youcans』带你从数模小白成为国赛达人。
1. 网络流问题的应用与推广
流在生活中十分常见,例如交通系统中的人流、车流、物流,供水管网中的水流,金融系统中的现金流,网络中的信息流。网络流优化问题是基本的网络优化问题,应用非常广泛,遍及通讯、运输、电力、工程规划、任务分派、设备更新以及计算机辅助设计等领域。
网络流优化问题最重要的指标是每条边的成本和容量限制,既要考虑成本最低(最短路径问题),又要满足容量限制(最大流问题),由此产生了网络最大流问题、最小费用流问题和最小费用最大流问题。在《Python小白的数学建模课-19.网络流优化》一文中,我们详细介绍了网络最大流问题、最小费用流问题和最小费用最大流问题的算法、编程和案例。
不论在实际工作中,还是在数模竞赛中,网络最大流问题、最小费用流问题和最小费用最大流问题都有很多延伸和推广的应用。小白同学对于网络图的概念和思维逻辑比较生疏,面对变换和深化的题型往往对题目能有基本判断和思路、但找不到具体的解决方法,觉得应该能做出来但就是差一点。
本文选择多源多汇物流转运问题、多商品流问题案例,详细介绍网络流问题的分析方法和解决方案,并使用线性规划方法建模和编程。
对于小白同学来说,这两个案例的建模和编程都有些复杂。别急,也别怕,问题分析和程序说明都很详细,只要静下来慢慢读,将程序说明、源程序和运行结果对照着读,相信小白同学也能看懂。
2. 网络最大流问题的应用与推广
2.1 指定流量的可行流
我们在《19.网络流优化》一文讨论了网络最大流的算法。如果不是计算最大流,而是要计算指定流量 v 的可行流,可以用如下图所示的增广路(相当于辅助线)方法处理。
(1)在容量网络 G 上增加一个顶点 t’ 和一条边 (t,t’),构建一个新的容量网络 G’,令边 (t,t’) 的容量为 v;
(2)对于容量网络 G‘,计算从源点 s 到汇点 t’ 的最大流 f m a x f_{max} fmax,显然 f m a x ≤ v f_{max}\\leq v fmax≤v;
(3)若 v < f m a x v<f_{max} v<fmax,则容量网络 G 不存在满足流量 v 的可行流;若 v = f m a x v=f_{max} v=fmax,则从最大流 f m a x f_{max} fmax 的路径中去掉边 (t,t’) 即得到容量网络 G 满足流量 v 的可行流。
另一种思路是,设置源点 s /汇点 t 的流量为 v,再按最小费用流即可。这种方法不仅得到了流量 v 的可行流,而且是最小费用流,但该方法的计算量较大。主要程序如下,完整程序详见《Python小白的数学建模课-19.网络流优化》。
G2.add_node("s", demand=-v) # nx.min_cost_flow() 的设置要求
G2.add_node("t", demand=v) # 设置源点/汇点的流量
# 求最小费用流(demand=v)
minFlowCost = nx.min_cost_flow_cost(G2) # 求最小费用流的费用
minFlowDict = nx.min_cost_flow(G2) # 求最小费用流
2.2 多源点多汇点的最大流
《19.网络流优化》一文讨论的网络流优化问题,都是单源点、单汇点的网络。对于多源点单汇点、单源点多汇点、多源点多汇点的容量网络的最大流问题,可以通过如下图所示的增加一个超级源点和/或超级汇点的方法处理。
(1)在容量网络 G 上增加一个超级源点 s 和一个超级源点 t;从超级源点 s 向网络 G 中的每个源点连一条容量为 + ∞ +\\infty +∞ 的边,从网络 G 中的每个汇点向超级汇点 t 连一条容量为 + ∞ +\\infty +∞ 的边。于是得到了一个新的容量网络 G’。
(2)对于容量网络 G‘,计算从超级源点 s 到超级汇点 t 的最大流 f m a x f_{max} fmax,即为多源多汇容量网络 G 的最大流。
使用 NetworkX 工具包,也有另一种特殊方法可以解决多源多汇最大流问题。
NetworkX 中求解费用最小流问题的函数 min_cost_flow()
、min_cost_flow_cost()
是求解费用最小流问题的函数,并不是设定供应点、需求点,而是通过设置顶点属性 ‘demand’ 确定供应点、需求点及各顶点的净流量,因而允许网络中存在多个供应点、需求点。
min_cost_flow(G, demand=‘demand’, capacity=‘capacity’, weight=‘weight’)
min_cost_flow_cost(G, demand=‘demand’, capacity=‘capacity’, weight=‘weight’)
因此,对于指定流量的多源多汇网络可行流问题,只要在每个源点、汇点的顶点属性 ‘demand’ 设置对应的供应量、需求量,再按最小费用流即可。这种方法不仅得到了流量 v 的可行流,而且是最小费用流。类似地,对于多源多汇网络的最大流问题,可以参考《19.网络流优化》中“3.4 案例:运输费用”的方法,v 从 1 逐渐递增,计算所有流量下的最小费用流,直到达到网络容量的极限,即得到容量网络的最大费用流。该方法不需要构造新的容量网络,编程实现简单,而且可以获得最大流量的最小费用流,但计算量很大。
2.3 带顶点容量约束的最大流
标准形式的网络流优化问题,只有边的容量约束,没有顶点的容量约束。在实际问题中,顶点所表示的工厂、仓库、销售点,都存在最大储存量,因而带有顶点的容量约束。
对于顶点容量约束的网络流问题,可以通过如下图所示的将每个带约束的顶点 N 分拆为两个顶点(入顶点 Nin 和出顶点 Nout)的方法处理:
(1)将指向顶点 N 的边,改为指向入顶点 Nin;
(2)将顶点 N 所指向的边,改为出顶点 Nout 所指向的边;
(3)增加一条从入顶点 Nin 指向出顶点 Nout 的边,边的容量为顶点 N 的容量,单位费用为 0。
于是得到了一个新的容量网络 G’。由此,原有网络 G 的顶点 N 的容量限制,转化为网络 G’ 的边 (Nin,Nout) 的容量限制。带顶点约束的容量网络 G 的最大流问题,转化为新的标准形式的容量网络 G’ 的最大流问题。
3. 最小费用流问题的应用与推广
3.1 运输问题
有出发地(供应点、供应量)和目的地(需求点、需求量),没有转运点,没有路段(边)的容量限制,目标是总运输成本最小或总利润最大。
3.2 指派问题
出发地(供应点、供应量=1)是人,目的地(需求点、需求量=1)是任务,没有转运点,没有路段(边)的容量限制,目标是总指派成本最小或总利润最大。
3.3 转运问题
有出发地(供应点、供应量)和目的地(需求点、需求量),有转运点,没有路段(边)的容量限制(或带有容量限制),目标是总流量费用最小或总利润最大。
3.4 最大流问题
最大流问题是特殊的最小费用流问题:有供应点、需求点、转运点,有路段(边)的容量限制,没有供应量和需求量的限制,目标是通过网络到目的地的总流量最大。
3.5 最短路问题
有供应点(供应量=1)、需求点(需求量=1)、转运点,没有路段(边)的容量限制,目标是通过网络到目的地的总距离最短。
4. 案例:多源多汇的物流转运问题
4.1 问题描述
如图所示,某公司的工厂(供应点)位于F1、F2,仓库(中转点)位于W1、W2,零售商(需求点)位于R1、R2、R3、R4。从工厂生产的产品先送到仓库,再由仓库发到零售商。图中给出了各供应点和需求点的流量、每条发货线路的最大流量、单位流量的运输成本,要求在产销平衡的条件下,找出总运输费用最小的运输方案。
4.2 问题分析
这是一个运输路径规划问题,也是一个多源点、多汇点的最小费用流问题。
对于多源多汇的最小费用流问题,构造一个费用网络 G,网络 G 的各条边没有容量限制,单位流量的运输成本为边的权值 w。F1、F2 是网络 G 的源点(供应点),R1~R4 是汇点(需求点),W1、W2 是中间节点。F1、F2 的供应量与 R1~R4 的需求量是平衡的。因此可以用 NetworkX 的 maximum_flow() 函数求出最小费用流。
该问题也是一个典型的线性规划问题,可以用线性规划方法求解。
m
i
n
∑
i
,
j
∈
E
w
i
j
f
i
j
s
.
t
.
:
{
∑
j
f
i
j
≤
−
v
i
,
i
∈
S
∑
j
f
j
i
=
v
i
,
i
∈
T
∑
i
f
i
k
−
∑
j
f
k
j
=
0
,
k
∉
S
,
T
min\\;\\sum_{i,j\\in E} w_{ij}f_{ij}\\\\ s.t.:\\;\\begin{cases} \\sum_j f_{ij} \\leq -v_i \\;,i \\in S\\\\ \\sum_j f_{ji}=v_i \\;,i \\in T\\\\ \\sum_i f_{ik} - \\sum_j f_{kj} = 0 \\;,k\\notin S,T\\\\ \\end{cases}
mini,j∈E∑wijfijs.t.:⎩⎪⎨⎪⎧∑jfij≤−vi,i∈S∑jfji=vi,i∈T∑ifik−∑jfkj=0,k∈/S,T
式中:f(i,j) 表示路段 (i,j) 的流量,w(i,j) 表示路段 (i,j) 的单位流量运输成本,S 表示源点(供应点),T 表示汇点(需求点)。
对于上式描述的线性规划问题,可以用 PuLP工具包求解(参见《Python小白的数学建模课-03.线性规划》),编程步骤如下:
(1) 导入 PuLP库函数;
(2) 定义一个线性规划问题;
(3)定义决策变量,决策变量的上下限:
f11:F1-W1 流量, 0<=f11<=600
f12:F1-W2 流量, 0<=f12<=600
f21:F2-W1 流量, 0<=f21<=400
f22:F2-W2 流量, 0<=f22<=400
w11:W1-R1 流量, 0<=w11<=200
w12:W1-R2 流量, 0<=w12<=150
w13:W1-R3 流量, 0<=w13<=350
w14:W1-R4 流量, 0<=w14<=300
w21:W2-R1 流量, 0<=w21<=200
w22:W2-R2 流量, 0<=w22<=150
w23:W2-R3 流量, 0<=w23<=350
w24:W2-R4 流量, 0<=w24<=300
(4)定义目标函数:
min cost = 2*f11 + 3*f12 + 3*f21 + f22 + 2*w11 + 6*w12
+ 3*w13 + 6*w14 + 4*w21 + 4*w22 + 6*w23 + 5*w24
(5)定义约束条件:
f11 + f12 <= 600
f21 + f22 <= 400
f11 + f21 = w11 + w12 + w13 + w14
f12 + f22 = w21 + w22 + w23 + w24
w11 + w21 = 200
w12 + w22 = 150
w13 + w23 = 350
w14 + w24 = 300
注意,f11+f12<=600, f21+f22<=400,意味着允许存在产销不平衡的情况。
(6)求解规划问题,输出优化结果
本文给出了基于 NetworkX 工具包和基于 PuLP 工具包的两种方法的程序,以便小白理解图论中的最小费用流问题与线性规划问题的联系。
4.3 Python 例程
# mathmodel21_v1.py
# Demo20 of mathematical modeling algorithm
# Demo of network flow problem optimization with NetworkX
# Copyright 2021 YouCans, XUPT
# Crated:2021-07-18
import numpy as np
import matplotlib.pyplot as plt # 导入 Matplotlib 工具包
import networkx as nx # 导入 NetworkX 工具包
import pulp # 导入 pulp库
# # 4. 多源多汇最小费用流问题 (Capacity network with multi source and multi sink)
# # 4.1 费用网络
# 创建有向图
G1 = nx.DiGraph() # 创建一个有向图 DiGraph
G1.add_edges_from([('F1','W1',{'capacity': 9e5, 'weight': 2}), # F1~F2:工厂
('F1','W2',{'capacity': 9e5, 'weight': 3}), # W1~W2:仓库
('F2','W1',{'capacity': 9e5, 'weight': 3}),
('F2','W2',{'capacity': 9e5, 'weight': 1}),
('W1','R1',{'capacity': 9e5, 'weight': 2}), # R1~R4:零售店
('W1','R2',{'capacity': 9e5, 'weight': 6}),
('W1','R3',{'capacity': 9e5, 'weight': 3}),
('W1','R4',{'capacity': 9e5, 'weight': 6}),
('W2','R1',{'capacity': 9e5, 'weight': 4}),
('W2','R2',{'capacity': 9e5, 'weight': 4}),
('W2','R3',{'capacity': 9e5, 'weight': 6}),
('W2','R4',{'capacity': 9e5, 'weight': 5})]) # 添加边的属性 'capacity', 'weight'
G1.add_node("F1", demand=-600) # nx.min_cost_flow() 的设置要求
G1.add_node("F2", demand=-400) # 设置源点的流量,负值表示净流出
G1.add_node("R1", demand=200) # 设置汇点的流量,正值表示净流入
G1.add_node("R2", demand=150)
G1.add_node("R3", demand=350)
G1.add_node("R4", demand=300)
pos={'F1':(2,6.5),'F2':(2,3.5),'W1':(5,6.5),'W2':(5,3.5),'R1':(9,8),'R2':(9,6),'R3':(9,4),'R4':(9,2)} # 指定顶点绘图位置
# # 4.2 用 NetworkX 求最小费用流
# 求最小费用流(demand=v)
minFlowCost = nx.min_cost_flow_cost(G1) # 求最小费用流的费用
minFlowDict = nx.min_cost_flow(G1) # 求最小费用流
# minFlowCost, minFlowDict = nx.network_simplex(G2) # 求最小费用流--与上行等效
# 整理边的标签,用于绘图显示
# 数据格式转换
edgeCapacity = nx.get_edge_attributes(G1, 'weight')
edgeLabel = {} # 边的标签
for i in edgeCapacity.keys(): # 整理边的标签,用于绘图显示
edgeLabel[i] = f'w={edgeCapacity[i]:}' # 边的容量
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))
print("1. NetworkX 网络与图(最小费用流优化结果):") # NetworkX 工具包
print("最小费用:{}".format(minFlowCost)) # 输出最小费用的值
print("最小费用流的路径及流量: ", minFlowDict) # 输出最小费用流的途径和各路径上的流量
print("最小费用流的路径:", edgeLists) # 输出最小费用流的途径
# 绘制有向网络图
fig, ax = plt.subplots(figsize=(8,6))
ax.text(1.2,6.4,"600",color='navy')
ax.text(1.2,3.4,"400"以上是关于Python小白的数学建模课-20.网络流优化案例的主要内容,如果未能解决你的问题,请参考以下文章