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 fmaxv

(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,jEwijfijs.t.:jfijvi,iSjfji=vi,iTifikjfkj=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.网络流优化案例的主要内容,如果未能解决你的问题,请参考以下文章

Python小白的数学建模课-A1.2021年数维杯C题(运动会优化比赛模式探索)探讨

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

Python小白的数学建模课-06 固定费用问题

Python小白的数学建模课-A1.国赛赛题类型分析

Python小白的数学建模课-A1.国赛赛题类型分析

Python小白的数学建模课-09 微分方程模型