禁忌搜索(Tabu Search)原理梳理和应用细节-附求解VRPTW问题C++代码

简介: 禁忌搜索(Tabu Search)原理梳理和应用细节-附求解VRPTW问题C++代码

1、禁忌搜索(TS)的相关概念


禁忌搜索的基本原理是在局部搜索(Local Search)的基础上添加“记忆”机制,使搜索方向不能返回到之前的位置,实现这种“记忆”机制的方法为构建“禁忌表”。



1.1 搜索空间(search space)


TS的搜索空间指的是在算法搜索解的过程之中,所有可能被搜寻到的解所构成的集合。例如,在VRPTW问题中,搜索空间中的每一个点表示满足所有问题约束的一组车辆路径集合。值得注意的是:将搜索空间限制在可行解范围之内并不是一种好的策略,允许搜索到不可行解在多数情况下式非常有价值的,有时候这也是必要的(e.g 当初始解构建十分困难的时候)。




1.2 邻域结构(neighborhood structure)

邻域是全部搜索空间(search space)的一个子集。定义邻域为: N ( S ) N(S) N(S)

N ( S ) = { S o l u t i o n s   o b t a i n e d   b y   a p p l y i n g   a   s i n g l e   l o c a l   t r a n s f o r m a t i o n   t o   S } N(S)=\{Solutions \ obtained \ by \ applying \ a \ single \ local \ transformation \ to \ S\} N(S)={Solutions obtained by applying a single local transformation to S}

即对于搜索空间 中的某一个解,执行一次局部转变操作,这个局部转变操作的集合就叫做邻域。


以VRPTW问题为例,下面介绍两种邻域结构:


1 )  简单的VRPTW邻域结构

在当前解 s s s中随机选择一条车辆路径 r n r_n rn中的任意一个客户点 i i i进行移除,之后将移除的客户点 i i i插入到车辆路径 r n 或者 s中的的任意其他一条路径 r m中。


这种邻域结构的关键之处需要考虑怎样选择客户点 i的插入位置:


(1)可以随机选择一条路径中的随机一个位置进行插入;


(2)使用贪婪思想,计算所有插入可能,选择变动成本最小的插入位置进行插入;


(3)使用贪婪+随机的思想,选择某几条路径(2条)进行搜索,选择其中变动成本最小的位置进行插入。


2 )  复杂的VRPTW邻域结构

λ \qquad \lambda λ- i n t e r c h a n g e interchange interchange 方法:允许同时移动不同路径的多个客户点,也允许不同路径之间客户点的相互交换。


e j c t i o n    c h a i n s \qquad ejction\; chains ejctionchains 方法:包含一系列协同的客户点移动操作,如3- e j c t i o n    c h a i n s ejction\; chains ejctionchains 方法包含的动作为:将客户点 v 1 v_1 v1从路径 r 1 r_1 r1移动到路径 r 2 r_2 r2,将客户点 v 2 v_2 v2从路径 r 2 r_2 r2移动到路径 r 3 r_3 r3,将客户点 v 3 v_3 v3从路径 r 3 r_3 r3移动到路径 r 4 r_4 r4。


3 )其他复杂的VRPTW邻域结构


允许路径之间多个客户点进行同时交换操作。


1.3 禁忌表(tabu)


禁忌表是TS区别于传统LS的显著特征,禁忌表的建立是为了阻止算法向着已经探索过的方向进行寻优。禁忌表的存在使得算法能够有效从之前探索果的寻优空间中跳出来,从而增加寻优空间的广度,达到更好的寻优效果。


禁忌表中的禁忌移动采用一种短期记忆的机制,通常采用固定数量的记忆数量进行储存。最常用的禁忌表为:记录当前解之前几步的邻域动作,禁止在当前邻域动作中选择已经存在于禁忌表中的邻域动作。


以VRPTW问题为例:假设现在的邻域动作是将客户点 v 1从路径 r 1移动到路径 r 2,那么禁忌表中的禁忌邻域动作可以是禁止将客户点 v1从路径 r 2 移动到路径 r 1,记为: ( v 1 , r 2 , r 1 ) ,但这种禁忌约束不强,因为一旦客户点 v1又从路径路径 r 2 r_2 r2移动到路径 r 3 ,那么客户点就可能从路径 r3重新移回路径 r 1;较强的禁忌表形式为:禁止客户点 v 1 移动回路径 r 1,记为:(v1,r1),即忽略当前解的影响;更强的禁忌表形式为:要求客户点 v 1在接下来的几步之中禁止向任何路径进行移动,即: ( v 1 )。



1.4 解禁标准(Aspiration criteria)


为了防止禁忌表中的禁忌动作约束过强导致组织了积极搜索方向或者使得算法停滞,使用解禁标准来删除禁忌表中的某些禁忌动作。最简单且常用的解禁标准为:若某一个存在于禁忌表中的邻域动作生成了比当前最优解更优的解时,则将这个邻域动作从禁忌表中进行删除,因为这个新的解明显之前没有被访问过,所以不可能造成重复搜索的问题。



2、禁忌搜索(TS)的算法框架


