python 线性规划全局最优解_Python Pulp库求解线性规划问题(五) 多阶段多产品供应链全局优化...

问题描述

在上一节中我们求解了简单物流配送模型,但是在实际的物流过程中,通常物流配送是一个多阶段的过程:工厂先将产品发到各地的配送中心,然后由配送中心再将产品配送到顾客手中。但是也存在顾客需求量较大时,由工厂直发到顾客处的情况。对于多阶段的情况,我们需要进一步扩充我们的模型。多阶段物流配送问题的描述如下图:多阶段多产品物流网络示意

从工厂到顾客处有不同的路径:到有些顾客处有直通的路线,但是要求对方总需求量达到一定值才可以直发;

还可以通过配送中心对顾客进行配送,这种方式对起发值没有限制,但是配送中心的容量有限,另外经过配送中心还会产生额外的装卸费用;

此外,每条路径的配送费用和最大运力都有一定约束限制。

工厂往往能生产的也不止一类产品:每个工厂有原先就能生产的几种产品,另外还可以通过开模的方式,以付出开模费用为代价增加能生产产品的种类。但是不管生产多少类产品,工厂的总产能是固定的。

需要在这样的情况下,求出使全局费用最低(包括开模费用、配送中心装卸费用、物流费用)的生产布局和物流配送方案。

数学模型

决策变量

为了表达该物流网络的数学模型,定义如下参数:

: 工厂数量

: 配送中心数量

: 顾客数量

: 商品数量

: 工厂

的最大产能;

: 0-1变量,未新开模时,工厂是否能够生产商品,可以生产为1,否则为0;

: 商品

的开模成本;

: 商品

的单位重量;

: 配送中心

的最大容量;

: 顾客

对商品

的需求量;

: 路径

上单位重量商品的配送成本;

: 路径

的最大流量限制;

: 路径

的起送量限制;

: 经过配送中心

额外产生的单位重量装卸成本,

​: 一个与路径配送量有数量级差距的大数;

决策变量可以设定为:

​: 路径​

上商品​

的配送件数。

目标函数

此问题的目标函数是全局总成本,包括开模费用,配送中心装卸费用,物流费用三部分。可以表达为下式:

约束条件

工厂产能约束

满足顾客需求

路径流量约束 -- 每条路径上的配送量必须大于起送量并且小于最大运力

配送中心容量约束 -- 配送中心通过的流量小于容量限制

编程求解

在编程求解过程中,由于物流费用中逻辑条件的实施需要增加额外变量,为了降低求解时间,实际上我省去了用ceil向上取整将是否生产某产品转化为0-1变量的过程,这样虽然目标函数值与上面给出的目标函数有所出入,但是求解出的变量结果是相同的。

#---- 导入依赖库

from pulp import *

import pandas as pd

import networkx as nx

import os

import time

import matplotlib.pyplot as plt

from math import ceil

#---- 数据导入

fpath = os.path.join("data", "生产布局数据.xlsx")

factories = pd.read_excel(fpath, sheet_name="工厂")

distributors = pd.read_excel(fpath, sheet_name="配送中心")

commodities = pd.read_excel(fpath, sheet_name="商品")

customers = pd.read_excel(fpath, sheet_name="顾客")

routes = pd.read_excel(fpath, sheet_name="路线")

#---- 网络建模

# 建立节点

# 创建有向图

G = nx.DiGraph()

# 添加节点

for i, row in factories.iterrows():

G.add_node(row.工厂, node_type='工厂',

supply=row.最大产量_件,

canProduce1=row.当前能否生产产品1,

canProduce2=row.当前能否生产产品2,

canProduce3=row.当前能否生产产品3,)

for i, row in distributors.iterrows():

G.add_node(row.配送中心, node_type='配送中心',

loadingCost=row.装卸费用_元每吨,

maxCapa=row.最大容量_吨)

for i, row in customers.iterrows():

G.add_node(row.顾客, demand1=row.商品1需求量_件,

demand2=row.商品2需求量_件,

demand3=row.商品3需求量_件, node_type='顾客')

# 添加边

for i, row in routes.iterrows():

G.add_edge(row.起点, row.终点, cost=row.运输单价_元每吨, capacity=row.最大运力_吨,

directRoute=row.工厂直发路线, minAmount=row.起发值_吨)

#---- 网络可视化

pos = nx.drawing.layout.shell_layout(G)

color = []

