禁忌搜索算法

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。


三、代码:

[cpp]  view plain  copy
 print ?
  1. void localsearchWNS(const vector< vector<int> > &v,vector<int> &x,int &costbest,int firstcity)  
  2. {  
  3.     swap(x[0],x[firstcity]);  
  4.     costbest=countDis(x,v);//countDis用于计算花费,代码在此不再贴出  
  5.     vector<int> tempx=x;  
  6.     int num=x.size();     
  7.     while(true)  
  8.     {  
  9.         int min=costbest;  
  10.         for(int i=1;i<num-1;i++)  
  11.         {     
  12.             for(int j=i+1;j<num;j++)  
  13.             {  
  14.                 swap(x[i],x[j]);  
  15.                 int temp=countDis(x,v);  
  16.                 if(temp<min)  
  17.                 {  
  18.                     min=temp;  
  19.                     tempx=x;  
  20.                 }  
  21.                 swap(x[i],x[j]);  
  22.             }     
  23.         }  
  24.         if(min==costbest)  
  25.         {  
  26.             break;  
  27.         }  
  28.         costbest=min;  
  29.         x=tempx;  
  30.     }     
  31. }  

四、实验:

实验所得,当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) = 空集,跳出循环

得解

二、代码:

[cpp]  view plain  copy
 print ?
  1. void localsearchOSRS(const vector< vector<int> > &v,vector<int> &x,int &costbest,int firstcity)  
  2. {  
  3.     swap(x[0],x[firstcity]);  
  4.     costbest=countDis(x,v);  
  5.     vector< vector<int> > tempx;  
  6.     int num=x.size();     
  7.     int flag=0;  
  8.     while(true)  
  9.     {  
  10.         if(flag==0)  
  11.         {  
  12.             for(int i=1;i<num-1;i++)  
  13.             {     
  14.                 for(int j=i+1;j<num;j++)  
  15.                 {  
  16.                     swap(x[i],x[j]);  
  17.                     tempx.push_back(x);  
  18.                     swap(x[i],x[j]);  
  19.                 }     
  20.             }  
  21.         }  
  22.         int n=random(0,tempx.size()-1);  
  23.         int cost=countDis(tempx[n],v);    
  24.         if(cost<costbest)  
  25.         {  
  26.             costbest=cost;  
  27.             x=tempx[n];  
  28.             vector< vector<int> > clearv;  
  29.             tempx=clearv;  
  30.             flag=0;  
  31.         }  
  32.         else  
  33.         {  
  34.             if(tempx.size()>0)  
  35.             {  
  36.                 tempx.erase(tempx.begin()+n);  
  37.                 if(tempx.size()==0)  
  38.                 {  
  39.                     return;  
  40.                 }  
  41.                 flag=1;  
  42.             }  
  43.         }  
  44.     }  
  45. }   

三、实验:

实验所得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步,但是禁忌长度短,或许早就出现循环,解收敛了)。

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

[cpp]  view plain  copy
 print ?
  1. #include"tsp.h"  
  2. using namespace std;  
  3. bool cmp(int a,int b)  
  4. {  
  5.     return a<b;  
  6. }  
  7. bool countMin(const vector< vector<int> > &v,vector<int> &x,vector< vector<int> > &tabutable,const int &num)  
  8. {  
  9.     int xtemp;  
  10.     int ytemp;  
  11.     int min=INT_MAX;  
  12.     for(int i=1;i<num-1;i++)  
  13.     {     
  14.         for(int j=i+1;j<num;j++)  
  15.         {  
  16.             swap(x[i],x[j]);  
  17.             int temp=countDis(x,v);  
  18.             if(temp<min&&tabutable[x[i]][x[j]]==0&&tabutable[x[j]][x[i]]==0)  
  19.             {  
  20.                 min=temp;  
  21.                 xtemp=i;  
  22.                 ytemp=j;  
  23.             }  
  24.             swap(x[i],x[j]);  
  25.         }     
  26.     }  
  27.     if(min==INT_MAX)  
  28.     {  
  29.         return false;  
  30.     }  
  31.     else  
  32.     {  
  33.         swap(x[xtemp],x[ytemp]);  
  34.         tabutable[x[xtemp]][x[ytemp]]=1;  
  35.         tabutable[x[ytemp]][x[xtemp]]=1;  
  36.         return true;  
  37.     }  
  38. }  
  39.   
  40. void tabusearchN(const vector< vector<int> > &v,vector<int> &x,int &costbest,int firstcity)  
  41. {  
  42.     swap(x[0],x[firstcity]);  
  43.     int num=x.size();//城市节点个数  
  44.     vector< vector<int> > tabutable;//禁忌表,tabutable[i][j]=z表示对换对(i,j)的禁忌长度为z   
  45.     vector<int> f;//记录每一个局部最优解   
  46.     initX(tabutable,num);  
  47.       
  48.     for(int i=0;i<num;i++)//初始化禁忌长度为0   
  49.     {  
  50.         for(int j=i+1;j<num;j++)  
  51.         {  
  52.             tabutable[x[i]][x[j]]=0;  
  53.             tabutable[x[j]][x[i]]=0;  
  54.         }  
  55.     }  
  56.     f.push_back(countDis(x,v));  
  57.     while(countMin(v,x,tabutable,num))  
  58.     {  
  59.         f.push_back(countDis(x,v));   
  60.     }  
  61.     sort(f.begin(),f.end(),cmp);  
  62.     costbest=f[0];  
  63. }  

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

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)目标控制原则,如果在一个给定步数内,当前最优值没有变化,可终止计算。


  • 25
    点赞
  • 76
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值