前言:cplex的安装教程已经发布在 前面的内容中了,可自行领取
本系列以官方examples为主导,讲解官方examples所涉及的问题以及代码,旨在学习官方给出的例子,熟练使用cplex并且能够解决真实场景下的问题
第一部分,约束规划问题(cp)
四色问题
问题描述
- 为地图上的国家选择颜色,最多使用四种颜色(蓝色、白色、黄色、绿色),没有邻国是相同的颜色。以下例子中对欧洲的六个国家的地图进行着色:比利时、丹麦、法国、德国、卢森堡和荷兰(可打开欧洲地图查看关系),问题比较简单,不多叙述。
- 欧洲图片
代码
- 首先导包并且实例化对象
from docplex.cp.model import CpoModel
mdl = CpoModel()
- 给优化问题添加优化变量,
integer_val
用于给优化问题添加整数变量,三个参数分别为最小值/最大值以及名称,四种颜色用0,1,2,3代替
Belgium = mdl.integer_var(0, 3, 'Belgium')
Denmark = mdl.integer_var(0, 3, 'Denmark')
France = mdl.integer_var(0, 3, 'France')
Germany = mdl.integer_var(0, 3, 'Germany')
Luxembourg = mdl.integer_var(0, 3, 'Luxembourg')
Netherlands = mdl.integer_var(0, 3, 'Netherlands')
- 添加约束条件,此约束条件可以查看欧洲地图,相邻国家不可用相同颜色
mdl.add(Belgium != France)
mdl.add(Belgium != Germany)
mdl.add(Belgium != Netherlands)
mdl.add(Belgium != Luxembourg)
mdl.add(Denmark != Germany)
mdl.add(France != Germany)
mdl.add(France != Luxembourg)
mdl.add(Germany != Luxembourg)
mdl.add(Germany != Netherlands)
- 求解问题,注意此处在原始cplex_example中代码为
msol = mdl.solve(TimeLimit=10)
是不能运行的,要改到本地运行,求解器路径由execfile
接收,路径作为参考,根据个人安装目标更改
msol = mdl.solve(
TimeLimit=10,
agent='local',
execfile=r'C:\Program Files\IBM\ILOG\CPLEX_Enterprise_Server1210\CPLEX_Studio\cpoptimizer\bin\x64_win64\cpoptimizer.exe'
)
- 打印结果。msol如果求解失败会返回False,此处做个判断,并根据求解结果还原color,逻辑较为简单不多赘述
ALL_COUNTIES = (Belgium, Denmark, France, Germany, Luxembourg, Netherlands)
if msol:
print(msol.get_solve_status())
colors = ('Yellow', 'Red', 'Green', 'Blue')
for country in ALL_COUNTIES:
print(country.get_name() + ':', end=' ')
print(colors[msol[country]])
else:
print("No solution found")
资源调度问题
问题描述
- 一家公司有8家商店。
- 每个商店必须由一个仓库提供货物。
- 该公司在5个可能的地点(待选)建立供应仓库:波恩、波尔多、伦敦、巴黎和罗马。
- 不同地点建立仓库有不同的供给能力。在波尔多或罗马建造的仓库只能供应一家商店;在伦敦建一个仓库可以供应两家商店;在波恩建造的一个仓库可以供应三个仓库;在巴黎建一个仓库可以供应四家商店。
- 每个商店的供应成本各不相同,具体取决于由哪个仓库供应。例如,位于巴黎的一家商店如果由同样位于巴黎的仓库供应,供应成本会很低。如果由其他仓库供应,同一家商店的供应成本会高得多。
- 建造仓库的成本因仓库位置而异。
- 目标:找到最具成本效益的方案来解决这个问题,同时确保每个商店都由一个仓库提供。
代码与模型描述
- 导包
from docplex.cp.model import CpoModel
from collections import namedtuple
- 添加数据。数据描述: 5个城市可以用来建仓库,
WAREHOUSES
用来表示城市信息,三个属性分别为城市名/城市容量(建仓库的情况下能够供给的商店数量)以及如果在当前城市建仓库所消耗的代价;8个商店,数据形成一个2维矩阵,SUPPLY_COST
i j _{ij} ij表示第 i i i个商店由第 j j j个城市的仓库供货所消耗的代价
Warehouse = namedtuple(
'Wharehouse', ('city', 'capacity', 'cost')
)
WAREHOUSES = (Warehouse("Bonn", 3, 480),
Warehouse("Bordeaux", 1, 200),
Warehouse("London", 2, 320),
Warehouse("Paris", 4, 340),
Warehouse("Rome", 1, 300))
NB_WAREHOUSES = len(WAREHOUSES)
NB_STORES = 8
SUPPLY_COST = ((24, 74, 31, 51, 84),
(57, 54, 86, 61, 68),
(57, 67, 29, 91, 71),
(54, 54, 65, 82, 94),
(98, 81, 16, 61, 27),
(13, 92, 34, 94, 87),
(54, 72, 41, 12, 78),
(54, 64, 65, 89, 89))
- 实例化模型并添加变量。
integer_var_list
与integer_var
功能相同,只是前者能够同时创建多个变量,supplier
一共8个变量,表示由第几个城市的仓库供货,变量范围为[0, 5]表示5个城市的代号;open
变量表示要不要在当前城市建仓库,由0/1决定
mdl = CpoModel()
supplier = mdl.integer_var_list(NB_STORES, 0, NB_WAREHOUSES - 1, 'supplier')
open = mdl.integer_var_list(NB_WAREHOUSES, 0, 1, 'open')
- 约束条件。
element
为下标的功能,例如element(a[i], j)
就表示 a i j a_{ij} aij,element(a, i)
表示 a i a_i ai- 首先第一个
for
循环表示open
s _{s} s=1,对这个约束做个说明,由于 s s s是supplier中的一个变量,即由上述的定义可知 s s s的值域 ∈ [ 0 , 4 ] \in [0, 4] ∈[0,4], 假如 s = 0 s=0 s=0,说明 s s s对应的商店由第一个城市的仓库供给,这时第一个城市就需要建仓库,但是此时还没有求解,具体哪个城市建仓库还不知道,只是用这个约束定义该例的情况。这个理解有一些抽象,可以从结果论角度出发理解:如果 s s s求出是某个值,则对应的城市就必须建仓库,于是就形成了这样的一个等式约束。 - 第二个
for
循环比较好理解,表示一个仓库供应的商店个数不超过自身的容量的限制 - 最后的部分理解起来也比较简单:首先
scal_prod
其实类似于向量内积(cplex中的这些操作其实还未发生,在求解之前只是定义了此种行为),即 ∑ i = 0 4 o p e n i ∗ w i . c o s t \sum_{i=0}^{4}open_{i} * w_{i}.cost ∑i=04openi∗wi.cost,这一部分表示建仓库所花费的代价。for
循环是在计算由仓库供给给商店所花的代价。直接加上上面的2维矩阵对应数据即可
- 首先第一个
for s in supplier:
mdl.add(mdl.element(open, s) == 1)
for wx in range(NB_WAREHOUSES):
mdl.add(mdl.count(supplier, wx) <= WAREHOUSES[wx].capacity)
total_cost = mdl.scal_prod(open, [w.cost for w in WAREHOUSES])
for sx in range(NB_STORES):
total_cost = total_cost + mdl.element(supplier[sx], SUPPLY_COST[sx])
- 增加目标函数。本例是有目标函数的,即最小化代价
mdl.add(mdl.minimize(total_cost))
- 求解。同样照上例更改
mdl.solve
,求解完后变量都可以用mdl
进行调用,打印结果即可
msol = mdl.solve(
TimeLimit=10,
agent='local',
execfile=r'C:\Program Files\IBM\ILOG\CPLEX_Enterprise_Server1210\CPLEX_Studio\cpoptimizer\bin\x64_win64\cpoptimizer.exe'
)
if msol:
for wx in range(NB_WAREHOUSES):
if msol[open[wx]] == 1:
print("城市'{}'建立仓库并供应商店{}".format(
WAREHOUSES[wx].city, ','.join(str(sx) for sx in range(NB_STORES) if msol[supplier[sx]] == wx)
))
print(f'花费为: {msol.get_objective_values()[0]}')
else:
print("No solution")
- 结果
城市'Bonn'建立仓库并供应商店2,5,7
城市'Bordeaux'建立仓库并供应商店3
城市'Paris'建立仓库并供应商店0,1,4,6
花费为: 1383