一、前言
今年开年那会还在做一个课题的实验,那时候想用large neighborhood search来做一个问题,但是后来发现常规的一些repair、destroy算子效果并不是很好。后来才知道,large neighborhood search以及它的衍生算法,这类框架给人一种非常通用的感觉,就是无论啥问题都能往里面套。
往往的结果是套进去效果也是一般。这也是很多刚入行的小伙伴经常喜欢干的事吧,各种算法框架套一个问题,发现结果不好了就感觉换下一个。最后复现了N多个算法发现依然no process,这时候就会怀疑人生了。其实要想取得好的performance,肯定还是要推导一些问题特性,设计相应的算子也好,邻域结构也好。
好了,回到正题。当时我试了好几个large neighborhood search算子,发现没啥效果的时候,心里难受得很。那几天晚上基本上是转辗反侧,难以入眠,当然了是在思考问题。然后一个idea突然浮现在我的脑瓜子里,常规的repair算子难以在问题中取得好的performance,是因为约束太多了,插入的时候很容易违背约束。在不违背约束的条件下又难以提升解的质量,我就想能不能插入的啥时候采取branch and bound。遍历所有的可能插入方式,然后记录过程中的一个upper bound用来删掉一些分支。
感觉是有搞头的,后来想想,这个branch的方法以及bound的方法似乎是有点难设计。然后又搁置了几天,最后没进展的时候突然找了一篇论文,是好多年前的一篇文章了。里面详细讲解了large neighborhood search中如何利用branch and bound进行插入,后来实现了以下感觉还可以。感觉这个方法还是有一定的参考价值的,因此今天就来写写(其实当时就想写了,只不过一直拖到了现在。。。)
二、large neighborhood search
关于这个算法,我在此前的推文中已经有过相应的介绍,详情小伙伴们可以戳这篇的链接进行查看:
自适应大邻域搜索(Adaptive Large Neighborhood Search)入门到精通超详细解析-概念篇
我把其中的一段话摘出来:
大多数邻域搜索算法都明确定义它们的邻域。 在LNS中,邻域是由 d e s t r o y destroy destroy和 r e p a i r repair repair方法隐式定义的。 d e s t r o y destroy destroy方法会破坏当前解的一部分,而后 r e p a i r repair repair方法会对被破坏的解进行重建。 d e s t r o y destroy destroy方法通常包含随机性的元素,以便在每次调用 d e s t r o y destroy destroy方法时破坏解的不同部分。
那么,解 x x x的邻域 N ( x ) N(x) N(x)就可以定义为:首先通过利用 d e s t r o y destroy destroy方法破坏解 x x x,然后利用 r e p a i r repair repair方法重建解 x x x,从而得到的一系列解的集合。LNS算法框架如下:
有关该算法更详细的介绍可以参考Handbook Of Metaheuristics这本书2019版本中的Chapter 4
Large Neighborhood Search(David Pisinger and Stefan Ropke),文末我会放出下载的链接。
关于destroy算子呢,有很多种,比如随机移除几个点,贪心移除一些比较差的点,或者基于后悔值排序移除一些点等,这里我给出文献中的一种移除方式,Shaw (1998)提出的基于 r e l a t e n e s s relateness relateness进行移除:
假设需要从解中所有的 C u s t o m e r s Customers Customers移除 n n n个,它首先随机选择一个 C u s t o m e r Customer Custom