导读:本篇是【CDN布局规划】系列的合集。在该系列中,我们用3篇文章,介绍了在当前短视频和自媒体平台的急速发展的过程中,网络体验越发丝滑的背后的CDN布局规划技术。介绍了如何对一个包含“选址”+“带宽分配”的CDN布局规划问题进行建模求解,并将设计开发一个一键运行,完成多案例、多求解器(Gurobi/CPLEX/COPT/MindOpt)的自动化求解脚本工具。
作者 1:向杜兵,算法专家,某制造业龙头
作者 2:张哲铭,算法专家,某互联网大厂
- 明人不说暗话,【运筹匠心】想要大家的赞!赞!赞!。先赞后看,多多益善(* ̄︶ ̄)~~~
- 本期推文代码获取方式,将推文转发朋友圈获20赞,添加管理员微信,管理员会定期回复数据代码链接哈~~~
- 各个系列的开源资料会定期整理发放,大家可以加粉丝群领取哦~~~
请加管理员微信:IndustryOR

大家好!我们是IndustryOR 团队,致力于分享业界落地的算法技术。欢迎关注微信公众号/知乎【运筹匠心】 。本期我们来谈一谈《CDN布局规划的算法技术》。本期案例共分 3 篇文章依次讲解:(1)业务问题拆解;(2)模型算法实现;(3)多求解器批量求解对比。
首篇文章讲解(1)业务问题拆解。
共分为 3 个部分,依次为:
1 业务背景
2 关键挑战
3 问题拆解
1.业务背景
随着各类短视频和自媒体平台的急速发展,移动互联网中无时无刻不产生着巨量的信息,但每个人刷视频、看直播却越来越丝滑,这其中,除了无线网络技术的快速发展,让我们从2G到如今的5G时代外,还有很多其他技术的加持。CDN(Content Delivery Network)就是其中之一。
CDN英文全称Content Delivery Network,中文翻译即为内容分发网络。它是建立并覆盖在承载网之上,由分布在不同区域的边缘节点服务器群组成的分布式网络1。
简单来说,CDN会将源站点的内容缓存,使用户可以就近、快速地获取到所需内容。
无论对用户还是内容提供者来说,都希望对内容的访问速度越快越好,因为这极大程度地影响用户体验和网站/平台的用户留存率。那么,作为CDN服务商,必须要尽量保证自己的服务质量,同时价格也要足够低,这就需要考虑:如何在提高用户网络体验和降低自身成本之间寻找到一个最佳的平衡?
在技术层面来说,解决这一难题的关键点之一在于对CDN布局进行合理的规划。一个科学合理的CDN布局方案对服务商商和消费者均至关重要:对于服务商而言,可以帮助其快速响应市场需求,同时降低成本。对于消费者而言,这意味着更稳定、更高速、更优质的上网体验。
那么,在规划的过程中,又面临哪些挑战呢?
2.关键挑战
CDN布局规划面临着众多挑战,其中有两个关键挑战:
(1)数据规模的爆炸性增长
自媒体时代,网络数据量呈现爆炸性增长态势。大量的文本、图片、音频和视频等数据形式不断涌入网络,对网络的容量和带宽提出了更高的要求。更大的数据量意味着需要投入更多的服务器、存储等硬件设备,同时还需要投入大量的人力和时间成本。
(2)移动数据需求的时空不均匀分布
不同地区、不同时段的用户对数据的需求可能存在显著差异,这可能导致源站负载不均衡的问题。合理的CDN服务器布局一方面可以更好地满足不同区域的数据需求,另一方面可以保持用户的数据流量在不同节点之间的均衡,避免某些服务器过载而导致服务质量下降,造成卡顿/延时、图像丢失、访问不稳定等诸多问题,极大降低用户体验,导致用户流失。
综上,本期将介绍CDN服务器布局规划问题。该问题通常需要确定CDN的数目和位置选择,以及数据如何“流向”不同用户。
3.问题拆解
CDN服务器布局规划要解决的问题是:在某个通信网络中,为了能将内容快速、低成本地传送到每个用户,需要在这个网络中选择一些网络节点放置内容存储服务器(以下简称服务器),并决定每个服务器分别向哪些用户分配多大的带宽。
每台服务器就像一个大仓库,存放着各类数据,可以发送给不同的用户,而用户的数据也可能来源于多个不同的服务器。不同的数据流从各个服务器出发,“行驶”在网络链路中,途中可能在多个网络节点进行中转,最终到达不同用户的终端。而网络链路就像一条马路,存在最大的“道路宽度”(总带宽),占用带宽将产生相应的租用费用。显然,每条链路上占用的带宽总和不得超过该链路的总带宽。
下面让我们从最简单的场景开始:当整个网络中只有一个用户,且一台普通档次的服务器就能满足其带宽需求。那么显然,这台服务器最佳的部署位置就是该用户所在的位置。
复杂一些,当网络中存在多个用户,但是依靠一台服务器就能满足所有用户的带宽需求时,由于该服务器总带宽有限,那么需要考虑该服务器部署在哪个位置,且如何分配该服务器的总带宽,才能更好地满足所有用户的需求,不至于导致某些用户带宽不足,体验感降低。
再复杂一些,当网络中存在多个用户,需要多台服务器才能满足所有用户的需求,且中间可能经过多个网络节点进行中转时,那么此时要综合考虑每台服务器部署的位置,以及每台服务器和每个用户之间的流量分配,且流量分配可能需要进行多次中转,最终满足所有用户的需求,并让这个布局规划的成本最小。
下图是一个小规模的网络结构示意图:红色点代表与用户直接相连的节点,绿色点代表网络节点,边的宽度代表链路的带宽,颜色表示单位租用费用。

