模拟退火算法——解决售货员的难题

模拟退火算法——解决售货员的难题

分类: 解题报告 其他   1000人阅读  评论(2)  收藏  举报

Simulation Annealing
    1982年,KirkPatrick将退火思想引入组合优化领域,提出一种解大规模组合优化问题的算法,对NP完全组合优化问题尤其有效。这源于固体的退火过程,即先将温度加到很高,再缓慢降温(即退火),使达到能量最低点。如果急速降温(即为淬火)则不能达到最低点.。
   模拟退火算法是一种能应用到求最小值问题或基本先前的更新的学习过程(随机或决定性的)。在此过程中,每一步更新过程的长度都与相应的参数成正比,这些参数扮演着温度的角色。然后,与金属退火原理相类似,在开始阶段为了更快地最小化或学习,温度被升得很高,然后才(慢慢)降温以求稳定。
模拟退火算法是一种用于求解大规模优化问题的随机搜索算法,它以优化问题求解过程与物理系统退火过程之间的相似性为基础;优化的目标函数相当于金属的内能;优化问题的自变量组合状态空间相当于金属的内能状态空间;问题的求解过程就是找一个组合状态,使目标函数值最小。利用Metropolis准则并适当地控制温度的下降过程实现模拟退火,从而达到在多项式时间内求解全局优化问题的目标。
   模拟退火算法来源于固体退火原理,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。根据Metropolis准则,粒子在温度T时趋于平衡的概率为e-ΔE/(kT),其中E为温度T时的内能,ΔE为其改变量,k为Boltzmann常数。用固体退火模拟组合优化问题,将内能E模拟为目标函数值f,温度T演化成控制参数t,即得到解组合优化问题的模拟退火算法:由初始解i和控制参数初值t开始,对当前解重复“产生新解→计算目标函数差→接受或舍弃”的迭代,并逐步衰减t值,算法终止时的当前解即为所得近似最优解,这是基于蒙特卡罗迭代求解法的一种启发式随机搜索过程。退火过程由冷却进度表(Cooling Schedule)控制,包括控制参数的初值t及其衰减因子Δt、每个t值时的迭代次数L和停止条件S。 
模拟退火算法的模型
  模拟退火算法可以分解为解空间、目标函数和初始解三部分。
 模拟退火的基本思想:
  (1) 初始化:初始温度T(充分大),初始解状态S(是算法迭代的起点), 每个T值的迭代次数L
  (2) 对k=1,……,L做第(3)至第6步:
  (3) 产生新解S′
  (4) 计算增量Δt′=C(S′)-C(S),其中C(S)为评价函数
  (5) 若Δt′<0则接受S′作为新的当前解,否则以概率exp(-Δt′/T)接受S′作为新的当前解.
  (6) 如果满足终止条件则输出当前解作为最优解,结束程序。
终止条件通常取为连续若干个新解都没有被接受时终止算法。
  (7) T逐渐减少,且T->0,然后转第2步。
算法对应动态演示图:
模拟退火算法新解的产生和接受可分为如下四个步骤:
  第一步是由一个产生函数从当前解产生一个位于解空间的新解;为便于后续的计算和接受,减少算法耗时,通常选择由当前新解经过简单地变换即可产生新解的方法,如对构成新解的全部或部分元素进行置换、互换等,注意到产生新解的变换方法决定了当前新解的邻域结构,因而对冷却进度表的选取有一定的影响。
  第二步是计算与新解所对应的目标函数差。因为目标函数差仅由变换部分产生,所以目标函数差的计算最好按增量计算。事实表明,对大多数应用而言,这是计算目标函数差的最快方法。
  第三步是判断新解是否被接受,判断的依据是一个接受准则,最常用的接受准则是Metropo1is准则: 若Δt′<0则接受S′作为新的当前解S,否则以概率exp(-Δt′/T)接受S′作为新的当前解S。
  第四步是当新解被确定接受时,用新解代替当前解,这只需将当前解中对应于产生新解时的变换部分予以实现,同时修正目标函数值即可。此时,当前解实现了一次迭代。可在此基础上开始下一轮试验。而当新解被判定为舍弃时,则在原当前解的基础上继续下一轮试验。
  模拟退火算法与初始值无关,算法求得的解与初始解状态S(是算法迭代的起点)无关;模拟退火算法具有渐近收敛性,已在理论上被证明是一种以概率l 收敛于全局最优解的全局优化算法;模拟退火算法具有并行性。