for node in G.nodes(data=True):

if node[1]['node_type'] == "工厂":

color.append("red")

elif node[1]['node_type'] == "配送中心":

color.append("yellow")

else:

color.append("blue")

nx.draw(G, pos, node_color=color)

plt.show()

#---- 问题建模

# 建立模型

model = LpProblem("全局优化问题", LpMinimize)

# 建立决策变量 -- 路径ijk上商品n的配送量

origins = routes.起点.values

destinations = routes.终点.values

commodity_cat = range(1, 4)

shipping_amount = LpVariable.dicts(

"配送量", (origins, destinations, commodity_cat), lowBound=0)

produce_cat = LpVariable.dicts(

"是否生产", (factories.工厂, commodity_cat), cat=LpBinary) # 额外变量, 表示是否在工厂i为商品n开模

# 目标函数 -- 总费用最小

cost = [] # 总费用

M = 100000 # 大数

for(origin, destination, data) in G.edges(data=True):

for comm in commodity_cat:

# 运输费用

cost.append(data['cost']*shipping_amount[origin][destination][comm])

if origin in distributors.配送中心.values:

# 装卸费用

cost.append(G.nodes(data=True)[

origin]["loadingCost"]*shipping_amount[origin][destination][comm])

# 模具费用

for name, data in G.nodes(data=True):

if name in factories.工厂.values:

cost.append((1-data["canProduce1"]) *

lpSum([shipping_amount[name][destination][1] for _, destination in G.out_edges(name)]))

cost.append((1-data["canProduce2"]) *

lpSum([shipping_amount[name][destination][2] for _, destination in G.out_edges(name)]))

cost.append((1-data["canProduce3"]) *

lpSum([shipping_amount[name][destination][3] for _, destination in G.out_edges(name)]))

# 累加总费用

model += lpSum(cost)

#---- 约束条件

# 工厂产能约束

for name, data in G.nodes(data=True):

if data["node_type"] == "工厂":

model += lpSum([shipping_amount[name][destination][comm]

for _, destination in G.out_edges(name) for comm in commodity_cat]) <= data['supply']

# 满足顾客需求

for name, data in G.nodes(data=True):

if data["node_type"] == "顾客":

model += lpSum([shipping_amount[origin][name][1]

for origin, _ in G.in_edges(name)]) == data['demand1']

model += lpSum([shipping_amount[origin][name][2]

for origin, _ in G.in_edges(name)]) == data['demand2']

model += lpSum([shipping_amount[origin][name][3]

for origin, _ in G.in_edges(name)]) == data['demand3']

# 路径流量约束 -- 每条路径上的配送量应该大于起送量,并且小于最大运力

for (origin, destination, data) in G.edges(data=True):

model += lpSum(shipping_amount[origin][destination][comm]

for comm in commodity_cat) <= data['capacity']

model += lpSum(shipping_amount[origin][destination][comm]

for comm in commodity_cat) >= data['minAmount']

# 配送中心的输出等于输入, 同时满足配送中心最大容量要求

for name, data in G.nodes(data=True):

if data["node_type"] == "配送中心":

model += lpSum([shipping_amount[name][destination][comm] for _, destination in G.out_edges(name) for comm in commodity_cat]

) == lpSum([shipping_amount[origin][name][comm] for origin, _ in G.in_edges(name) for comm in commodity_cat])

model += lpSum([shipping_amount[origin][name][comm] for origin,

_ in G.in_edges(name) for comm in commodity_cat]) <= data['maxCapa']

#---- 求解

start_time = time.clock()

model.solve()

print("求解状态: ", LpStatus[model.status])

print("求解用时: ", time.clock()-start_time, "s")

#---- 输出求解结果

rsl = []

for origin, destination in G.edges():

val1 = value(shipping_amount[origin][destination][1])

val2 = value(shipping_amount[origin][destination][2])

val3 = value(shipping_amount[origin][destination][3])

rsl.append({"起点": origin, "终点": destination,

"商品1运输量": val1, "商品2运输量": val2, "商品3运输量": val3})

rsl_df = pd.DataFrame(rsl)

print(rsl_df)

求解结果

求解状态: Optimal

求解用时: 0.056308000000000025 s

过程中画出的有向图如下,其中红色代表工厂,黄色代表配送中心,蓝色代表顾客:networkx建立的物流网络有向图

最终求解得到的每条路上的流量分配如下:

而是否开模的结果如下:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值