定义 S S S表示当前解; S ∗ S^* S∗表示最优解; f ∗ f^* f∗表示最优解的目标值; N ( S ) N(S) N(S)表示当前解的邻域; N ˉ ( S ) \bar{N}(S) Nˉ(S)当前解的可行邻域,即 N ( S ) N(S) N(S)去除禁忌表中的邻域动作。


TS的整体算法框架如下表2.1所示。


9a970bf2476f44b08e27dd2f92f882c3.png


2.1 终止准则


终止准则可以参考以下几种:


1) 固定计算次数或者CPU计算时间


2) 连续一定次数没有对最优解进行优化


3) 目标函数值达到一定的阈值标准




3、禁忌搜索(TS)的分散搜索机制(Diversification)


由于TS的本质仍是LS,所以TS本身具有容易陷入局部最优的算法缺陷。为了克服或者弥补这一个缺陷,提出分散搜索的算法机制。分散搜索机制通常基于“长记忆”来完成,例如在频率记忆(frequency memory)中,算法执行过程中会记录下不同的解成分在当前解中出现的迭代次数之和,并应用这个信息来进一步扩大搜索空间。



例如在VRPTW中,算法执行过程中记录下每一个客户点被移除和插入的次数和频率,从而有针对性地探索广度更大的搜索空间。


具体来说,分散搜索有以下几种方法:




3.1 重启分散法(Restart diversification)


这种方法的思想为:迫使当前解或者最优解中某些使用频率较少的邻域动作被使用来进行当前解的优化,例如在VRPTW中,将某些不经常被移除的客户点强制进行移除和插入操作。



3.2 连续分散法(Continuous diversification)


这种方法设法将分散融入到搜索过程之中,其通过将邻域动作使用频率作为目标函数的一部分,以此来选择可能的更优的操作。


3.3 战略震荡(Strategy oscillation)


这种方法的核心在于允许邻域动作产生不可行解。其方法为:放松问题的某些约束,在产生一个新的解之后,衡量这个新的解对于放松的这些约束的违反程度,将违反程度通过一个惩罚项添加到目标函数之中。但这样做的关键决策之处为:需要仔细确定惩罚项的权重,这是很难进行有效确定的。为了规避这个问题,一般采用自适应惩罚权重的调整方法:当前几次迭代之中只出现的不可行解,则相关违背约束的惩罚权重增加;当前几次迭代的解均是可行解,则相关约束的惩罚权重减小。



通过惩罚权重的自适应改变和调整,会引导搜索在可行解和不可行解之间来回穿梭,因此增加了搜索的分散性。



4、TS求解VRPTW的代码


本文使用C++编程实现了TS求解VRPTW问题,代码已上传至GitHub:

https://github.com/Dragon-wang-fei-long/Huristic-code/tree/Dragon-wang-fei-long-heuristic-TS

相关文章
|
4天前
|
存储 安全 算法
【Linux | C++ 】基于环形队列的多生产者多消费者模型(Linux系统下C++ 代码模拟实现)
【Linux | C++ 】基于环形队列的多生产者多消费者模型(Linux系统下C++ 代码模拟实现)
21 0
|
4天前
|
算法 Linux 数据安全/隐私保护
【Linux | C++ 】生产者消费者模型(Linux系统下C++ 代码模拟实现)
【Linux | C++ 】生产者消费者模型(Linux系统下C++ 代码模拟实现)
12 0
|
10天前
|
C++
【C++】一文深入浅出带你参透库中的几种 [ 智能指针 ]及其背后实现原理(代码&图示)
【C++】一文深入浅出带你参透库中的几种 [ 智能指针 ]及其背后实现原理(代码&图示)
|
10天前
|
C++ 数据格式
【C++】C++中的【文件IO流】使用指南 [手把手代码演示] & [小白秒懂]
【C++】C++中的【文件IO流】使用指南 [手把手代码演示] & [小白秒懂]
【C++】C++中的【文件IO流】使用指南 [手把手代码演示] & [小白秒懂]
|
10天前
|
编译器 C++
【C++】【C++的常变量取地址问题(对比C的不同)】const修饰的常变量&volatile修饰用法详解(代码演示)
【C++】【C++的常变量取地址问题(对比C的不同)】const修饰的常变量&volatile修饰用法详解(代码演示)
|
11天前
|
C++
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P6】大二C++实验作业-模板(4道代码题)【解析,注释】
|
11天前
|
Serverless C++ 容器
【期末不挂科-C++考前速过系列P5】大二C++实验作业-多态性(3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P5】大二C++实验作业-多态性(3道代码题)【解析,注释】
|
11天前
|
C++ 芯片
【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P4】大二C++实验作业-继承和派生(3道代码题)【解析,注释】
|
11天前
|
编译器 C++
【期末不挂科-C++考前速过系列P3】大二C++第3次过程考核(20道选择题&12道判断题&2道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P3】大二C++第3次过程考核(20道选择题&12道判断题&2道代码题)【解析,注释】
|
11天前
|
C++
【期末不挂科-C++考前速过系列P2】大二C++第2次过程考核(20道选择题&10道判断题&3道代码题)【解析,注释】
【期末不挂科-C++考前速过系列P2】大二C++第2次过程考核(20道选择题&10道判断题&3道代码题)【解析,注释】