模拟退火算法的简单应用
  作为模拟退火算法应用,讨论货郎担问题(Travelling Salesman Problem,简记为TSP):设有n个城市,用数码1,…,n代表。城市i和城市j之间的距离为d(i,j) i, j=1,…,n.TSP问题是要找遍访每个域市恰好一次的一条回路,且其路径总长度为最短.。
  求解TSP的模拟退火算法模型可描述如下:
  解空间 解空间S是遍访每个城市恰好一次的所有回路,是{1,……,n}的所有循环排列的集合,S中的成员记为(w1,w2 ,……,wn),并记wn+1= w1。初始解可选为(1,……,n)
  目标函数 此时的目标函数即为访问所有城市的路径总长度或称为代价函数: 
  我们要求此代价函数的最小值。
  新解的产生 随机产生1和n之间的两相异数k和m,若k,则将
  (w1, w2 ,…,wk , wk+1 ,…,wm ,…,wn)
  变为:
  (w1, w2 ,…,wm , wm-1 ,…,wk+1 , wk ,…,wn).
  如果是k>m,则将
  (w1, w2 ,…,wk , wk+1 ,…,wm ,…,wn)
  变为:
  (wm, wm-1 ,…,w1 , wm+1 ,…,wk-1 ,wn , wn-1 ,…,wk).
  上述变换方法可简单说成是“逆转中间或者逆转两端”。
  也可以采用其他的变换方法,有些变换有独特的优越性,有时也将它们交替使用,得到一种更好方法。 
  代价函数差 设将(w1, w2 ,……,wn)变换为(u1, u2 ,……,un), 则代价函数差为: 
根据上述分析,可写出用模拟退火算法求解TSP问题的伪程序:
Procedure TSPSA:
 begin 
  init-of-T; { T为初始温度}
  S={1,……,n}; {S为初始值}
  termination=false;
  while termination=false
   begin 
    for i=1 to L do
      begin
        generate(S′form S); { 从当前回路S产生新回路S′}
        Δt:=f(S′))-f(S);{f(S)为路径总长}
        IF(Δt<0) OR (EXP(-Δt/T)>Random-of-[0,1])
        S=S′;
        IF the-halt-condition-is-TRUE THEN 
        termination=true;
      End;
    T_lower;
   End;
 End
模拟退火算法的应用很广泛,可以较高的效率求解最大截问题(Max Cut Problem)、0-1背包问题(Zero One Knapsack Problem)、图着色问题(Graph Colouring Problem)、调度问题(Scheduling Problem)等等。
模拟退火算法的参数控制问题
  模拟退火算法的应用很广泛,可以求解NP完全问题,但其参数难以控制,其主要问题有以下三点:
  (1) 温度T的初始值设置问题。
  温度T的初始值设置是影响模拟退火算法全局搜索性能的重要因素之一、初始温度高,则搜索到全局最优解的可能性大,但因此要花费大量的计算时间;反之,则可节约计算时间,但全局搜索性能可能受到影响。实际应用过程中,初始温度一般需要依据实验结果进行若干次调整。
  (2) 退火速度问题。
  模拟退火算法的全局搜索性能也与退火速度密切相关。一般来说,同一温度下的“充分”搜索(退火)是相当必要的,但这需要计算时间。实际应用中,要针对具体问题的性质和特征设置合理的退火平衡条件。
  (3) 温度管理问题。
  温度管理问题也是模拟退火算法难以处理的问题之一。实际应用中,由于必须考虑计算复杂度的切实可行性等问题,常采用如下所示的降温方式:
