禁忌搜索 理解与c++实现 解决旅行商问题

禁忌搜索 理解与c++实现 解决旅行商问题

首先介绍一下旅行商问题本身,

旅行商问题,即TSP问题(Traveling Salesman Problem)又译为旅行推销员问题、货郎担问题,是数学领域中著名问题之一。假设有一个旅行商人要拜访n个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值。

和最小生成树有点类似,不同的是,每个地点只能走一次,并且需要返回开始的地点。

TSP问题是一个组合优化问题。该问题可以被证明具有NPC计算复杂性。因此,任何能使该问题的求解得以简化的方法,都将受到高度的评价和关注。

旅行推销员问题是图论中最著名的问题之一,即“已给一个n个点的完全图,每条边都有一个长度,求总长度最短的经过每个顶点正好一次的封闭回路”。Edmonds,Cook和Karp等人发现,这批难题有一个值得注意的性质,对其中一个问题存在有效算法时,每个问题都会有有效算法。

迄今为止,这类问题中没有一个找到有效算法。倾向于接受NP完全问题(NP-Complete或NPC)和NP难题(NP-Hard或NPH)不存在有效算法这一猜想,认为这类问题的大型实例不能用精确算法求解,必须寻求这类问题的有效的近似算法。

所以考虑用禁忌搜索尝试解决,禁忌搜索的资料好像也比较少。

禁忌搜索

禁忌搜索(Tabu Search,TS,又称禁忌搜寻法)是一种现代启发式算法,由美国科罗拉多大学教授Fred Glover在1986年左右提出的,是一个用来跳脱局部最优解的搜索方法。其先创立一个初始化的方案;基于此,算法“移动”到一相邻的方案。经过许多连续的移动过程,提高解的质量。

先介绍最核心的思路:

通过改进局部搜索,以跳脱局部最优解。

在一定时间里不找以前选择过的解,在一定情况下可以特赦。

原理听起来很简单,但是实现起来其实细节很多,这里以前选择过的解是广义上的,不一定是解本身,可以是类似的转移动作。

介绍一下几个名词

邻域:
下一步所有可能的解的集合。

领域动作
产生领域的方法,在tsp问题中,为交换两个地方的位置。

候选集
有的时候领域数量比较多,不把所有领域都做为候选集。可以采取一定的选取方式,或是随机,或是最优的前k个,亦或是别的方式。选择出候选集。

禁忌表
存储已经走过的解,当时间超过禁忌长度的时候,就可以释放。

禁忌长度:
禁忌表生效的时间,禁忌长度越短,内存占用越少,解禁范围越大(搜索范围上限越大),容易造成循环搜索,过早陷入局部最优。反之相反。

特赦规则
当有的解存在于禁忌表中时,不能一味的全部舍弃,当满足一定的规则时,就可以将他特赦,也就是参与选择,一般有以下几种特赦规则。
(1)基于评价值的规则,若出现一个解的目标值好于前面任何一个最佳候选解,可特赦;
(2)基于最小错误的规则,若所有对象都被禁忌,特赦一个评价值最小的解;
(3)基于影响力的规则,可以特赦对目标值影响大的对象。

候选集
候选集的大小,过大增加计算内存和计算时间,过小过早陷入局部最优。候选集的选择一般由邻域中的邻居组成,可以选择所有邻居,也可以选择表现较好的邻居,还可以随机选择几个邻居。

评价函数
评价函数分为直接评价函数和间接评价函数。
直接评价函数:上述例子,均直接使用目标值作为评价函数。
间接评价函数:反映目标函数特性的函数(会比目标函数的计算更为简便,用以减少计算时间等)。

终止规则
禁忌搜索是一个启发式算法,我们不可能让搜索过程无穷进行,所以一些直观的终止规则就出现了
(1)确定步数终止,无法保证解的效果,应记录当前最优解;
(2)频率控制原则,当某一个解、目标值或元素序列的频率超过一个给定值时,终止计算;
(3)目标控制原则,如果在一个给定步数内,当前最优值没有变化,可终止计算。

由此可见

我认为禁忌搜索是一种思想,上面的参数多到出奇,禁忌的方法也不限于狭义的解,例如在tsp问题中,将所有领域进行编号,禁忌的是领域的编号,在状态转换后,领域编号所对应的解已经不同,但是仍然放在禁忌表中。所以我认为禁忌搜索灵活且可拓展性大。具体到自己实现的时候,也确实遇到很多困难。

网上分析了一位大佬的代码,照着分析了一下。并自己划分了一下功能。

我将一次禁忌搜索分为进行K次小搜索

初始化全局最优解,禁忌表
循环K次小型搜索
	初始化搜索相关解(小型搜索最优解,特赦最优解,迭代过程中最优解)
	迭代N次
		1.遍历邻域
			if 在禁忌表中,更新特赦最优解
			else 不在表中,更新迭代最优解
		2.判断特设最优解是否满足特赦条件(常见为两种)
		3.选择迭代最优解进行转移,并更新禁忌表//可用优先队列 但是禁忌表往往规模不大
	检查全局最优解和搜索最优解

按照这个思路写了代码,相关注意点也写在了注释里

#include<bits/stdc++.h>
using namespace std;
#define rand(a,b) ((rand()%(b-a))+a)
const int INF=INT_MAX;
const int K=25;//小型搜索的次数
const int ITERATIONS=100 ;//小型搜索中的迭代次数
const int TABU_SIZE=10;//禁忌长度
const int SWAPSIZE=5;//交换数目 ,理解为候选集,此代码中没有用到

int city[60][2];//记录城市坐标
double adj[60][60];//记录城市之间的距离
int nowPath[60];//当前路径
int finalBestPath[60];//最优路径
int TabuList[2000][3];//第一维度 邻域id 第二维度 0:start 1:end 2.tabu
//原文件里还有个dis但是个人觉得没有必要 因为没有复用到
//在最大规模为60的问题中 邻域数量最大为60*59/2
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值