模拟退火算法(Simulate Anneal,SA)是一种通用概率演算法,用来在一个大的搜寻空间内找寻命题的最优解。模拟退火是由S.Kirkpatrick, C.D.Gelatt和M.P.Vecchi在1983年所发明的。V.Černý在1985年也独立发明此演算法。模拟退火算法是解决TSP问题的有效方法之一。
模拟退火的出发点是基于物理中固体物质的退火过程与一般组合优化问题之间的相似性。模拟退火算法是一种通用的优化算法,其物理退火过程由加温过程、等温过程、冷却过程这三部分组成。
——百度百科
简单说,模拟退火是一种随机化算法。当一个问题的方案数量极大(甚至是无穷的)而且不是一个单峰函数时,我们常使用模拟退火求解。
模拟退火的原理也和金属退火的原理近似:将热力学的理论套用到统计学上,将搜寻空间内每一点想像成空气内的分子;分子的能量,就是它本身的动能;而搜寻空间内的每一点,也像空气分子一样带有“能量”,以表示该点对命题的合适程度。演算法先以搜寻空间内一个任意点作起始:每一步先选择一个“邻居”,然后再计算从现有位置到达“邻居”的概率。
那么对应到OI上,就是每次随机出一个新解,如果这个解更优,则接受它,否则用与(现在的解与最优解的差)相关的概率(这个概率下面会提到)接受它。
如上图:随着温度的逐渐降低,随机出的解的范围逐渐减小,答案便逐渐固定下来;
之前提到的概率是根据物理学中的概率设定的,所以最好不要该这里的概率;设对于这次的解是tmpans,之前的最优答案是ans,目前的温度是T,k是一个随机数,那么设个概率KSM(e,fabs(tmpans-ans)/T/k);
在降温之前,先设定初始温度T0,终止温度Tn,降温系数w;
对于每次降温,T=Tpre*w;
程序开始时,我们要先srand(一个常数)
。这个常数可以决定分数。你可以使用233333,2147483647,甚至某个八位质数。
一遍SA往往无法跑出最优解,所以可以多跑几遍。
可以用一个全局变量记录所有跑过的SA的最优解,每次从那个最优解开始继续SA,可以减小误差。
一般降温系数w与的差减少一个数量级,耗时大约10多;
T=100.0; //初始温度 for(int i=0;i<100;i++) //控制迭代次数 { tar=getPos(); //在x的周围选一个点 E=f(tar)-f(x); if(E > eps) x=tar; //直接移动 else if(exp(E/T) > random(0,1)) x=tar; //接受移动 T=T*0.99; //降温 }
对于SA的玄学调参,我们有一下几种方案:
调大w,调大T0,调小Tn,多跑几遍SA,切换种子;
生成新解的方法:
- 坐标系内:随机生成一个点,或者生成一个向量。
- 序列问题:使用random_shuffle或者随机交换两个数。
- 网格问题:可以看做二维序列,每次交换两个格子即可。
模拟退火的思想在于,如果一个移动会使答案变得更优,我们就接受这个移动;否则我们以一定的概率接受这个移动。