禁忌搜索算法(现代优化计算方法)

1.局部搜索算法

a.定义

step1:选定一个初始可行解:x0;记录当前最优解:xbest = x0,T = N( xbest )
step2:当T \ ( xbest ) = 空集时,或者满足其他停止运算准则时,输出计算结果,停止运算;否则,从T中选一集合S,得到S中的最好接xnow;若f ( xnow ) < f ( xbest ),则xbest = xnow,T = N ( xbest );否则,T : = T \ S;重复step2。

b.方法1--全邻域搜索(以五城市对称TSP问题为例)

一、图:

二、算法描述(以五城市对称TSP问题为例):

初始:xbest = (A,B,C,D,E),f( xbest )=45,本例中,定义邻域映射为对换2个城市的位置。S为N( xbest )全集。

进入循环:

循环1:N(xbest)={(ABCDE),(ACBDE),(ADCBE),(AECDB),(ABDCE),(ABEDC),(ABCED)},对应目标函数为f(x)={45, 43, 45, 60, 60, 59, 44}

判定条件:xbest=(ACBDE),f(x)=43。xbest与初始状态不同,N(xbest) \ (xbest) != 空集

循环2:N(xbest)={(ACBDE),(ABCDE),(ADBCE),(AEBDC),(ACDBE),(ACEDB),(ACBED)},对应目标函数为f(x)={43, 45, 44, 59, 59, 58, 43}

判定条件:xbest=(ACBDE),f(x)=43。xbest与初始状态相同,N(xbest)与循环1相同,N(xbest) \ (xbest) = 空集,跳出循环

得解xbest=(ACBDE),f(xbest)=43。


三、代码:

void localsearchWNS(const vector< vector<int> > &v,vector<int> &x,int &costbest,int firstcity)
{
	swap(x[0],x[firstcity]);
	costbest=countDis(x,v);//countDis用于计算花费,代码在此不再贴出
	vector<int> tempx=x;
	int num=x.size();	
	while(true)
	{
		int min=costbest;
		for(int i=1;i<num-1;i++)
		{	
			for(int j=i+1;j<num;j++)
			{
				swap(x[i],x[j]);
				int temp=countDis(x,v);
				if(temp<min)
				{
					min=temp;
					tempx=x;
				}
				swap(x[i],x[j]);
			}	
		}
		if(min==costbest)
		{
			break;
		}
		costbest=min;
		x=tempx;
	}	
}

四、实验:

实验所得,当n<9(比较小的时候),所求与全局最优解相同(回溯求得),在n>10之后,开始有偏离,并且偏离程度不可预测。除起点A外,解空间树共4!=24种情况,以上搜索只搜索了12种,故局部搜索算法<全邻域搜索>并没有搜索完整棵解空间树,求得也不是全局最优解,而是局部最优解。用局部最优解代替全局最优解。

c.方法2--一步随机搜索

一、算法描述:

初始:xbest = (A,B,C,D,E),f( xbest )=45,本例中,定义邻域映射为对换2个城市的位置。

进入循环:

循环x:从N(xbest)中随机选择一点xnow,如果f(xnow)<f(xbest),则xbest=xnow,且重新构建N(xbest);如果f(xnow)>=f(xbest),则从N(xbest)中移除点xnow,继续循环

判定条件:N(xbest) = 空集,跳出循环

得解

二、代码:

void localsearchOSRS(const vector< vector<int> > &v,vector<int> &x,int &costbest,int firstcity)
{
	swap(x[0],x[firstcity]);
	costbest=countDis(x,v);
	vector< vector<int> > tempx;
	int num=x.size();	
	int flag=0;
	while(true)
	{
		if(flag==0)
		{
			for(int i=1;i<num-1;i++)
			{	
				for(int j=i+1;j<num;j++)
				{
					swap(x[i],x[j]);
					tempx.push_back(x);
					swap(x[i],x[j]);
				}	
			}
		}
		int n=random(0,tempx.size()-1);
		int cost=countDis(tempx[n],v);	
		if(cost<costbest)
		{
			costbest=cost;
			x=tempx[n];
			vector< vector<int> > clearv;
			tempx=clearv;
			flag=0;
		}
		else
		{
			if(tempx.size()>0)
			{
				tempx.erase(tempx.begin()+n);
				if(tempx.size()==0)
				{
					return;
				}
				flag=1;
			}
		}
	}
} 

