这是一个号称Google官方的开源优化sdk, 包括建模框架,CP求解器,支持市面上主流求解器,同时自带部分算法。这套框架已经上线好几年了,一直没有机会尝试。这次就实现一个简单的CVRP(Capacitated Vehicle Routing Problem, 运力约束的车辆路径规划)来测试一下。之后会将尝试VRPTW/MDCVRPTW等更加实际的模型。 该sdk目前最大的风险是只有一个人在维护。作者Laurent Perron是Google Paris的员工,而之前11年则一直在ILOG工作,2008年离职! 要知道2008年IBM收购了著名的ILOG,许多ILOG的精英大佬不满IBM的收购成批出逃。一部分不甘认输另起炉灶,创立Gurobi和IBM分庭抗礼。而这帮精英大佬也确实说到做到,短短几年就让Gurobi声名鹊起,现在已经稳稳压IBM CPLEX一头。 而这位大哥则选择投奔Google,凭一人之力,弄出了这套sdk。可见当年合并前的ILOG,技术实力恐怖如斯!
简介
该套件包括以下内容
- constraint programming solver
- 支持主流求解器(CBC, CLP, GLOP, GLPK, Gurobi, Cplex, SCIP)
- 各种网络算法: 最短路径(shortest path),最大流(max flow), 最小费用流(min cost flow)
- 旅行商问题TSP(traveling salesman problem), 路径规划问题VRP(vehicle routing problem)
- 部分动态规划算法
其中最大的亮点在于能求解VRP。 在OR-tool的范例中,不但给出了VRP的例子,还给了一个VRPTW(vehicle routing problem with time window)的例子。 这就很厉害了,市面上针对VRP的成熟sovler凤毛麟角。 可在共享经济,新零售和无人驾驶的狂潮之下,VRP的应用场景实在太多了,比如共享送货, 接N个单的共享专车,等等。
安装
官方教程简单明了,使用pip安装十分容易。很可惜,目前or-tool还不支持conda。
之前提到or-tool的开发人员实际只有一人, 实在精力有限。 开发论坛里15年就有人提出建议,希望能加入conda安装。Laurent给出的理由是:没有时间。 也是艰难的很。
案例
我手头上正好有一个共享送菜案例的订单记录, 不妨用这个小数据集作为一个案例。该数据包含300+客户和一个货站。为了简化问题,这里不考虑运力,时间等约束,只对路径进行优化。 好了,下面是小xuo生应用题时间, 题目如下:
有一家货运公司,需要给300+个客户需要送货,假设客户均只送一件。 公司现有小货车10辆, 每辆能装载35件货物。 公司王经理负责为车队设计送货路线, 为保证司机师傅安全驾驶,每条线路总里程不能超过50公里。每条线路从货站(index = 0)出发, 最终返回货站。 王经理希望在满足以上条件的情况下,总里程最短。 请问王经理该如何实现?
模型
客户的位置的经纬度已经得到,格式如下:
id | due_at | latitude | longitude |
---|---|---|---|
240254 | 2014-03-13 17:00:00 | 37.7819410348 | -122.398127254 |
240368 | 2014-03-13 18:00:00 | 37.7861609629 | -122.399636312 |
240369 | 2014-03-13 18:00:00 | 37.7789823487 | -122.407610425 |
货站只有一个,所有货运路径必须从货站出发,最后回到货站。 数据中也给出最晚的送达时间due_at
,留作以后扩展到VRPTW的时候使用。在这篇文章中只考虑VRP问题。 每辆车装载量是35件,每个客户只送一件, 即意味着每条线路最多拜访35个客户。 司机师傅只能开50公里, 那么每条线路的总距离是50.
因此该问题可以描述为:
- 目标函数: 总路径最短
- 约束1 - 距离约束: 每条路径最大距离50公里
- 约束2 - 容量约束: 每条路径最多拜访35个客户
- 约束3 - 车辆约束: 10辆小车,因此只规划10条线路
建立数据对象
|
|
距离
给定两点的经纬度,地表直线距离可以用Haversine公式进行计算:
$$\begin{aligned} hav(\frac{d}{r}) & = hav(lat_2 - lat_1) + cos(lat_1)\cdot cos(lat_2)\cdot hav(lon_2 - lon_1) \\ hav(\theta) &= sin^2(\frac{\theta}{2}) \\ \text{其中, }d & = 2r\cdot arcsin(\sqrt{sin^2(\frac{lat_2-lat_1}{2})+cos(lat_1)\cdot cos(lat_2)\cdot sin^2(\frac{lon_2 - lon_1}{2})}) \end{aligned}$$其中
$(lat_1, lon_1)$和$(lat_2, lon_2)$是给定两点
$r\simeq6373 km$是地球半径
$d$是两点距离
Haversine距离计算函数
|
|
定义约束1 - 距离约束
Google OR tool的routing算法中,约束使用dimension对象来定义。使用.AddDimension()
添加约束。 包括如下参数:
- 约束量化表达式。在这个例子里我们定义了描述两点距离的对象distance_evaluator。对于路径规划来说,大多数约束都是基于from, to两点关系的函数。因此具有普遍性
- slack数量。我们用0表示没有slack。
- 最大数值上限。每条路径最大里程50公里
- 数值初始化为0。每条路径初始里程为0
- 约束名称。 定义好约束后,可以使用名称来调用对应的约束。在例子中,距离约束命名为“distance”, 之后使用
distance_dimension = routing.GetDimensionOrDie(distance)
调用了该约束。
|
|
定义约束2 - 容量约束
容量约束比距离约束简单一些,每个点的货运需求均为1,不与其他点有任何的关系。即一条线路,点数量的总和不超过35。具体实现可以比照距离约束。
主函数
|
|
结果
以下是其中一辆车的结果。 共拜访14个客户,行驶10.3公里
Route for vehicle 0:
0(1) -> 190(2) -> 264(3) -> 277(4) -> 250(5) -> 241(6) -> 240(7) -> 274(8) -> 275(9) -> 12(10) -> 116(11) -> 6(12) -> 149(13) -> 290(14) -> 0
Distance of the route 0: 10.292126033856508
Load of the route: 14
最终路径如下图:
(地图使用Tableau绘制)
源码
|
|