业务目标
需要部署的服务器数量越多,花费的购买成本越多;带宽占用得越多,带宽的租用成本也越多。因此,对于服务商来说,期望在满足用户需求的基础上,尽可能地减少总的成本。
总成本 = 部署总成本 + 带宽的总租用成本
单个服务器的部署成本包括:
- 购买一台服务器需要的硬件成本
- 在网络节点进行部署的成本
部署要求
- 每个消费节点的带宽消耗需求必须被满足
- 每个网络节点上最多仅可部署一台服务器
- 服务器输出能力有限,其输出总流量不可超出最大输出流量
- 每条网络链路上的总流量不能超出该链路的总带宽
决策
在此过程中需要做出以下两个决策,以满足所有的用户需求,并且使总成本最小。
(1)如何选择服务器放置的位置
(2)每个用户与所有服务器之间的网络路径以及路径上占用的带宽
第二篇文章讲解(2)模型算法实现。
共分为 3 个部分,依次为:
1.基础网络构建
2.数学模型刻画
3.模型代码实现
上篇文章业务问题拆解中我们一起对CDN布局规划问题进行了拆解,明确了问题的边界。然而,talk is cheap, show me your code,本篇将开始动手环节,建立该问题的数学模型,并编写模型代码。本文代码基于 Python 3.9 + Gurobi 10.0.3 。如果你手上没有 Gurobi ,或者想用别的求解器来求解,那该如何是好?难道要从头学习其他求解器的API,然后重新写一遍几乎完全一样的代码吗?Don’t Repeat Yourself,我们下期也将介绍如何一键切换你的求解器,无需从头学习其他求解器的API,也无需编写新代码,直接求解。
1.基础网络构建
由业务问题拆解可知,该问题建立在一个基础的网络结构之上。因此,我们首先对网络基础要素进行梳理。
网络基础要素
设网络
G
=
(
V
,
E
)
G=(V,E)
G=(V,E),
V
V
V:节点集合。每个节点
i
∈
V
i \in V
i∈V存在以下关键属性:
1)需求
b
i
b_i
bi:
- b i < 0 b_i<0 bi<0:该点为与用户直接相连的网络节点, ∣ b i ∣ |b_i| ∣bi∣代表消费节点的需求带宽
- b i > 0 b_i>0 bi>0:该点为服务器部署节点, ∣ b i ∣ |b_i| ∣bi∣代表节点的输出能力
- b i = 0 b_i=0 bi=0:该点为中间节点,只起到传输的作用
2)部署成本 d i d_i di:在节点 i i i部署服务器将产生的成本
E E E:弧集合。每条弧 ( i , j ) ∈ E (i,j) \in E (i,j)∈E,存在以下关键属性:
- 容量上限 u i , j u_{i,j} ui,j:该条弧允许传输的最大带宽
- 单位传输费用 c i , j c_{i,j} ci,j:该条弧单位带宽的费用
- 流量 f i , j f_{i,j} fi,j:经过该条弧的总带宽
L = { 1 , . . . , m } L=\{1,...,m\} L={1,...,m}:服务器档次集合。每个档次 l ∈ L l \in L l∈L存在以下关键属性:
- 硬件成本 h l h_l hl:购买该档次服务器所需花费的费用
- 输出能力 p l p_l pl:该档次服务器最大能够输出的流量
2.数学模型刻画
在业务问题拆解中,我们介绍了该问题需要做出两方面的决策:
(1)如何选择服务器部署节点
(2)每个用户与所有服务器之间的网络路径以及路径上占用的带宽
对应的,我们会有两方面的决策变量:
- x i , l ∈ { 0 , 1 } , ∀ i ∈ V x_{i,l} \in \{0,1\}, \forall i \in V xi,l∈{0,1},∀i∈V:表示是否在节点 i i i部署档次为 l l l的服务器
- f i , j ∈ Z , 0 ≤ f i , j ≤ u i , j , ∀ ( i , j ) ∈ E f_{i,j}∈Z,0≤f_{i,j}≤u_{i,j},∀(i,j)∈E fi,j∈Z,0≤fi,j≤ui,j,∀(i,j)∈E:表示弧 ( i , j ) (i,j) (i,j)的流量
虚拟源点
乍一看,该问题是一个选址优化的问题,但是除了选址的决策外,我们还需要确定带宽的分配,且数据可能会经过多条网络路径。那么,我们可以将该问题视为一经典的多源点多汇点的费用流问题,从而同时考虑“选址+流量”两方面的决策。
建立一个虚拟源点
S
S
S,
S
S
S与所有的中间节点相连,即建立单向弧:
(
S
,
i
)
,
∀
i
∈
V
(S,i),\forall i \in V
(S,i),∀i∈V,该弧的流量无限,费用为0。所有的流量都从
S
S
S出发,经过中间节点和弧,最终到达消费节点。显然:
1)
b
S
=
b_S=
bS= 所有消费节点的带宽消耗需求总和
2)若
f
S
,
j
>
0
f_{S,j}>0
fS,j>0,说明中间节点
j
j
j部署了服务器
目标函数
目标为最小化总成本:部署成本总和+带宽租用成本总和
m
i
n
.
∑
i
∈
V
∑
l
∈
L
(
d
i
+
h
l
)
x
i
,
l
+
∑
(
i
,
j
)
∈
E
c
i
,
j
f
i
,
j
min. \quad \sum_{i\in V}\sum_{l\in L}{(d_i+h_l)x_{i,l}}+\sum_{\left(i,j\right)\in E}{c_{i,j}f_{i,j}}
min.i∈V∑l∈L∑(di+hl)xi,l+(i,j)∈E∑ci,jfi,j
约束条件
- 流守恒:
- 对于部署节点,流入的流量为0,流出的流量为供给量
- 对于用户节点,流入的流量为需求量,流出的流量为0
- 对于中转节点,流入的流量等于流出的流量
∑ ( i , j ) ∈ E f i , j − ∑ ( j , i ) ∈ E f j , i = b i , ∀ i ∈ V ∪ S \sum_{\left(i,j\right)\in E} f_{i,j}-\sum_{\left(j,i\right)\in E} f_{j,i}=b_i, \forall i\in V\cup{S} (i,j)∈E∑fi,j−(j,i)∈E∑fj,i=bi,∀i∈V∪S
- 网络节点最多只能部署一个服务器
∑ l ∈ L x i , l ≤ 1 , ∀ i ∈ V \sum_{l\in L} x_{i,l}\le1, \forall i\in V l∈L∑xi,l≤1,∀i∈V - 逻辑约束:若在节点
i
i
i部署了档次为
l
l
l的服务器,则弧
(
S
,
i
)
(S,i)
(S,i)上的流量
f
s
,
i
f_{s,i}
fs,i不能超过服务器的输出能力上限
p
l
p_l
pl
i f ∑ l ∈ L x i , l = 1 , t h e n f s , i ≤ p l if\ \sum_{l\in L} x_{i,l}=1,\ then\ f_{s,i}\le p_l if l∈L∑xi,l=1, then fs,i≤pl
i f ∑ l ∈ L x i , l = 0 , t h e n f s , i = 0 if\ \sum_{l\in L} x_{i,l}=0,\ then\ f_{s,i}=0 if l∈L∑xi,l=0, then fs,i=0
线性转化
上述逻辑约束为非线性约束,需要进行线性化:
f
s
,
i
≤
p
l
∑
l
∈
L
x
i
,
l
,
∀
i
∈
V
f_{s,i}\le p_l\sum_{l\in L} x_{i,l},\forall i\in V
fs,i≤pll∈L∑xi,l,∀i∈V
f
s
,
i
≥
∑
l
∈
L
x
i
,
l
,
∀
i
∈
V
f_{s,i}\geq\sum_{l\in L} x_{i,l},\forall i\in V
fs,i≥l∈L∑xi,l,∀i∈V
等价关系分析如下:
∑
l
∈
L
x
i
,
l
=
1
\sum_{l\in L} x_{i,l}=1
∑l∈Lxi,l=1 时,有
1
≤
f
s
,
i
≤
p
l
1\le f_{s,i}\le p_l
1≤fs,i≤pl
∑
l
∈
L
x
i
,
l
=
0
\sum_{l\in L} x_{i,l}=0
∑l∈Lxi,l=0 时,有
0
≤
f
s
,
i
≤
0
0\le f_{s,i}\le 0
0≤fs,i≤0,即
f
s
,
i
=
0
f_{s,i}=0
fs,i=0。
3.模型代码实现
1)基础网络数据加载解析
以下为加载基础数据的相关代码,网络结构信息利用NetworkX库来存储。