三、实验:

实验所得n=7时就与最优解开始偏离,偏离程度不可预计,与初始可行解、邻域结构有关

d.分析

局部最优解偏离全局最优解的程度,一部分上取决于初始可行解的选择,一部分取决于邻域结构的选择。因此在使用局部搜索算法时,为了得到好的解,可以比较不同的邻域结构(比如,ABCDE,除初始点外,其余点顺序右移。好的邻域结构的作用是:通过此操作,改变序列,形成邻域,达到搜索更小的范围,却搜到更接近全局最优解的局部最优解的目的)和不同的初始点。一个非常直观的结论是:如果初始点的选择足够多,总可以计算出全局最优解。

2.禁忌搜索

a.定义

step1:给以禁忌表H=空集,并选定一个初始解xnow;

step2:满足停止规则时,停止计算,输出结果;否则,在xnow的领域N(xnow)中选择不受禁忌的候选集Can_N(xnow);在Can_N(xnow)中选一个评价值最佳的解xnext,xnow=xnext;更新历史记录H,保存f(xnow),重复step2;

step3:在保存的众多f中,挑选最小(大)值作为解;

b.思想

标记已经解得的局部最优解或求解过程,并在进一步的迭代中避开这些局部最优解或求解过程。局部搜索的缺点在于,太过于对某一局部区域以及其邻域的搜索,导致一叶障目。为了找到全局最优解,禁忌搜索就是对于找到的一部分局部最优解,有意识地避开它,从而或得更多的搜索区域。

比喻:兔子们找到了泰山,它们之中的一只就会留守在这里,其他的再去别的地方寻找。就这样,一大圈后,把找到的几个山峰一比较,珠穆朗玛峰脱颖而出。

c.基本概念

