本文是偏应用的简要总结。
关于禁忌搜索的基础知识和具体代码,网上有很多,不重复写了。本文没有代码,而是展示一个实例中的代码运行产生的中间结果,用于辅助理解算法流程。
本文分为四个部分:
第一部分,算法简要流程
第二部分,简单实例,按照第一部分的流程整理的代码运行时的迭代过程
第三部分,关于算法的全局搜索和局部搜索的理解
第四部分,算法思路整理
1. 简要流程
Step 1. 产生一个初始解,作为当前解,加入禁忌表
(开始进入迭代,迭代环节为Step 2 ~ Step 4)
Step 2. 创建当前解的候选解解集(邻域解解集)
Step 3. 在候选解解集中选择目标函数值最优且不在禁忌表中的解,作为新的当前解
Step 4. 新的当前解加入禁忌表,并更新禁忌表
(当满足迭代终止条件时,跳出迭代环节,执行Step 5)
Step 5. 输出搜索到的最优解
2. 流程应用实例——TSP问题
例子:TSP
10个城市
(20,20), (40,20), (60,20), (80,20), (80,50), (80,80), (60,80), (40,80), (20,80), (20,50)
最短路径:连成正方形方框的路径,长度240
自定义:
禁忌表保存的是完整路径,禁忌表长度为3。候选解的产生方式为交换一个解中相邻的两点的顺序。如果某轮迭代中产生的候选解都在禁忌表中,那么这轮迭代直接选择禁忌表中的最优解作为新的当前解,不更新禁忌表。用一个数字记录禁忌表中每个解能在迭代中保存的剩余轮数,当解的剩余保存轮数为0时,将解移出禁忌表。
(算法里的每种操作都可以自由设计,而且设计方式非常多)
代码运行迭代过程:(按第一部分的流程整理标注)
Step 1. 随机初始化一个解,并计算对应的目标函数值
[0, 9, 2, 3, 7, 4, 1, 6, 8, 5] 520.2093924550331
加入禁忌表,此时禁忌表
[0, 9, 2, 3, 7, 4, 1, 6, 8, 5] 520.2093924550331 剩余保存轮数3
此时最优解:[0, 9, 2, 3, 7, 4, 1, 6, 8, 5] 520.2093924550331
第一轮迭代:
Step 2. 创建候选解解集,并计算对应的目标函数值
[9, 0, 2, 3, 7, 4, 1, 6, 8, 5] 492.43861803764105
[0, 2, 9, 3, 7, 4, 1, 6, 8, 5] 577.2914317800268
[0, 9, 3, 2, 7, 4, 1, 6, 8, 5] 528.4259594741145
[0, 9, 2, 7, 3, 4, 1, 6, 8, 5] 543.4549456584007
[0, 9, 2, 3, 4, 7, 1, 6, 8, 5] 488.0983669457533
[0, 9, 2, 3, 7, 1, 4, 6, 8, 5] 503.0193520063054
[0, 9, 2, 3, 7, 4, 6, 1, 8, 5] 529.5104584130406
[0, 9, 2, 3, 7, 4, 1, 8, 6, 5] 480.2093924550331
[0, 9, 2, 3, 7, 4, 1, 6, 5, 8] 475.3565787126474
Step 3. 在候选解解集中选择解[0, 9, 2, 3, 7, 4, 1, 6, 5, 8] 475.3565787126474
Step 4. 选出的解加入禁忌表,禁忌表更新为
[0, 9, 2, 3, 7, 4, 1, 6, 8, 5] 520.2093924550331 剩余保存轮数2
[0, 9, 2, 3, 7, 4, 1, 6, 5, 8] 475.3565787126474 剩余保存轮数3
此时最优解:[0, 9, 2, 3, 7, 4, 1, 6, 5, 8] 475.3565787126474
第二轮迭代:
Step 2. 创建候选解解集,并计算对应的目标函数值
[9, 0, 2, 3, 7, 4, 1, 6, 5, 8] 435.3565787126474
[0, 2, 9, 3, 7, 4, 1, 6, 5, 8] 532.438618037641
[0, 9, 3, 2, 7, 4, 1, 6, 5, 8] 483.57314573172886
[0, 9, 2, 7, 3, 4, 1, 6, 5, 8] 498.60213191601497
[0, 9, 2, 3, 4, 7, 1, 6, 5, 8] 443.2455532033676
[0, 9, 2, 3, 7, 1, 4, 6, 5, 8] 458.1665382639197
[0, 9, 2, 3, 7, 4, 6, 1, 5, 8] 513.523116976567
[0, 9, 2, 3, 7, 4, 1, 5, 6, 8] 464.22205101855957
[0, 9, 2, 3, 7, 4, 1, 6, 8, 5] 520.2093924550331
Step 3. 在候选解解集中选择解[9, 0, 2, 3, 7, 4, 1, 6, 5, 8] 435.3565787126474
Step 4. 选出的解加入禁忌表,禁忌表更新为
[0, 9, 2, 3, 7, 4, 1, 6, 8, 5] 520.2093924550331 剩余保存轮数1
[0, 9, 2, 3, 7, 4, 1, 6, 5, 8] 475.3565787126474 剩余保存轮数2
[9, 0, 2, 3, 7, 4, 1, 6, 5, 8] 435.3565787126474 剩余保存轮数3
此时最优解:[9, 0, 2, 3, 7, 4, 1, 6, 5, 8] 435.3565787126474
第三轮迭代:
Step 2. 创建候选解解集,并计算对应的目标函数值
[0, 9, 2, 3, 7, 4, 1, 6, 5, 8] 475.3565787126474
[9, 2, 0, 3, 7, 4, 1, 6, 5, 8] 495.3565787126474
[9, 0, 3, 2, 7, 4, 1, 6, 5, 8] 446.4911064067352
[9, 0, 2, 7, 3, 4, 1, 6, 5, 8] 458.60213191601497
[9, 0, 2, 3, 4, 7, 1, 6, 5, 8] 403.2455532033676
[9, 0, 2, 3, 7, 1, 4, 6, 5, 8] 418.1665382639197
[9, 0, 2, 3, 7, 4, 6, 1, 5, 8] 473.52311697656705
[9, 0, 2, 3, 7, 4, 1, 5, 6, 8] 424.22205101855957
[9, 0, 2, 3, 7, 4, 1, 6, 8, 5] 492.43861803764105
Step 3. 在候选解解集中选择解[9, 0, 2, 3, 4, 7, 1, 6, 5, 8] 403.2455532033676
Step 4. 选出的解加入禁忌表,禁忌表更新为
[0, 9, 2, 3, 7, 4, 1, 6, 5, 8] 475.3565787126474 剩余保存轮数1
[9, 0, 2, 3, 7, 4, 1, 6, 5, 8] 435.3565787126474 剩余保存轮数2
[9, 0, 2, 3, 4, 7, 1, 6, 5, 8] 403.2455532033676 剩余保存轮数3
此时最优解:[9, 0, 2, 3, 4, 7, 1, 6, 5, 8] 403.2455532033676
迭代以此类推
3. 全局搜索 VS 局部搜索
全局搜索:理论上通过不断的扩展邻域得到的解也能覆盖全部的解空间
局部搜索:偏向于局部搜索,不断的从当前的一个解扩展出解的邻域,并在邻域中搜索
4. 核心思路
始终维护一个禁忌表,保存每轮迭代中产生的最优解,搜索时如果遇到这些最优解,避开它们搜索另外的最优解