2)Gurobi建模与求解
构建虚拟源点与虚拟弧

创建决策变量

创建目标函数

创建约束条件

求解
某个小规模案例求解log如下:
Optimize a model with 641 rows, 1868 columns and 5016 nonzeros
Model fingerprint: 0x8b2b3561
Variable types: 0 continuous, 1868 integer (960 binary)
Coefficient statistics:
Matrix range [1e+00, 3e+02]
Objective range [1e+00, 5e+03]
Bounds range [1e+00, 1e+06]
RHS range [1e+00, 2e+03]
Found heuristic solution: objective 219986.00000
Presolve removed 60 rows and 487 columns
Presolve time: 0.01s
Presolved: 581 rows, 1381 columns, 3603 nonzeros
Found heuristic solution: objective 148186.00000
Variable types: 0 continuous, 1381 integer (540 binary)
Found heuristic solution: objective 145586.00000
Root relaxation: objective 3.958798e+04, 468 iterations, 0.00 seconds (0.00 work units)
Nodes | Current Node | Objective Bounds | Work
Expl Unexpl | Obj Depth IntInf | Incumbent BestBd Gap | It/Node Time
0 0 39587.9778 0 34 145586.000 39587.9778 72.8% - 0s
H 0 0 102992.00000 39587.9778 61.6% - 0s
H 0 0 87192.000000 39587.9778 54.6% - 0s
H 0 0 83043.000000 39587.9778 52.3% - 0s
0 0 42144.2319 0 109 83043.0000 42144.2319 49.3% - 0s
H 0 0 72878.000000 42144.2319 42.2% - 0s
H 0 0 70973.000000 42809.8713 39.7% - 0s
0 0 42946.6136 0 122 70973.0000 42946.6136 39.5% - 0s
0 0 42978.6986 0 130 70973.0000 42978.6986 39.4% - 0s
0 0 42978.6986 0 135 70973.0000 42978.6986 39.4% - 0s
0 0 44170.9399 0 163 70973.0000 44170.9399 37.8% - 0s
0 0 44625.8545 0 166 70973.0000 44625.8545 37.1% - 0s
0 0 44680.3733 0 172 70973.0000 44680.3733 37.0% - 0s
0 0 44684.3434 0 173 70973.0000 44684.3434 37.0% - 0s
0 0 44690.5862 0 175 70973.0000 44690.5862 37.0% - 0s
0 0 44692.3081 0 171 70973.0000 44692.3081 37.0% - 0s
H 0 0 66991.000000 45061.6853 32.7% - 0s
0 0 45061.6853 0 184 66991.0000 45061.6853 32.7% - 0s
H 0 0 66391.000000 45061.6853 32.1% - 0s
0 0 45264.1061 0 181 66391.0000 45264.1061 31.8% - 0s
0 0 45383.3795 0 186 66391.0000 45383.3795 31.6% - 0s
0 0 45400.7807 0 189 66391.0000 45400.7807 31.6% - 0s
0 0 45401.6067 0 193 66391.0000 45401.6067 31.6% - 0s
H 0 0 57072.000000 45401.6067 20.4% - 0s
0 0 45637.4756 0 188 57072.0000 45637.4756 20.0% - 0s
H 0 0 53569.000000 45694.3344 14.7% - 0s
H 0 0 52381.000000 45694.3344 12.8% - 0s
0 0 45694.3344 0 199 52381.0000 45694.3344 12.8% - 0s
0 0 45704.9157 0 193 52381.0000 45704.9157 12.7% - 0s
0 0 45714.0296 0 213 52381.0000 45714.0296 12.7% - 0s
0 0 45721.1947 0 214 52381.0000 45721.1947 12.7% - 0s
H 0 0 52244.000000 45723.4471 12.5% - 0s
0 0 45723.4471 0 219 52244.0000 45723.4471 12.5% - 0s
H 0 0 52234.000000 45906.9921 12.1% - 0s
0 0 45906.9921 0 224 52234.0000 45906.9921 12.1% - 0s
H 0 0 51513.000000 46023.6109 10.7% - 0s
0 0 46023.6109 0 236 51513.0000 46023.6109 10.7% - 0s
0 0 46050.2421 0 240 51513.0000 46050.2421 10.6% - 0s
0 0 46056.4006 0 248 51513.0000 46056.4006 10.6% - 0s
0 0 46062.3405 0 247 51513.0000 46062.3405 10.6% - 0s
0 0 46065.6351 0 250 51513.0000 46065.6351 10.6% - 0s
H 0 0 50985.000000 46065.6351 9.65% - 0s
H 0 0 50881.000000 46213.0157 9.17% - 0s
0 0 46213.0157 0 223 50881.0000 46213.0157 9.17% - 0s
H 0 0 50864.000000 46254.0013 9.06% - 0s
0 0 46254.0013 0 232 50864.0000 46254.0013 9.06% - 0s
0 0 46263.4558 0 232 50864.0000 46263.4558 9.04% - 0s
0 0 46283.1497 0 228 50864.0000 46283.1497 9.01% - 0s
H 0 0 50861.000000 46283.6392 9.00% - 0s
0 0 46283.6392 0 235 50861.0000 46283.6392 9.00% - 0s
0 0 46350.1715 0 231 50861.0000 46350.1715 8.87% - 0s
0 0 46362.0998 0 238 50861.0000 46362.0998 8.85% - 0s
0 0 46365.2646 0 244 50861.0000 46365.2646 8.84% - 0s
0 0 46402.5678 0 248 50861.0000 46402.5678 8.77% - 0s
0 0 46402.5678 0 248 50861.0000 46402.5678 8.77% - 0s
H 0 0 48643.000000 46402.5678 4.61% - 0s
0 2 46402.5678 0 248 48643.0000 46402.5678 4.61% - 0s
H 31 43 48631.000000 46519.7793 4.34% 82.8 0s
H 37 43 48628.000000 46525.3314 4.32% 74.0 0s
H 162 189 48614.000000 46525.3314 4.30% 46.0 0s
H 173 189 48561.000000 46525.3314 4.19% 45.0 0s
H 182 189 48528.000000 46525.3314 4.13% 44.8 0s
H 188 229 48399.000000 46525.3314 3.87% 43.6 0s
H 395 442 48345.000000 46525.3314 3.76% 33.1 0s
H 409 441 48341.000000 46525.3314 3.76% 32.3 0s
H 435 441 48326.000000 46525.3314 3.73% 31.7 0s
H 446 438 48280.000000 46525.3314 3.63% 31.3 0s
H 1006 900 48261.000000 46543.7005 3.56% 23.0 0s
H 1026 897 48258.000000 46543.7005 3.55% 23.1 0s
H 1045 888 48244.000000 46543.7005 3.52% 22.9 0s
H 1196 1048 48237.000000 46543.7005 3.51% 22.6 1s
H 1261 1046 48233.000000 46543.7005 3.50% 22.5 1s
H 1326 1034 48219.000000 47223.9267 2.06% 21.4 2s
H 1327 981 48218.000000 47229.1988 2.05% 21.4 2s
H 1345 942 48124.000000 47280.7639 1.75% 21.1 2s
H 1415 939 48116.000000 47360.6116 1.57% 25.8 4s
1418 941 47714.8143 64 328 48116.0000 47361.7927 1.57% 25.8 5s
H 1418 893 48109.000000 47361.7927 1.55% 25.8 5s
H 1505 911 48103.000000 47444.9116 1.37% 35.8 6s
H 1508 866 48081.000000 47444.9116 1.32% 35.8 6s
H 1537 853 48071.000000 47444.9116 1.30% 36.8 6s
H 1538 813 48046.000000 47444.9116 1.25% 36.9 6s
H 1542 775 48025.000000 47444.9116 1.21% 37.2 6s
H 1590 762 48016.000000 47444.9116 1.19% 38.6 6s
H 1591 729 48015.000000 47444.9116 1.19% 38.6 6s
H 1594 696 48003.000000 47444.9116 1.16% 38.8 6s
H 1596 665 48002.000000 47444.9116 1.16% 38.9 6s
H 1603 634 47989.000000 47444.9116 1.13% 39.0 6s
H 2188 734 47977.000000 47444.9116 1.11% 45.1 6s
* 2189 707 65 47942.000000 47444.9116 1.04% 45.1 6s
* 3739 1297 54 47932.000000 47562.4590 0.77% 45.6 7s
* 7386 2945 84 47925.000000 47657.0985 0.56% 47.1 9s
H 8060 2910 47900.000000 47668.2514 0.48% 47.9 9s
* 8194 2785 63 47891.000000 47668.2514 0.47% 47.7 9s
9317 3173 cutoff 53 47891.0000 47690.6018 0.42% 47.6 10s
*10269 2854 52 47862.000000 47703.4881 0.33% 48.3 10s
*10745 2377 60 47843.000000 47712.2186 0.27% 48.3 10s
*11655 1587 66 47819.000000 47732.0101 0.18% 48.5 11s
Cutting planes:
Gomory: 32
Implied bound: 1
MIR: 363
Flow cover: 187
Zero half: 1
RLT: 1
Relax-and-lift: 10
Explored 13830 nodes (632154 simplex iterations) in 11.75 seconds (10.40 work units)
Thread count was 12 (of 12 available processors)
Solution count 10: 47819 47843 47862 ... 47989
Optimal solution found (tolerance 1.00e-04)
Best objective 4.781900000000e+04, best bound 4.781900000000e+04, gap 0.0000%
我们发现:
即使是小规模,该问题的求解时长也并不算很短,说明该问题可能是一个比较困难的问题。下期我们也将给出更多案例的求解结果,并对比多个商业求解器在该问题上的表现。
末篇文章讲解(3)多求解器批量求解对比。
目前,市面上有众多商业和开源的 MIP 求解器可供选择,大多都支持不同的编程语言,且提供了丰富的建模求解接口。然而,如果我们想要更换求解器,或者想对不同求解器进行对比验证,调用不同求解器的 API 从头编写代码的方式需要重新学习各类求解器的 API,时间成本相对较高。
值得庆幸的是,大多数求解器都支持通过读取模型文件的方式进行求解,这意味着我们可以将问题建模后的模型数据保存到文件中,然后通过求解器的命令行进行求解,无需从头开始学习每种求解器的诸多接口,大大减少重复工作。
然而,不同求解器的命令行调用命令也存在差异。因此,我们对各类求解器的命令行调用方式进行了调研,并以前几期的 CDN 布局规划问题为例,对目前常见的 4 个求解器(Gurobi、CPLEX、COPT、MindOpt)进行了对比测试。
本文主要结构如下:
1. 各求解器命令行调用模式
2. 批量求解 Python 代码设计
3. CDN 布局规划问题求解对比分析
1.各求解器命令行调用模式
Gurobi
方式1:单行命令执行
gurobi_cl model.mps
方式2:交互执行
m=read("model.mps")
m.optimize()
CPLEX
交互式执行
read model.mps
optimize
COPT
方式1:交互式执行
read model.mps
optimize
方式2:读取命令文件
copt_cmd -i config.in
MindOpt
单行命令执行
mindopt model.mps
最终,确定各个求解器的调用模式,如下表所示:
| 求解器 | 调用模式 |
|---|---|
| Gurobi | ‘gurobi_cl model.mps’ |
| CPLEX | ‘read model.mps’, ‘optimize’ |
| COPT | ‘copt_cmd -i config.in’ |
| MindOpt | ‘mindopt model.mps’ |
注:在后文所述的利用Python脚本对这些求解器进行命令行调用的过程中,部分求解器的某些方式调用失败,暂时没有解决,如果有读者有相关经验,我们可以互相交流
那么,了解了怎么通过这些求解器的命令行求解模型文件,就足够了吗?
命令行调用的方式多数是交互模式,即需要在命令行窗口交互式地输入命令才能向下执行,如果我们想要批量自动化求解多个模型,则可以通过编写自动化脚本的方式实现。
接下来将介绍如何编写 Python 脚本,实现对不同求解器的命令行调用,并自动化求解多个模型数据文件。该脚本通过读取模型文件、设置参数和调用相应的求解器命令,实现了自动化求解的过程。使用时只需准备好模型文件,然后一键运行,即可求解多个模型数据文件。
2.批量求解 Python 代码设计
由于不同求解器的调用方式存在差异,而对于自动化批量求解来说,我们不关心采用哪种求解器完成求解这个操作,我们又不想把各个求解器的调用逻辑混在一块,然后通过丑陋的if else判断,同时还得考虑后续可能需要扩展其他的求解器。因此,本着开闭原则,我们决定采用继承的方式来实现这个功能。这样,每个求解器的调用实现完全独立,扩展其他求解器也无需修改已有代码。