禁忌对象:禁掉谁?(根据受禁对象的不同选择,可行解是一禁禁一个;还是一禁禁掉一大片。主要对禁忌范围,及搜索范围有影响

禁忌长度:禁多久?(禁掉的东西什么时候放出来?禁忌长度过短,会导致循环;禁忌长度过长,会导致计算时间过长。主要对解的收敛速度有影响

禁忌表:包括禁忌对象和禁忌长度(总的来说就是:怎么禁?)

候选集:邻域中可行解的选取?(候选集的大小,过大增加计算内存和计算时间,过小过早陷入局部最优)

d.小栗子(以四城市非对称TSP为例)

一、图:

二、过程模拟(以四城市非对称TSP为例):

禁忌长度为3(等于候选集个数)


禁忌长度是3(等于候选集中对换个数)的缺点在于:如cd对换一直从步2禁忌到结束,虽然在步2与步1衔接时,防止了回到原先候选集的可能,但也同时抹除了后续ACBD->ADCB,ADCB->ACBD,ACDB->ADCB,ADCB->ACDB的可能。

将禁忌长度改为2,上述步骤一样


从禁忌长度为2的迭代与禁忌长度为3的迭代比较可得:
禁忌长度短会造成循环,也就可能在一个局部最优解的附近循环(步6,产生ABCD循环到步1)(极限状况下,禁忌长度为1:造成一直在一个局部最优解处循环,退化为局部搜索算法)(此处由于城市个数只有4个,除去起点A,BCD排列一共才6种,所以看上去好像<循环会产生的足够晚,会比禁忌长度更长的方案产生更多的候选局部最优解>。但如果城市个数是20呢?禁忌长度为1,是否会在第5,6步就产生循环呢?相对而言禁忌长度为(19-1)*(19)/2的方案,会迭代(19-1)*(19)/2步,产生更多候选局部最优解)。禁忌长度长会造成,算法的记忆存储量增加,使得算法的计算时间增加(譬如,上述例子,20个城市的TSP,从头禁到尾,一定要跑(19-1)*(19)/2步,但是禁忌长度短,或许早就出现循环,解收敛了)。

三、代码(禁忌长度等于禁忌对象个数):

#include"tsp.h"
using namespace std;
bool cmp(int a,int b)
{
	return a<b;
}
bool countMin(const vector< vector<int> > &v,vector<int> &x,vector< vector<int> > &tabutable,const int &num)
{
	int xtemp;
	int ytemp;
	int min=INT_MAX;
	for(int i=1;i<num-1;i++)
	{	
		for(int j=i+1;j<num;j++)
		{
			swap(x[i],x[j]);
			int temp=countDis(x,v);
			if(temp<min&&tabutable[x[i]][x[j]]==0&&tabutable[x[j]][x[i]]==0)
			{
				min=temp;
				xtemp=i;
				ytemp=j;
			}
			swap(x[i],x[j]);
		}	
	}
	if(min==INT_MAX)
	{
		return false;
	}
	else
	{
		swap(x[xtemp],x[ytemp]);
		tabutable[x[xtemp]][x[ytemp]]=1;
		tabutable[x[ytemp]][x[xtemp]]=1;
		return true;
	}
}

void tabusearchN(const vector< vector<int> > &v,vector<int> &x,int &costbest,int firstcity)
{
	swap(x[0],x[firstcity]);
	int num=x.size();//城市节点个数
	vector< vector<int> > tabutable;//禁忌表,tabutable[i][j]=z表示对换对(i,j)的禁忌长度为z 
	vector<int> f;//记录每一个局部最优解 
	initX(tabutable,num);
	
	for(int i=0;i<num;i++)//初始化禁忌长度为0 
	{
		for(int j=i+1;j<num;j++)
		{
			tabutable[x[i]][x[j]]=0;
			tabutable[x[j]][x[i]]=0;
		}
	}
	f.push_back(countDis(x,v));
	while(countMin(v,x,tabutable,num))
	{
		f.push_back(countDis(x,v));	
	}
	sort(f.begin(),f.end(),cmp);
	costbest=f[0];
}

考虑:禁忌长度的选择?候选集的选择?禁忌对象(禁忌表中被禁的那些变化元素)的选择?评价值的替代形式?终止原则怎样给出?

e.技术问题(解决上述<考虑>)

零、靠近全局最优解与计算时间长短的影响因素

1.是由禁忌长度(禁多久)的选择、禁忌对象(禁掉谁)的选择、候选集的选择、评价值的形式等综合影响的。

如以下禁忌对象的选择中,解的简单变化比(解的分量变化和目标值变化)的禁忌范围要小,这可能造成计算时间的增加,但它也给予了较大的搜索范围。但这只是给予了更大的搜索范围,并不代表它能比其他两种方式获得更好的解。比如在此同时,它的禁忌长度过短,导致过早陷入某个局部最优解的循环,那么再大的搜索范围也是没用的。

总的而言,对于上述因素的考虑,对于靠近全局最优解的考虑。它是一个搜索范围以及是否会过早陷入局部最优解的一个博弈。

2.计算时间长短主要体现在对解的评估,即计算目标值。

一、状态变化和禁忌对象的选择(对受禁范围的影响)

0)例子背景

初始解:xnow = (A,B,C,D,E),f( xnow)=45,N(xnow)邻域的构造:定义邻域映射为对换2个城市的位置。候选集Can_N(xnow)是从N(xnow)中选择除初始解外的5个可行解。

1)解的简单变化

x,y∈D(解的定义域)。简单解变化,x->y∈N(x)(x的邻域)。即在x的邻域中选择(体现在禁忌对象上就是禁掉)一个解了事。这种手段在两种局部搜索方法中都用过。这种变化将问题的解看成变化的最基本因素。

以上述背景为例:

N(xnow)={(ACBDE),(ADCBE),(AECDB),(ABDCE),(ABCED)},对应目标函数为f(x)={ 43, 45, 60, 60, 44}。比较选择f值最低的(ACBDE)作为xnext,并且将上一个xnow(A,B,C,D,E)拉入禁忌表,H={ ( A , B , C , D , E ; 45 ) }。注意是将一个可行解列为禁忌对象。

