系统建模 - 使用gurobi求解钢管下料问题

在这里插入图片描述

题面

某钢管零售商从钢管厂进货,将钢管按照顾客的要求切割后售出,从钢管厂进货时得到的原料钢管都是19m。

  1. 现有一客户需要50根4m、20根6m和15根8m的钢管应如何下料最省(总余料最少或使用的钢管最少)?
  2. 零售商如果采用的不同切割模式太多,将会导致生产过程复杂化,从而增加生产和管理成本,所以该零售商规定采用的不同切割模式不能超过3种。此外,如果客户还需要10根5m的钢管,又该如何下料?请分别针对上述两种情景需求建立数学规划模型,利用Gurobi求解。

Task 1

问题分析

由于原料钢管的长度为19m,客户将其切割成长度为4m、6m和8m的钢管。故一根钢管有有限个切割组合,如下表所示

num. of 4mnum. of 6mnum. of 8m余料 / 根 · m
组合14003
组合23101
组合32013
组合41111
组合51203
组合60301

一共有6中组合。因而只需要设置6个决策变量(Integer型),并以总余料最小为问题的目标即可。

变量约定

设采取的不同的组合数分别为
x i ,   i = 1 , 2 , … , 6 x_i,\ i=1,2,\ldots,6 xi, i=1,2,,6
相应的余料为
c i ,   i = 1 , 2 , … , 6 c_i,\ i=1,2,\ldots,6 ci, i=1,2,,6
总余量为
z z z

约束条件

4 x 1 + 3 x 2 + 2 x 3 + x 4 + x 5 = 50 4x_1+3x_2+2x_3+x_4+x_5=50 4x1+3x2+2x3+x4+x5=50

x 2 + x 4 + 2 x 5 + 3 x 6 = 20 x_2+x_4+{2x}_5+{3x}_6=20 x2+x4+2x5+3x6=20

x 3 + x 4 = 15 x_3+x_4=15 x3+x4=15

目标函数

min ⁡ z = ∑ i = 1 6 c i x i \min z= \sum_{i=1}^{6} c_i x_i minz=i=16cixi

编程求解

import gurobipy as gp
from gurobipy import GRB

model = gp.Model("steel_cutting")

x = model.addVars(6, vtype=GRB.INTEGER, name="x")
c = [3, 1, 3, 1, 3, 1]  # 每种组合对应的余料

model.addConstr(4*x[0] + 3*x[1] + 2*x[2] + x[3] + x[4] == 50, "")
model.addConstr(x[1] + x[3] + 2*x[4] + 3*x[5] == 20, "")
model.addConstr(x[2] + x[3] == 15, "")

obj = sum(c[i] * x[i] for i in range(6))
model.setObjective(obj, GRB.MINIMIZE)

model.optimize()

if model.status == GRB.OPTIMAL:
    print("最优解为:")
    for i in range(6):
        print(f"x[{i}] = {x[i].x}")
    print(f"总余料最小为: {model.objVal}")
else:
    print("未找到最优解")

结果为

在这里插入图片描述

即10根采用组合2(3根4m和1根6m的钢管),5根采用组合3(2根4m和1根8m的钢管),10根采用组合4(1根4m、1根6m和1根8m的钢管)。得到的最小总余料为35.0m。

Task2

问题分析

如上文所述,我们提出的解决方案一共有6中切割方式。要想限制切割方式,只需要针对每一个切割方式 x 引入新的0-1变量,并确保这16个0-1的和不超过3。

在问题2中,客户还需要10根5m的钢管,故而原有的切割模式需要进行改变。最终得到下表.

切割方式num. of 4mnum. of 5mnum. of 6mnum. of 8m余料
100023
200301
301110
401202
502011
602103
710111
810203
911012
1013000
1120013
1221100
1322001
1430101
1531002
1640003

*注:推算所有切割方式的代码见附录。

变量约定

设采取的不同的组合数分别为
x i ,   i = 1 , 2 , … , 16 x_i,\ i=1,2,\ldots,16 xi, i=1,2,,16
相应的余料为
c i ,   i = 1 , 2 , … , 16 c_i,\ i=1,2,\ldots,16 ci, i=1,2,,16
另设启用标志(0-1变量)
k i ,   i = 1 , 2 , … , 16 k_i,\ i=1,2,\ldots,16 ki, i=1,2,,16
总余量为
w w w

约束条件

(展开)
k 7 x 7 + k 8 x 8 + k 9 x 9 + k 10 x 10 + 2 k 11 x 11 + 2 k 12 x 12 + 2 k 13 x 13 + 3 k 14 x 14 + 3 k 15 x 15 + 4 k 16 x 16 = 50 k_{7}x_7+k_{8}x_8+k_{9}x_9+ k_{10}x_{10} +2k_{11}x_{11}+2k_{12}x_{12}+2k_{13}x_{13}+3k_{14}x_{14}+3k_{15}x_{15} +4k_{16}x_{16}=50 k7x7+k8x8+k9x9+k10x10+2k11x11+2k12x12+2k13x13+3k14x14+3k15x15+4k16x16=50

k 3 x 3 + k 4 x 4 + 2 k 5 x 5 + 2 k 6 x 6 + k 9 x 9 + 3 k 10 x 10 + k 12 x 12 + 2 k 13 x 13 + k 15 x 15 = 10 k_{3}x_3+k_{4}x_4+2k_{5}x_5+2k_{6}x_6+k_{9}x_9+3k_{10}x_{10}+k_{12}x_{12}+2k_{13}x_{13}+k_{15}x_{15}=10 k3x3+k4x4+2k5x5+2k6x6+k9x9+3k10x10+k12x12+2k13x13+k15x15=10