T(t+1)=k×T(t)
式中k为正的略小于1.00的常数,t为降温的次数。

模拟退火算法的主要思想
就函数最小值问题来说,模拟退火的主要思想是:在搜索区间(二维平面中)随机游走(即随机选择点),再以Metropolis抽样准则,使随机游走逐渐收敛于局部最优解。而温度即是Metropolis算法中的一个重要控制参数,可以认为这个参数的大小控制了随时过程向局部或全局最优解移动的快慢。
冷却参数表、领域结构和新解产生器、接受准则和随机数产生器(即Metropolis算法)一起构成算法的三大支柱。 
l 重点抽样与Metroplis算法: 
Metropolis是一种有效的重点抽样法,其算法为:系统从能量一个状态变化到另一个状态时,相应的能量从E1变化到E2,概率为p = exp[ - (E2- E1)/kT ]。如果E2 < E1,系统接收此状态,否则,以一个随机的概率接收此或丢弃此状态。这种经常一定次数的迭代,系统会逐渐趋于一引稳定的分布状态。
重点抽样时,新状态下如果向下则接受(局部最优),若向上(全局搜索),以一定机率接受。模拟退火方法从某个初始解出发,经过大量解的变换后,可以求得给定控制参数值时组合优化问题的相对最优解。然后减小控制参数T的值,重复执行Metropolis算法,就可以在控制参数T趋于零时,最终求得组合优化问题的整体最优解。控制参数的值必须缓慢衰减。
其中温度是一个Metropolis的重要控制参数,模拟退火可视为递减控制参数什时Metroplis算法的迭代。开始T值大,可能接受较差的恶化解,随着T的减小,只能接受较好的恶化解,最后在T趋于0时,就不再接受任何恶化解了。
在无限高温时,系统立即均匀分布,接受所有提出的变换。T的衰减越小,T到达终点的时间越长;但可使马可夫链越小,到达准平衡分布的时间越短, 
l 参数的选择: 
我们称调整模拟退火法的一系列重要参数为冷却进度表。它控制参数T的初值及其衰减函数,对应的MARKOV链长度和停止条件,非常重要。 
一个冷却进度表应当规定下述参数: 
1. 控制参数t的初值t0; 
2. 控制参数t的衰减函数; 
3. 马尔可夫链的长度Lk。(即每一次随机游走过程,要迭代多少次,才能趋于一个准平衡分布,即一个局部收敛解位置) 
4. 结束条件的选择 
有效的冷却进度表判据: 
一.算法的收敛:主要取决于衰减函数和马可夫链的长度及停止准则的选择 
二.算法的实验性能:最终解的质量和CPU的时间 
参数的选择: 
一)控制参数初值T0的选取 
一般要求初始值t0的值要充分大,即一开始即处于高温状态,且Metropolis的接收率约为1。 
二)衰减函数的选取  
衰减函数用于控制温度的退火速度,一个常用的函数为:T(n + 1) = K*T(n),其中K是一个非常接近于1的常数。 
三)马可夫链长度L的选取 
原则是:在衰减参数T的衰减函数已选定的前提下,L应选得在控制参数的每一取值上都能恢复准平衡。 
四)终止条件 
有很多种终止条件的选择,各种不同的条件对算法的性能和解的质量有很大影响,本文只介绍一个常用的终止条件。即上一个最优解与最新的一个最优解的之差小于某个容差,即可停止此次马尔可夫链的迭代。
使用模拟退火法求函数f(x,y) = 5sin(xy) + x2 + y2的最小值 
解:根据题意,我们设计冷却表进度表为: 
即初始温度为100 
衰减参数为0.95 
马可夫链长度为10000 
Metropolis的步长为0.02 
结束条件为根据上一个最优解与最新的一个最优解的之差小于某个容差。

 

 

 

售货员的难题

Time Limit:1000MS  Memory Limit:65536K
Total Submit:153 Accepted:10

Description