2)向量分量变化

(x0,x1,x2,x3....xn)->(x0,x1,y,x3...xn),这种变化方式在解空间树为子集树的向量上,体现为其中几个xi取反(0-1背包),变化数目为2^n;在解空间树为排列树的向量上,体现为swap其中两个元素,或多个元素(2-opt,TSP),变化数目为n!。体现在禁忌对象上就是禁掉一个交换对或者一种取反方式。这种变化以问题解向量的每个分量变化为基础因素

以上述背景为例:

N(xnow)={(ACBDE),(ADCBE),(AECDB),(ABDCE),(ABCED)},对应目标函数为f(x)={ 43, 45, 60, 60, 44}。比较选择f值最低的(ACBDE)作为xnext,并且将上一个xnow(A,B,C,D,E)到现在的xnext(A,C,B,D,E)之间的交换对(B,C)拉入禁忌表,H={ ( B,C) }。注意是将一个交换对列为禁忌对象。

3)目标值变化

H( a ) = { x ∈ D | f( x ) = a },即禁掉所有目标值为a的可行解。如此一来倘若a变成b,那么禁掉的目标变化的就是整整一个目标值为x的集合!

以上述背景为例:

N(xnow)={(ACBDE),(ADCBE),(AECDB),(ABDCE),(ABCED)},对应目标函数为f(x)={ 43, 45, 60, 60, 44}。比较选择f值最低的(ACBDE)作为xnext,并且将上一个xnow(A,B,C,D,E)的目标值45禁忌掉,H={ ( 45) }。注意是将一个目标值列为禁忌对象。

4)分析

控制其他变量,单就禁忌对象的选择和状态变化而言。解的简单变化比(解的分量变化和目标值变化)的禁忌范围要小,这可能造成计算时间的增加,但它也给予了较大的搜索范围;(解的分量变化和目标值变化)比解的简单变化的禁忌范围要大,这减少了计算时间,可能引发的问题是禁忌范围太大已至于陷在局部最优点。

受禁范围小,计算时间长,搜索范围大,易找出全局最优;受禁范围打,计算时间短,搜索范围小,易陷入局部最优。

二、禁忌长度的选择(对受禁时间的影响)

 1)分析

控制其他变量,单就禁忌长度的选择而言,禁忌长度越短,机器内存占用越少,解禁范围更大(搜索范围上限越大),但很容易造成搜索循环(实际去搜索的范围却很小),过早陷入局部最优。禁忌长度过长又会导致计算时间过长(具体例子见上粉字分析)

2)禁忌长度的选取方案

(1)t 可以为常数,易于实现。
(2)t∈[ tmin , tmax ],t 变化的,tmin和tmax是确定的。tmin和tmax根据问题的规模确定,t 的大小主要依据实际问题、实验和设计者的经验。
(3) tmin和tmax的动态选择。

三、特赦规则

通俗定义:对于在禁忌的对象,如果出现以下情况,不论现在对象的禁忌长度如何,均设为0

(1)基于评价值的规则,若出现一个解的目标值好于前面任何一个最佳候选解,可特赦;
(2)基于最小错误的规则,若所有对象都被禁忌,特赦一个评价值最小的解;

(3)基于影响力的规则,可以特赦对目标值影响大的对象。

四、候选集

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

五、评价函数

评价函数分为直接直接评价函数和简介评价函数

直接评价函数:上述例子,均直接使用目标值作为评价函数

间接评价函数:反映目标函数特性的函数(会比目标函数的计算更为简便,用以减少计算时间等)

六、终止规则

禁忌算法是一个启发式算法,我们不可能让禁忌长度充分大,所以一些直观的终止规则就出现了

(1)确定步数终止,无法保证解的效果,应记录当前最优解;

(2)频率控制原则,当某一个解、目标值或元素序列的频率超过一个给定值时,终止计算;

(3)目标控制原则,如果在一个给定步数内,当前最优值没有变化,可终止计算。






  • 11
    点赞
  • 63
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值