本文发布于公众号【数据魔术师】同名文章
一 什么是禁忌搜索算法?
禁忌搜索算法(Tabu Search Algorithm,简称TS)起源于对于人类记忆功能的模仿,是一种亚启发式算法(meta-heuristics)。它从一个初始可行解(initial feasible solution)出发,试探一系列的特定搜索方向(移动),选择让特定的目标函数值提升最多的移动。为了避免陷入局部最优解,禁忌搜索对已经历过的搜索过程信息进行记录,从而指导下一步的搜索方向。
禁忌搜索是人工智能的一种体现,是局部搜索的一种扩展。禁忌搜索是在邻域搜索(local search)的基础上,通过设置禁忌表(tabu list)来禁忌一些曾经执行过的操作,并利用藐视准则来解禁一些优秀的解。
二 禁忌搜索算法基本步骤:
① 初始化
利用贪婪算法等局部搜索算法生成一个初始解,清空禁忌表,设置禁忌长度。
② 邻域搜索产生候选解
根据步骤①产生初始解,通过搜索算子(search operators),如relocation、exchange、2-opt等,产生候选解(candidate solution),并计算各个候选解的适应值(即解对应的目标函数值)。
③ 选择最好的候选解
从步骤②产生的所有候选解中选出适应值最好的候选解,将其与当前最好解(即搜索算法开始到现在找到的最好解)进行比较,如果优于当前最好解,那么就不考虑其是否被禁忌,用这个最好的候选解来更新当前最好解,并且作为下一个迭代的当前解,然后将对应的操作加入禁忌表;如果不优于当前最好解,就从所有候选解中选出不在禁忌状态下的最好解作为新的当前解,然后将对应操作加入禁忌表。
④ 判断终止条件
若满足终止条件,则立即停止并输出当前最好解;否则继续搜索。一般终止条件为是否到达一定的迭代次数或者达到了一个时间限制。
禁忌搜索算法流程图:
禁忌搜索算法涉及编码解码(Encoding and decoding)、搜索算子(search operators)、邻域 (Neighborhood)、禁忌表(Tabu list)、禁忌长度(Tabu tenure)、候选解(Candidate solution)、藐视准则(Aspiration criterion)等关键组成部分。
关于禁忌搜索的上述相关内容在之前的推文中已有详细的介绍,分别从禁忌搜索的发展由来、主要构成要素和详细的实验结论三个角度给大家一一做了讲解,使大家对禁忌搜索有全方位的理解。下面给出两篇禁忌搜索推文的链接:干货 | 到底是什么算法,能让人们如此绝望?、干货|十分钟快速复习禁忌搜索(c++版)
下面我们以TSP问题为例说明介绍这些组成部分:如下图所示,有5个城市,任何两个城市之间的距离都是确定的,现要求一个旅行商从某城市出发必须经过每个城市一次且仅有一次,最后回到出发的城市,问如何确定一条最短的线路(每条边的长度已在图中标出)?
1.编码和解码(Encodingand Decoding): 与编码和解码的相关内容在之前的文章中出现,给出链接:干货 | 嘿!你和遗传算法的距离也许只差这一文(附C++代码和详细代码注释),构造初始解如下图所示:
初始解对应的适应值为
2.搜索算子:
(1)Relocation算子
该算子在当前解中选择并移除一个节点(node),然后再选择一个位置将选中的节点插入。
例子:
如图所示,当前解中选择节点2,再选择插入位置节点3(之后),执行后得到候选解,此时适应值变化量为
(2)Swap算子
该算子在当前解中同时选择两个不同的节点,然后对这两个节点的位置交换。
例子:
如图所示,当前解中选择节点2和4,再对这两个节点交换位置,执行后得到候选解2,此时适应值变化量为
3.邻域 (Neighborhood):从当前解对一系列的搜索方向进行一次试探(通过算子搜索一次)能得到的所有解的集合,即仅经过一次操作能得到的所有解。
例子:
如上图所示,通过②中搜索算子搜索一次得到的候选解的集合即为当前邻域。
4.禁忌表(Tabu List):记录当前所选择操作的状态变化,一般包括禁忌对象和禁忌长度。
例子:
在初始解的邻域中,候选解10为所有候选解中改进最大的解(即|ΔF|最大,ΔF=-2)。因此,候选解10被选中作为下一个迭代的当前解,则禁忌对象如上图所示,l为禁忌长度(即在未来的l次迭代中禁止移动节点4)。
5.禁忌长度(Tabu Tenure):禁止在之后的k次迭代中对禁忌表中所记录的状态进行改变,这里的k即称为禁忌长度。
6.候选解(Candidate Solution):当前邻域中的解。
7.藐视准则(Aspiration Criterion):从候选解集合中挑选出最好的候选解,将其与当前最好解进行比较,若其是被禁止的解但是优于当前最好解,那么就将其解禁,用来作为下一迭代的当前解并及替代当前最好解。藐视准则(Aspiration criterion)防止了因为禁忌表的存在,而错过优异解的情况出现。
三 禁忌搜索算法解带时间窗的车辆路径问题(VRPTW)
VRPTW问题可描述为:假设一个配送中心为周围若干个位于不同地理位置、且对货物送达时间有不相同要求的客户点提供配送服务。其中,配送中心全部用于运行的车辆都是同一型号的(即拥有相同的容量);配送中心对车辆出入的时间有限制;车辆在所有客户点有相同的停留服务时间。
给出的连通G=(N,A)中,n+1个位置节点表示为集合N={0,1,2,...,n},连接节点之间的边表示为集合A={(i,j):i≠j∈N}。其中节点0是仓库,剩余每个节点对应一个客户。假设车辆的速度恒定(即从节点i到节点j的行驶时间在数值上与其欧式距离相等)。可用的车辆数表示为m,所有车必须从位置0开始并回到位置0。每个点节i属于N且带有时间窗,其中和分别表示节点i最早和最晚允许开始接受货物的时间。若节点i被选中且在路线r中,则决策变量的值为1,否则为0。若边(i,j)被选中且在路线r中,则决策变量的值为1,否则为0。路线r中车辆抵达客户i的时间点用决策变量表示。在车辆早抵达的情况下,车辆必须等候至时间窗起始时间点。若抵达时间点没有超出时间窗的结束时间点,则服务成功(即获得利润)。
VRPTW问题在之前的推文中有更详细的介绍,分别从VRPTW问题的由来、建模实例和CPLEX求解方法三个角度给大家有层次地剖析,使大家能对于VRPTW问题有更深入的了解。下面给出VRPTW问题的一个介绍的链接:干货|十分钟快速掌握CPLEX求解VRPTW数学模型(附JAVA代码及CPLEX安装流程)
本文参照文献编写代码,具体操作设置如下:
编码方式采取自然数编码,利用将车辆所需服务客户点的集合(解集)作为集合内元素数目大小的自然数数组。数组中各个元素的值代表各个客户点的编号,元素的顺序代表服务客户点的顺序。
搜索算子采用插入算子:删除原路径中的客户节点,遍历插入到任意车辆路径的任意位置,选取邻域最好解或者非禁忌最好解作为下一迭代的当前解。邻域为插入算子完全遍历能得到的解的集合。
总迭代次数和禁忌长度分别设置为2000和40。
四 代码说明
(a). 代码模块说明
代码一共分为main()、ReadIn_and_Initialization()、Construction()、Calculation()、Tabu_Search()、Check()和Output()等7个函数模块构成,其中main()函数构建了算法的主体框架;ReadIn_and_Initialization()函数的功能是初始化所有变量,完成数据读入操作并存储;Construction()、Calculation()、Tabu_Search()这3个函数则为整个禁忌搜索算法(构建初始解、计算对应解的适应值、对邻域进行搜索并选择对应操作进行禁忌)的实现过程;Check()函数的功能是用来检验解是否满足对应的所有约束;Output()函数输出结果。
(b). 文本数据输入格式说明:
采取读取文本格式来作为数据输入的形式,具体格式见下表:
序号横坐标纵坐标服务量时间窗起始时间窗结束服务时间135.0035.000.000.00230.000.00241.0049.0010.00161.00171.0010.00
由上表可知,输入数据为一行七列的形式,依次为对应点的序号、横坐标、纵坐标、所需服务量、服务时间窗*起始*时间点、服务时间窗、结束时间点和服务所需时间。由此可见,节点1为仓库(depot),其他节点为待服务点。
若有兴趣进行技术上的交流,欢迎联系微信号:tigerqin_china