【运筹优化】Python 调用 Gurobi 实现列生成算法求解一维下料问题


一、一维下料问题

所谓一维下料问题是指:单一规格或者多种规格的条 形原材料数量若干,需要切割成多种规格的零件,每种零件数量若干,使得原材料的利用率高,废料少。


二、列生成算法

可以参考:列生成算法原理(单纯形基础)


三、Python 调用 Gurobi 实现列生成求解一维下料问题

3.1 完整代码

# -*- coding: utf-8 -*-#
# Author: WSKH
# Blog: wskh0929.blog.csdn.net
# Time: 2023/2/10 10:24
# Description:
import time

from gurobipy import *


def cg_solve(master_batch_length, sub_material_lengths, demands):
    try:
        sub_material_count = len(sub_material_lengths)
        plan_list = []
        # 声明限制主问题模型和定价子问题模型
        rmp = Model("rmp")
        sub = Model("sub")
        # 取消模型打印信息
        rmp.setParam("OutputFlag", 0)
        sub.setParam("OutputFlag", 0)
        # 初始化方案
        for i in range(sub_material_count):
            plan_list.append(
                [master_batch_length // sub_material_lengths[i] if i == j else 0 for j in range(sub_material_count)])
        # 初始化RMP
        rmp_var = []
        for i in range(len(plan_list)):
            # 1 是目标函数上的系数
            rmp_var.append(rmp.addVar(0, GRB.INFINITY, 1, GRB.CONTINUOUS, f"rmp_{i}"))
        rmp_con = []
        for i in range(sub_material_count):
            rmp_con.append(
                rmp.addConstr(quicksum(rmp_var[j] * plan_list[j][i] for j in range(len(plan_list))) >= demands[i],
                              f"rmp_con_{i}"))
        rmp.setAttr("ModelSense", GRB.MINIMIZE)
        # 初始化sub
        sub_var = []
        for i in range(sub_material_count):
            sub_var.append(sub.addVar(0, GRB.INFINITY, 0, GRB.INTEGER, f"sub_{i}"))
        sub.addConstr(
            quicksum(sub_var[i] * sub_material_lengths[i] for i in range(sub_material_count)) <= master_batch_length,
            "sub_con")
        # cg loop
        epoch = 1
        while True:
            print("-" * 20, f"Iteration {epoch}", "-" * 20)
            # 求解受限主问题
            rmp.optimize()
            print(f"RMP Obj: {rmp.ObjVal}")
            # 获取对偶值
            dual_values = rmp.getAttr("Pi", rmp.getConstrs())
            print(f"Dual Values: {dual_values}")
            # 根据对偶值给子问题定价
            sub.setObjective(1 - quicksum(sub_var[i] * dual_values[i] for i in range(sub_material_count)), GRB.MINIMIZE)
            # 求解子问题
            sub.optimize()
            print(f"Sub Obj: {sub.ObjVal}")
            # cg 结束判断
            if sub.ObjVal > -1e-06:
                print("CG Over!")
                break
            # 加入新列
            new_plan = sub.getAttr("X", sub.getVars())
            plan_list.append([int(round(v)) for v in new_plan])
            print(f"New Col: {plan_list[-1]}")
            rmp_col = Column(new_plan, rmp_con)
            rmp.addVar(0, GRB.INFINITY, 1, GRB.CONTINUOUS, f"cg_{epoch}", rmp_col)
            # 迭代次数自增
            epoch += 1
        # 将RMP转化为MIP
        print("-" * 20, f"Solve MIP", "-" * 20)
        mip_var = rmp.getVars()
        for i in range(len(mip_var)):
            mip_var[i].setAttr("VType", GRB.INTEGER)
        rmp.optimize()
        print(f"MIP Obj: {rmp.ObjVal}")
        c = 1
        for i in range(len(mip_var)):
            if mip_var[i].x > 0.5:
                print(
                    f"Plan-{c}: {plan_list[i]} , len: {quicksum([sub_material_lengths[j] * plan_list[i][j] for j in range(sub_material_count)])} , cnt: {int(round(mip_var[i].x))}")
                c += 1
    except GurobiError as e:
        print(f"Error code {e.errno} : {e}")
    except AttributeError:
        print('Encountered an attribute error')


if __name__ == '__main__':
    # 母料长度
    master_batch_length = 115
    # 子料长度
    sub_material_lengths = [25, 40, 50, 55, 70]
    # 子料需求
    demands = [50, 36, 24, 8, 30]
    # 求解
    start_time = time.time()
    cg_solve(master_batch_length, sub_material_lengths, demands)
    print(f"Total Solve Time: {time.time() - start_time} s")

3.2 运行结果

-------------------- Iteration 1 --------------------
RMP Obj: 76.5
Dual Values: [0.25, 0.5, 0.5, 0.5, 1.0]
Sub Obj: -0.5
New Col: [0, 1, 0, 0, 1]
-------------------- Iteration 2 --------------------
RMP Obj: 61.5
Dual Values: [0.25, 0.5, 0.5, 0.5, 0.5]
Sub Obj: -0.25
New Col: [1, 2, 0, 0, 0]
-------------------- Iteration 3 --------------------
RMP Obj: 60.75
Dual Values: [0.25, 0.375, 0.5, 0.5, 0.625]
Sub Obj: -0.125
New Col: [3, 1, 0, 0, 0]
-------------------- Iteration 4 --------------------
RMP Obj: 60.0
Dual Values: [0.25, 0.25, 0.5, 0.5, 0.75]
Sub Obj: 0.0
CG Over!
-------------------- Solve MIP --------------------
MIP Obj: 60.0
Plan-1: [4, 0, 0, 0, 0] , len: 100.0 , cnt: 8
Plan-2: [0, 0, 2, 0, 0] , len: 100.0 , cnt: 12
Plan-3: [0, 0, 0, 2, 0] , len: 110.0 , cnt: 4
Plan-4: [0, 1, 0, 0, 1] , len: 110.0 , cnt: 30
Plan-5: [3, 1, 0, 0, 0] , len: 115.0 , cnt: 6
Total Solve Time: 0.0059947967529296875 s
  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WSKH0929

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值