模拟退火,随机化下的贪心

前言

贪心,是一个很好的算法,只可惜适用的范围不广。随机化,是一个很好的算法,只可惜正确率不高
如果将这两个算法结合起来,我们能不能得到一个适用范围广、正确率较高的算法呢?
答案是肯定的。
模拟退火(Simulated Annealing,简称SA),一个随机化贪心结合的算法,就可以轻松解决许多难题(前提是你的RP较好或是数据范围较小)。

一个经典的故事

为了找出地球上最高的山,一羣有志气的兔子们开始想办法。

  • 兔子朝着比现在高的地方跳去。它们找到了不远处的最高山峯。但是这座山不一定是珠穆朗玛峯。这就是爬山算法,它不能保证局部最优值就是全局最优值。
  • 兔子喝醉了。他随机地跳了很长时间。这期间,它可能走向高处,也可能踏入平地。但是,它渐渐清醒了并朝最高方向跳去。这就是模拟退火
  • 兔子们知道一个兔的力量是渺小的。它们互相转告着,哪里的山已经找过,并且找过的每一座山他们都留下一只兔子做记号。它们制定了下一步去哪里寻找的策略。这就是禁忌搜索
  • 兔子们吃了失忆药片,并被发射到太空,然后随机落到了地球上的某些地方。它们不知道自己的使命是什么。但是,如果你过几年就杀死一部分海拔低的兔子,多产的兔子们自己就会找到珠穆朗玛峯。这就是遗传算法

其中第二部分,讲的就是模拟退火

主要思路

我们可以一起来归纳一下模拟退火的主要思路。

  • 首先,我们要先设置一个初始的温度 T T (遵循物理学定律),至于这个初始温度选什么好,我也不太清楚,据说一些大佬可以根据题目推出最优的初始温度。
  • 确定完初温,我们就可以开始操作了。
  • 对于每一个能够从当前状态转移到的下一个状态,我们可以将其与原本的答案进行比较:
    • 如果 res<new r e s < n e w _ res r e s ,则我们必定转移答案。
    • 否则,我们有一定概率转移答案(转移答案的概率随时间的推移 res r e s new n e w _ res r e s 差值的增大而增大),因此我们可以表示为 exp((resnew e x p ( ( r e s − n e w _ res)/T) r e s ) / T )
  • 最后,在一次答案转移结束后,我们要记得将 T T 乘上一个小于但十分接近于 1 1 的数 delta d e l t a ,以体现时间对答案的影响。

根据这个主要思路,我们就可以写出模拟退火的模板了:

int T=***;//给T赋一个初温(我也不知道该赋什么值)
while(T>eps)//eps是一个大于0的极小值,一般取1e-15
{
    int nxt=Next(now),new_res=Calc(nxt);//nxt记录下一个状态,new_res记录下一个状态能得到的答案
    if(res<new_res||exp((res-new_res)/T)*RAND_MAX>rand()) now=nxt,res=new_res;//如果新的代价小于当前代价,或在一定的机率下,更新当前状态
    T*=delta;//降温的过程,将温度减小,是一个模拟物理学上的退火操作的过程
}

例题

最后,我们一起来看一道例题来感受一下模拟退火的神奇所在:【洛谷1337】[JSOI2004]吊打XXX
这道题目的正解我也不知道是什么(据说是暴搜?),反正它已经成为了模拟退火的一道经典题。
题目的大意就是要你求出一张图的广义费马点,具体解析见博客。

点赞