前言: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
相关的钢板 - 当遍历完所有的
c
,su
就保存了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")
- 结果
- 这个问题代码没多少,但比之前的一些问题挺不好理解的。