某乡有N个村庄(1<N<40),有一个售货员,他要到各个村庄去售货,各个村庄之间的路程S(0<S<10000)是已知的,且A村到B村与B村到A村的路大多不同.为了提高效率,他从商店出发到每个村庄一次,然后返回商店所在的村,假设商店所在的村庄为1,他不知道选择什么样的路线才能使所走过的路程最短.请你帮他选择一条最短的路.

Input

村庄N和各个村之间的路程(均是整数). 

Output

最短的路程. 

Sample Input

3
0 2 1
1 0 2
2 1 0

 

Sample Output

3 

 

 

CODE:

[cpp]  view plain copy
  1. /*模拟退火*/  
  2. /*AC代码:0ms*/  
  3. #include <iostream>  
  4. #include <ctime>  
  5. #include <cstdlib>  
  6. using namespace std;  
  7. const int MAX=41;  
  8. const int RANN=1000;  
  9. const int RUNN=50;  
  10. const int INF=99999999;  
  11. int map[MAX][MAX],rpath[RANN][MAX],min[RANN],N;  
  12. void adjust(int x[],int rn)//rn为调整次数  
  13. {  
  14.     int a,b;  
  15.     while(rn--)  
  16.     {  
  17.         a=rand()%(N-1)+1;//调整的位置  
  18.         b=rand()%(N-1)+1;  
  19.         swap(x[a],x[b]);  
  20.     }  
  21. }  
  22. void get_map()  
  23. {  
  24.     int i,j;  
  25.     for(i=1;i<=N;i++)  
  26.         for(j=1;j<=N;j++)  
  27.             scanf("%d",&map[i][j]);  
  28. }  
  29. void get_rpath()//产生RANN组初始数列  
  30. {  
  31.     int i,j;  
  32.     for(i=0;i<RANN;i++)  
  33.     {  
  34.         for(j=1;j<=N-1;j++)  
  35.             rpath[i][j]=j+1;  
  36.         adjust(rpath[i],N-1);//初始调整  
  37.         min[i]=INF;  
  38.     }  
  39. }  
  40.   
  41. void swap(int &x,int &y)//交换x,y  
  42. {  
  43.     int t=x;  
  44.     x=y;  
  45.     y=t;  
  46. }  
  47. int get_dis(int x[])//返回路径长度  
  48. {  
  49.     int sum=0,i,p=1;  
  50.     for(i=1;i<=N-1;i++)  
  51.     {  
  52.         sum+=map[p][x[i]];  
  53.         p=x[i];  
  54.     }  
  55.     sum+=map[p][1];  
  56.     return sum;  
  57. }  
  58. void numcpy(int x[],int y[])  
  59. {  
  60.     for(int i=1;i<=N-1;i++)  
  61.         x[i]=y[i];   
  62. }  
  63. void get_ans()  
  64. {  
  65.     int i,j,t=N-1,temp,p[41];  
  66.     while(t--)//调整的范围递减  
  67.     {  
  68.         for(i=0;i<RANN;i++)//遍历RanN组数据  
  69.         {  
  70.             for(j=0;j<RUNN;j++)//对于每组做RunN次  
  71.             {  
  72.                 numcpy(p,rpath[i]);//因为每次都在原有的rpath[i]上改变  
  73.                 adjust(p,t);  
  74.                 temp=get_dis(p);  
  75.                 if(temp<min[i])  
  76.                 {  
  77.                     numcpy(rpath[i],p);//更新rpath[i];  
  78.                     min[i]=temp;  
  79.                 }  
  80.             }  
  81.         }  
  82.     }  
  83.     int ans=min[0];  
  84.     for(i=1;i<RANN;i++)  
  85.         if(min[i]<ans)  
  86.             ans=min[i];  
  87.         printf("%d/n",ans);  
  88. }  
  89. int main ()  
  90. {  
  91.     srand(time(0));//以时间为随机数种子产生随机数  
  92.     while(scanf("%d",&N)!=EOF)  
  93.     {  
  94.         get_map();  
  95.         get_rpath();  
  96.         get_ans();  
  97.     }  
  98.     return 0;  
  99. }  
 

