[cplex教程]cp问题系列9


前言:cplex的安装教程已经发布在 前面的内容中了,可自行领取

cplex系列1的链接为:https://blog.csdn.net/wlgtcl/article/details/137999127?spm=1001.2014.3001.5502
cplex系列2的链接为:https://blog.csdn.net/wlgtcl/article/details/138037571?spm=1001.2014.3001.5502
cplex系列3的链接为:https://blog.csdn.net/wlgtcl/article/details/138091170?spm=1001.2014.3001.5502
cplex系列4的链接为:https://blog.csdn.net/wlgtcl/article/details/138525911?spm=1001.2014.3001.5501
cplex系列5的链接为:https://blog.csdn.net/wlgtcl/article/details/138539016?spm=1001.2014.3001.5501
cplex系列6的链接为:https://blog.csdn.net/wlgtcl/article/details/138562705?spm=1001.2014.3001.5502
cplex系列7的链接为:https://blog.csdn.net/wlgtcl/article/details/138610919?spm=1001.2014.3001.5502
cplex系列8的链接为:https://blog.csdn.net/wlgtcl/article/details/138617200?spm=1001.2014.3001.5501

钢厂板坯设计问题

  • 作者对于下面概念的描述比较混乱,注意区分
    • 规格/重量/尺寸/size/weight是一个意思
    • 颜色/品类是一个意思

问题描述

  • 以题中样例做解释
    • 给定一组重量固定的半成品钢板,size(这里指的是重量,下面说的重量与尺寸是一个意思,表示一种规格)有以下选择:[11, 13, 16, 17, 19, 20, 23, 24, 25, 26, 27, 28, 29, 30, 33, 34, 40, 43, 45]
    • 再给定一组成品钢板的订单
      • (Order( 1, 22, 5),
        Order( 2, 9, 3),
        Order( 3, 9, 4),
        Order( 4, 8, 5),
        Order( 5, 8, 7),
        Order( 6, 6, 3),
        Order( 7, 5, 6),
        Order( 8, 3, 0),
        Order( 9, 3, 2),
        Order(10, 3, 3),
        Order(11, 2, 1),
        Order(12, 2, 5))
    • 表示编号/重量/color,color表示成品钢板的品类
    • 现在每种规格半成品钢板不限量,用于制造上述成品钢板的订单,要求:
      • 每个成品钢板的订单只能由一块半成品钢板来做
      • 多个成品订单可以由同一块半成品钢板来做
      • 一个半成品钢板只最多能用于制作两种颜色(下面颜色与品类表示一个意思)的成品钢板
      • 成品钢板的制作重量(尺寸)不能超过所用的单个半成品钢板的重量(假如选定了几个订单要用某个半成品钢板做,这几个订单的重量不过超过这个钢板的重量)
    • 要求:制作完所有的订单,在满足上面约束的基础上剩余的半成品钢板重量(剩余物料)最小

代码与解析

  • 导包
from docplex.cp.model import CpoModel
from collections import namedtuple
  • 数据定义
    • ORDERS表示订单
    • MAX_COLOR_PRE_SLAB表示每块半成品钢板最多用于多少种颜色的成品钢板制作
    • AVAILABLE_SLAB_WEIGHTS表示能用的半成品钢板的重量(规格)
    • loss解析:由题干可知,订单中要生产的成品钢板重量不可能超过最重的半成品,要不然问题无解,所以loss考虑了当生产的成品钢板重量小于等最大的半成品钢板重量max_时的所有损失情况,即从假设成品钢板重量为1开始遍历到假设成品钢板重量为max_时,用目前可选的半成品钢板AVAILABLE_SLAB_WEIGHTS制作成品钢板所产生的最小缺失。
Order = namedtuple("Order", ['id', 'weight', 'color'])
ORDERS = (
            Order( 1, 22, 5),
            Order( 2,  9, 3),
            Order( 3,  9, 4),
            Order( 4,  8, 5),
            Order( 5,  8, 7),
            Order( 6,  6, 3),
            Order( 7,  5, 6),
            Order( 8,  3, 0),
            Order( 9,  3, 2),
            Order(10,  3, 3),
            Order(11,  2, 1),
            Order(12,  2, 5)
         )
         
MAX_COLOR_PER_SLAB = 2

AVAILABLE_SLAB_WEIGHTS = [11, 13, 16, 17, 19, 20, 23, 24, 25,
                          26, 27, 28, 29, 30, 33, 34, 40, 43, 45]
MAX_SLABS = len(ORDERS)

allcolors = set(o.color for o in ORDERS)

max_slab_weight = max(AVAILABLE_SLAB_WEIGHTS)

