[cplex教程]cp问题系列1


前言: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_listinteger_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=04openiwi.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
  • 53
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值