3 k 2 x 2 + k 3 x 3 + 2 k 4 x 4 + k 6 x 6 + k 7 x 7 + 2 k 8 x 8 + k 12 x 12 + k 14 x 14 = 20 3k_{2}x_2+k_{3}x_3+2k_{4}x_4+k_{6}x_6+k_{7}x_7+2k_{8}x_8+k_{12}x_{12}+k_{14}x_{14}=20 3k2x2+k3x3+2k4x4+k6x6+k7x7+2k8x8+k12x12+k14x14=20

2 k 1 x 1 + k 3 x 3 + k 5 x 5 + k 7 x 7 + k 9 x 9 + k 11 x 11 = 15 2k_{1}x_1+k_{3}x_3+k_{5}x_5+k_{7}x_7+k_{9}x_9+k_{11}x_{11}=15 2k1x1+k3x3+k5x5+k7x7+k9x9+k11x11=15

max ⁡ ∑ i = 1 16 k i = 3   \max \sum_{i=1}^{16}{k_i=3}\ maxi=116ki=3 

目标函数

min ⁡ w = ∑ i = 1 16 c i x i \min w= \sum_{i=1}^{16} c_i x_i minw=i=116cixi

编程求解

from gurobipy import *
m = Model("steel_cutting")

c = [3, 1, 0, 2, 1, 3, 1, 3, 2, 0, 3, 0, 1, 1, 2, 3]
x = m.addVars(16, vtype=GRB.INTEGER, name="x")
k = m.addVars(16, vtype=GRB.BINARY, name="k")

m.addConstr(quicksum(k[i] * x[i] for i in range(16)) == 50)
m.addConstr(k[2] * x[2] + k[3] * x[3] + 2 * k[4] * x[4] + 2 * k[5] * x[5] + k[8] * x[8] + 3 * k[9] * x[9] + k[11] * x[11] + 2 * k[12] * x[12] + k[14] * x[14] == 10)
m.addConstr(3 * k[1] * x[1] + k[2] * x[2] + 2 * k[3] * x[3] + k[5] * x[5] + k[6] * x[6] + k[7] * x[7] + 2 * k[8] * x[8] + k[12] * x[12] + k[13] * x[13] == 20)
m.addConstr(2 * k[0] * x[0] + k[2] * x[2] + k[4] * x[4] + k[6] * x[6] + k[8] * x[8] + k[10] * x[10] == 15)
m.addConstr(quicksum(k[i] for i in range(16)) <= 3)

m.setObjective(quicksum(c[i] * x[i] for i in range(16)), GRB.MINIMIZE)
m.optimize()

for i in range(16):
    print(f"x[{i}] = {x[i].x}, k[{i}] = {k[i].x}")
print(f"Objective Value: {m.objVal}")

求解结果为
在这里插入图片描述

结论

对于Task1.最优方案为10根采用组合2(3根4m和1根6m的钢管),5根采用组合3(2根4m和1根8m的钢管),10根采用组合4(1根4m、1根6m和1根8m的钢管)。得到的最小总余料为35.0m

对于Task2.最优方案为启用切割方案7、方案12和方案15,相应的切割参数如图所示。得到的最小总余料为110m。

附录

Task 2中生成最优切割方案的暴力解法

ans=[]
for i in range(5):
    for j in range(5):
        for h in range(5):
            for g in range(5):
                if 4*i+5*j+6*h+8*g<=19 and 19-4*i-5*j-6*h-8*g<4:
                    ans.append([i,j,h,g,19-4*i-5*j-6*h-8*g])
__=0
for _ in ans:
    print(__, *_)
    __+=1
最短路径问题是指从一个起点到一个终点,经过若干个中间节点,使得路径上的边权之和最小。本文将介绍如何使用YALMIP和gurobi求解最短路径问题。 首先,需要安装YALMIP和gurobi,并将它们与MATLAB集成。然后,定义问题的变量和约束条件。假设有n个节点和m条边,其中边e的起点为i(e),终点为j(e),权重为w(e)。定义变量x(e)表示边e是否在路径中出现,即x(e)=1表示边e在路径中,x(e)=0表示边e不在路径中。则问题可以表示为: minimize ∑e∈E w(e)·x(e) subject to x(e)∈{0,1} for all e∈E ∑e∈δ+(i) x(e) - ∑e∈δ-(i) x(e) = {1 if i=s, -1 if i=t, 0 otherwise} for all i∈V-{s,t} 其中,E表示边集,V表示节点集,s表示起点,t表示终点,δ+(i)表示以节点i为起点的边集,δ-(i)表示以节点i为终点的边集。 然后,需要使用YALMIP来定义问题,并使用gurobi求解。代码如下: ```matlab % 定义变量和参数 n = 5; % 节点数 m = 7; % 边数 s = 1; % 起点 t = 5; % 终点 i = [1 1 2 2 3 4 4]; % 边的起点 j = [2 3 3 4 5 5 3]; % 边的终点 w = [2 1 3 2 1 3 1]; % 边的权重 x = binvar(m,1); % 定义变量x(e) obj = w*x; % 定义目标函数 constr = []; for k = 1:n if k == s % 起点 constr = [constr, sum(x(find(i==k))) - sum(x(find(j==k))) == 1]; elseif k == t % 终点 constr = [constr, sum(x(find(i==k))) - sum(x(find(j==k))) == -1]; else % 中间节点 constr = [constr, sum(x(find(i==k))) - sum(x(find(j==k))) == 0]; end end % 定义问题 ops = sdpsettings('solver','gurobi'); optimize(constr,obj,ops); % 输出结果 value(obj) value(x) ``` 运行上述代码,可以得到最优解为4,路径为1->2->4->5。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值