POJ--1379[Run Away] 模拟退火

分类: POJ 解题报告   353人阅读  评论(0)  收藏  举报
 

算法:模拟退火http://www.cppblog.com/RyanWang/archive/2010/05/05/106112.html

 
 
 
CODE:
[cpp]  view plain copy
  1. #include <iostream>  
  2. #include <cmath>  
  3. #include <cstdlib>  
  4. #include <ctime>  
  5. const int RanN=30;  
  6. const int RunN=30;  
  7. const double P=0.6;  
  8. int X,Y,M;  
  9. struct px  
  10. {  
  11.     double x,y,d;  
  12. };  
  13. struct px stu[1001];  
  14. struct px S[RanN+1];  
  15. int Max(int x,int y)  
  16. {  
  17.     return x>y?x:y;  
  18. }  
  19. double get_dis(px t)  
  20. {  
  21.     double sum=1e10,temp;  
  22.     int i;  
  23.     for(i=1;i<=M;i++)  
  24.     {  
  25.         temp=sqrt((double)((t.x-stu[i].x)*(t.x-stu[i].x)+(t.y-stu[i].y)*(t.y-stu[i].y)));  
  26.         if(sum>temp)  
  27.             sum=temp;  
  28.     }  
  29.     return sum;  
  30. }  
  31. void get_ans()  
  32. {  
  33.     int i,r;  
  34.     double delta,epsilon=1e-2,k1,k2,max;  
  35.     px temp;  
  36.     delta=(double)Max(X,Y)/(sqrt(1.0*M));  
  37.     for(r=1;r<=RanN;r++)  
  38.     {  
  39.         S[r].x=double(rand()%1000+1)/1000.000*X;  
  40.         S[r].y=double(rand()%1000+1)/1000.000*Y;  
  41.         S[r].d=get_dis(S[r]);  
  42.     }  
  43.     while(delta>epsilon)  
  44.     {  
  45.         for(r=1;r<=RanN;r++)  
  46.         {  
  47.             for(i=1;i<=RunN;i++)  
  48.             {  
  49.                 k1=double(rand()%1000+1)/1000.000*delta;//定长度  
  50.                 k2=sqrt(delta*delta-k1*k1);//k1,k2的平方和开根号是delta  
  51.                 if (rand()%2==1) k1*=-1;//定方向  
  52.                 if (rand()%2==1) k2*=-1;  
  53.                 temp.x=S[r].x+k1;  
  54.                 temp.y=S[r].y+k2;  
  55.                 if(temp.x<=X&&temp.x>=0&&temp.y<=Y&&temp.y>=0)//在界内才处理  
  56.                 {  
  57.                     max=get_dis(temp);  
  58.                     if(max>S[r].d)//直接更新该点  
  59.                     {S[r].d=max;S[r].x=temp.x;S[r].y=temp.y;}  
  60.                 }  
  61.             }  
  62.         }  
  63.         delta*=P;  
  64.     }  
  65.     max=0;  
  66.     px ans;  
  67.     for(i=1;i<=RanN;i++)  
  68.     {  
  69.         if(S[i].d>max)  
  70.         {max=S[i].d;ans=S[i];}  
  71.     }  
  72.     printf("The safest point is (%.1lf, %.1lf)./n",ans.x,ans.y);  
  73. }  
  74. int main ()  
  75. {  
  76.     int T,i;  
  77.     scanf("%d",&T);  
  78.     //srand((unsigned)time(NULL));   
  79.     while(T--)  
  80.     {  
  81.         scanf("%d%d%d",&X,&Y,&M);  
  82.         for(i=1;i<=M;i++)  
  83.         {  
  84.             scanf("%lf%lf",&stu[i].x,&stu[i].y);  
  85.             //if(stu[i].x<0||stu[i].x>X||stu[i].y<0||stu[i].y>Y)//除去界外点  
  86.             //{i--;M--;}  
  87.         }  
  88.         get_ans();  
  89.     }  
  90.     return 0;  
  91. }  
 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值