智能算法(一)——模拟退火,随机化下的贪心
前言
贪心,是一个很好的算法,只可惜适用的范围不广。随机化,是一个很好的算法,只可惜正确率不高。
如果将这两个算法结合起来,我们能不能得到一个适用范围广、正确率较高的算法呢?
答案是肯定的。
模拟退火(\(Simulated Annealing\),简称\(SA\)),一个随机化与贪心结合的算法,就可以轻松解决许多难题(前提是你的RP较好或是数据范围较小)。
一个经典的故事
为了找出地球上最高的山,一群有志气的兔子们开始想办法。
- 兔子朝着比现在高的地方跳去。它们找到了不远处的最高山峰。但是这座山不一定是珠穆朗玛峰。这就是爬山算法,它不能保证局部最优值就是全局最优值。
- 兔子喝醉了。他随机地跳了很长时间。这期间,它可能走向高处,也可能踏入平地。但是,它渐渐清醒了并朝最高方向跳去。这就是模拟退火。
- 兔子们知道一个兔的力量是渺小的。它们互相转告着,哪里的山已经找过,并且找过的每一座山他们都留下一只兔子做记号。它们制定了下一步去哪里寻找的策略。这就是禁忌搜索。
- 兔子们吃了失忆药片,并被发射到太空,然后随机落到了地球上的某些地方。它们不知道自己的使命是什么。但是,如果你过几年就杀死一部分海拔低的兔子,多产的兔子们自己就会找到珠穆朗玛峰。这就是遗传算法。
其中第二部分,讲的就是模拟退火。
原理
类似于物理学上金属退火的过程,故称模拟退火。
主要思路
我们可以一起来归纳一下模拟退火的主要思路。
- 首先,我们要先设置一个初始的温度\(T\)(遵循物理学定律),至于这个初始温度选什么好,我也不太清楚,据说一些大佬可以根据题目推出最优的初始温度。
- 确定完初温,我们就可以开始操作了。
- 对于每一个能够从当前状态转移到的下一个状态,我们可以将其与原本的答案进行比较:
- 如果\(res<new\) _ \(res\),则我们必定转移答案。
- 否则,我们有一定概率转移答案(转移答案的概率随时间的推移和\(res\)与\(new\) _ \(res\)差值的增大而减小),因此我们可以表示为\(\frac1{exp((res-new\_res)/T)}\)。
- 最后,在一次答案转移结束后,我们要记得将\(T\)乘上一个小于但十分接近于\(1\)的数\(delta\),以体现时间对答案的影响。
根据这个主要思路,我们就可以写出模拟退火的模板了:
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。
这道题目的正解我也不知道是什么(据说是暴搜?),反正它已经成为了模拟退火的一道经典题。
题目的大意就是要你求出一张图的广义费马点,具体解析见博客。
待到再迷茫时回头望,所有脚印会发出光芒