干货 | Column Generation算法求解VRPTW松弛模型(附java代码)

寒假已经过去,小伙伴们这个假期里有没有好好学习呢?

在这里插入图片描述
眼看着寒假快结束,小编也赶紧抓住寒假的尾巴,快马加鞭地学习了一下列生成(Column Generation)的方法,并结合往期公众号的代码:

  • 干货 | 求解VRPTW松弛模型的Column Generation算法的JAVA代码分享
  • 干货 | VRPTW子问题ESPPRC的介绍及其求解算法的C++代码

编写了一份“模型求解主问题+pulse algorithm求解子问题”的求解VRPTW的列生成代码,在这里和大家分享最近学到的知识。

列生成概述

关于列生成方法,公众号内已经有许多文章介绍了,还不太了解的小伙伴可以点击传送门:

  • 干货 | 10分钟带你彻底了解Column Generation(列生成)算法的原理附java代码
  • 运筹学教学|列生成(Column Generation)算法(附代码及详细注释)

简单来说,列生成算法就是单纯形法的一种变体,更适合求解大规模线性规划问题。对于一些变量很多的问题,列生成方法在最开始只考虑其中一部分变量并得到最优解,在后续问题中通过求解子问题找到使得主问题非最优的变量,将新的变量加入求解的问题中,相当于在单纯形表中添加一列。

“找到使主问题非最优的变量”就是找最小/最大的reduce cost(这里不懂的小伙伴请复习单纯形法)。由于reduce cost即是影子价格,又是对偶变量,所以求解reduce cost也有两种方法:求解原问题的对偶问题,或者利用修正单纯形法得到新问题的 B n − 1 B_n^{-1} Bn1矩阵。上面两篇推文分别采用这两种方法求解,建议大家都能掌握。

VRPTW主问题/子问题

一般来说我们比较熟悉的模型是边-流模型,即:
在这里插入图片描述
但在这里,我们使用Set Covering的建模方法。这种模型是基于可行路径建模的,只要保证路径的可行性,模型形式上可以有很大的简化。
在这里插入图片描述
在这个模型中,约束(1)可以设置为等于1;约束(3)可以设置为0-1变量。即使不这样设置,由于目标值要求最小,模型结果也会自动实现这一约束。

求解子问题的部分我们采用“直接求解原问题的对偶问题”的方法。原问题的对偶问题经过转化可以得到一个ESPPRC的子问题:
在这里插入图片描述
这里的 λ i ∗ \lambda_i^* λi是由主问题确定的对偶变量。

模型的细节可以参考论文以及推文干货 | 10分钟带你彻底了解Column Generation(列生成)算法的原理附java代码,或者阅读论文原文(文末获取)。在这里小编就不赘述啦。

ESPPRC的Pulse Algorithm

关于这个算法,公众号文章:

  • 干货 | VRPTW子问题ESPPRC的介绍及其求解算法的C++代码

已经有所介绍,这里给大家简单补充一下小编的理解。

pulse算法的思想类似于深度优先搜索。实际上,使用最简单的深度优先搜索求解ESPPRC,只需要在搜索过程中判断是否符合时间窗、容量等约束,也可以成功求解。pulse算法的高明之处在于,在深度优先过程中引入了bounding scheme和roll back两部分,使用类似分支定界的方法进行剪枝,加快搜索速度。

在这里插入图片描述
roll back部分通过回滚操作检查新路径是否被旧路径dominate。这种dominance关系非常好判断。如图,路径1: v s → v i → v k → v j v_s → v_i → v_k → v_j vsvivkvj 是通过路径0: v s → v i → v j v_s → v_i → v_j vsvivj拓展得到的。路径2的容量明显小于路径1,对于满足三角不等式的算例而言,路径2的总时长也小于路径1,因此dominance的判断只需要对比路径1和路径0的reduce cost即可。

bound部分则更类似于一般的分支定界,通过给每条路径一个解的下界判断下界与最优解的关系,进行剪枝。下界通过一个前置的bound函数获得。简单来说,bound函数通过对每一个时间段 t t t的每一个客户点 c c c绘制一个二维表,记录从 c c c出发时间为 t t t执行ESPPRC算法到达终点的最小reduce cost值。那么对于每一条新路径,只需要根据当前时间和节点找到二维表中对应的reduce cost,加上该路径原先的值,就能得到整条路径reduce cost的下界。

bound函数的时间段由用户划分(比方说将总时长划分为5块)。在执行bound函数时,先计算时间较迟的部分,这样在bound函数内部执行ESPPRC时也能起到剪枝效果。

传统的最短路算法一般采用labeling算法求解。关于labeling,公众号内也有推文介绍:

  • 标号法(label-setting algorithm)求解带时间窗的最短路问题

相比于pulse算法,labeling算法一般采用广度优先搜索的思想,通过dominance rule进行占优剪枝。两种算法有很多异同,建议小伙伴们都有所了解。

代码分享&算法性能测试

终于到了激动人心的代码环节啦!学习了这么多理论知识,小伙伴们一定忍不住要亲自实现了吧!

在往期推文中,干货 | 求解VRPTW松弛模型的Column Generation算法的JAVA代码分享分享了一份通过模型求解ESPPRC子问题的列生成代码,这份代码中的主问题建模时在目标函数中加入了每个节点的service time,因此模型略有不同。
干货 | VRPTW子问题ESPPRC的介绍及其求解算法的C++代码则分享了pulse 算法求解ESPPRC的C++代码。

小编参考了这两份代码,完全按照文中的模型和思路编写了一份列生成+pulse的代码,供大家学习参考。相比于之前另一个小编编写的列生成代码,这份代码相对简单一些,可能更适合新手学习。

在这里,小编简单的测试,模型求解子问题与pulse算法求解子问题两种算法的时间:

算例\算法模型求解Pulse Algorithm
C101(25点)2.80.8
C101(50点)16.33.9
C101(100点)126.744.9

可见我们学习的pulse algorithm还是很有效果的,比模型求解快了3-4倍。

那么列生成求解VRPTW的内容就到这里结束了。小编认为在学习列生成的过程中,相比于理论知识的学习,学习编程的过程更加困难,尤其是对CPLEX等求解器的学习,需要阅读大量的API、参考代码。大家在学习的时候也千万不要觉得推文内容简单就小瞧算法,一定要亲手尝试编写才能有进步。

关注公众号【数据魔术师】,在公众号内输入【cgvrptw2】
或访问github:https://github.com/zll-hust/CG-VRPTW
即可获得本文代码文献!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值