基类:SolverCaller

子类:GurobiCaller、CplexCaller、CoptCaller、MindOptCaller

3.CDN布局规划问题求解分析
借助该脚本,对 4 个商业求解器(经小案例测试,开源求解器 SCIP、OR-Tools 等在该问题上求解速度与商业求解器差距较大,不再对比)进行了对比,求解了 3*10 共30个案例(数据来源:某公开比赛数据集),以下为求解结果:
求解时间对比图

横轴:案例编号
纵轴:各求解器的求解时长(秒)
注:除了求解时长限制为 3600s 外,所有求解器均采用默认参数。
结论:
30 个案例中,solver_1、solver_2、solver_3、solver_4 在 1 小时内达到时长限制的案例数量分别为:3、4、15、12。
另外,对于 solver_3 和 solver_4 均达到时长限制(1小时)的案例,统计各自最终的 Gap,如下图所示:
求解 Gap对比图

横轴:案例的编号
纵轴:Gap(%)
末篇:介绍了使用命令行调用求解器进行自动化批量求解的方法,并通过对CDN布局规划问题的多个实例进行比较分析,提供了对不同求解器求解性能的初步了解。需要强调的是,本文的对比结果只是针对这一特定问题的特定数据集,在其他数据,其他不同问题上,各个求解器的表现可能与本文中的存在差异。
结语
本系列文章:在该系列中,我们一起从问题拆解、模型刻画、批量求解等方面研究了CDN布局规划问题,并沉淀了一套多求解器、批量求解、可扩展的自动化方案。完结撒花~~~
值得一提的是,如果我们不追求最优解,而是想要在更短的时间内得到一个不错的规划方案,那么可以设计启发式算法来对该问题求解,这也是在本次案例对应比赛中的主流技术方案。得益于灵活、高效等特点,启发式算法在工业界的应用更加广泛,且在更前沿的应用中,会与机器学习、深度学习、强化学习等 AI 技术相结合。后续我们也将分享在业界落地的诸多技术方案,期待我们下一次的相遇。
请大家多多点赞、评论、转发、关注!大家的支持是我们持续创作的动力。我们是【运筹匠心】 ,咱们下期见~~~
参考文献:
[1]什么是CDN?它解决了什么难题?5分钟让你明明白白!
[2]Gurobi Optimizer Reference Manual
[3]启动 CPLEX
[4]COPT交互式命令行工具
[5]命令行调用 MindOpt

1071

被折叠的 条评论
为什么被折叠?