loss = [0] + [min([sw - use for sw in AVAILABLE_SLAB_WEIGHTS if sw >= use]) for use in range(1, max_slab_weight + 1)]                      
  • 模型实例化与变量定义
    • production_slab表示每个成品钢板使用的半成品钢板的索引,这个索引是指组的索引,具体的说多个订单可以用一个半成品钢板,所以这几个订单的编号是一样的,它们形成一个组,本变量就是这个组的索引
    • slab_use表示每个成品钢板用的半成品钢板的量
mdl = CpoModel()

production_slab = mdl.integer_var_list(len(ORDERS), 0, MAX_SLABS - 1, "production_slab")

slab_use = mdl.integer_var_list(MAX_SLABS, 0, max_slab_weight, "slab_use")
  • mdl.pack实现一种特殊的约束—“打包”约束。组合优化问题中,打包约束经常用于描述如何将一系列物品(在这里是订单)分配到有限数量的容器(这里指半成品)中,同时满足每个容器的容量限制。
    • 分配订单到半成品钢板:它确保每个订单(在这里为重量)都被分配到一个半成品钢板上。分配是通过决策变量 production_slab 完成的,这个变量指定了每个订单应该使用哪个半成品上(即上面所说的组,千万不能理解成了半成品钢板的重量列表的编号)。
    • 满足容量限制:mdl.pack 还确保每个半成品上的订单总重量不超过半成品的容量。这是通过检查 slab_use 变量(它表示每个半成品的使用情况)来实现的。
    • 创建打包约束:mdl.pack 在模型中添加了一个或多个约束,这些约束确保上述两个条件都得到满足。这些约束是隐式添加的,不需要显式地写出每个半成品的容量约束。
mdl.add(mdl.pack(slab_use, production_slab, [o.weight for o in ORDERS]))
  • 保证一个组内所有的成品钢板颜色数量不超过MA_COLOR_PRE_SLAB
    • 对代码做个说明,先看第二层for,对于一种颜色c,遍历所有的订单,如果当前订单颜色为c,做lo |= (production_slab[i] == s),即如果production_slab[i] == s成立(此时当前订单被分到组s中了),su=True=1,,如果遍历完所有订单没有出现o.color == c(production_slab[i] == s)成立,则当前组内不存在与颜色c相关的钢板
    • 当遍历完所有的csu就保存了s这一个组中的所有颜色变量的数量,需要满足不能超过MAX_COLOR_PRE_SLAB = 2的约束
    • 因此当遍历完最外层的for,则对每个组都进行了这种限制
for s in range(MAX_SLABS):
   su = 0
   for c in allcolors:
       lo = False
       for i, o in enumerate(ORDERS):
           if o.color == c:
               lo |= (production_slab[i] == s)
       su += lo
   mdl.add(su <= MAX_COLOR_PER_SLAB)
  • 计算损失
    • loss变量已经提前算好了,表示成品钢板重量在最大非成品钢板范围内的各种情况(loss中有一些情况是不存在的),total_loss就是把这些存在情况的缺失加到一起,再进行最小化
    • 注意mdl.element的用法,参数顺序可以任意
      在这里插入图片描述
    • mdl.search_phase(production_slab) 创建了一个搜索阶段,指定了求解器在求解时应优先考虑 production_slab 决策变量列表。这意味着求解器将首先尝试确定每个订单应该使用哪个半成品钢板,然后再考虑其他决策变量和约束。
    • mdl.set_search_phases 方法用于设置模型的搜索阶段。搜索阶段定义了求解器在寻找最优解时应遵循的搜索策略,包括决策变量的搜索顺序、分支策略等。
total_loss = sum([mdl.element(slab_use[s], loss) for s in range(MAX_SLABS)])
mdl.add(mdl.minimize(total_loss))

mdl.set_search_phases([mdl.search_phase(production_slab)])
  • 求解与结果展示
print("Solving model....")
msol = mdl.solve(FailLimit=100000, TimeLimit=10, agent='local',
                 execfile=r'C:\Program Files\IBM\ILOG\CPLEX_Enterprise_Server1210\CPLEX_Studio\cpoptimizer\bin\x64_win64\cpoptimizer.exe')

if msol:
    print("Solution: ")
    for s in set(msol[ps] for ps in production_slab):
        lordrs = [o for i, o in enumerate(ORDERS) if msol[production_slab[i]] == s]
        used_weight = msol[slab_use[s]]          
        loss_weight = loss[used_weight]          
        colors = set(o.color for o in lordrs)    
        loids = [o.id for o in lordrs]           
        print("Slab weight={}, used={}, loss={}, colors={}, orders={}"
              .format(used_weight + loss_weight, used_weight, loss_weight, colors, loids))
else:
    print("No solution found")
  • 结果
    在这里插入图片描述
  • 这个问题代码没多少,但比之前的一些问题挺不好理解的。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值