前置技能——登山:https://www.cnblogs.com/AKMer/p/9555215.htmlhtml
模拟退火是一种很是好的随机化算法,是登山算法的改进版,它的灵感来源于金属冶炼退火,和遗传算法同样,也是一种大天然馈赠给咱们的自适应随机化算法。算法
有兴趣的同窗能够去看看遗传算法:https://www.cnblogs.com/AKMer/p/9479890.html函数
关于模拟退火,牵扯到物理学的一些知识,有兴趣的同窗们能够去看百度百科。spa
百度百科:https://baike.baidu.com/item/%E6%A8%A1%E6%8B%9F%E9%80%80%E7%81%AB/8664695?fr=aladdincode
$T$:初始温度。htm
$T_0$:结束温度。blog
$\Delta T$:温度变化系数,通常在$[0.950,0.999]$之间,每次降温温度都乘以$\Delta T$。get
$calc(x)$:状态$x$所对应的权值。在讲登山算法的时候咱们就提到过了,模拟退火也将状态空间里每个状态映射成一个个点,而状态的优秀度就是一个个点对应的函数值。博客
温度通常为double类型,由高温逐渐降到低温。it
登山算法是一种鼠目寸光的贪心,只会往当前优秀的邻居状态转移。咱们只能经过增长登山的人数来覆盖尽可能多的状态空间以保证算法效率。
然而模拟退火却不同,对于一个比当前状态更加优秀的状态,咱们能够坚决果断地转移过去。
可是若是该邻居状态不比当前状态优秀,咱们也不会一棒子打死,而是根据物理学上一些知识来计算转移几率。
而后在信息学领域内,咱们只要记住一件事情就行了。那就是:
在温度为$T$时,当前状态$A$转移到一个不优于它的状态$B$的几率是$exp(\frac{calc(B)-calc(A)}{T})$
别问我为啥去问物理学家去……总之按这个几率去转移能够大大增长遍历到表明正解的状态的概率。
根据题意咱们有时须要将$calc(B)-calc(A)$改为$calc(A)-calc(B)$,由于模拟退火算法须要知足一条性质,那就是“在温度不一样的状况下,温度越低时发生同一个转移的几率就越低”,也就是当$calc(B)-calc(A)$为定值的时候,$T$越小几率也越小,因此分子要保证是负数。
显然,这个几率是属于区间$[0,1)$的。
记得找邻居节点的时候灵性一点,不要死死板板找挨在一块儿的,否则我前脚往一个差一点的状态走了后脚就走回来了。还有就是更改状态能够借鉴遗传里的变异。而后其他的跟登山就没啥区别了。
说实话,会了退火以后感受登山就没什么卵用了……
#include <ctime> #include <cstdio> #include <algorithm> using namespace std; #define ll long long const double T_0=1e-5; const double del_T=0.998; double T=1e5; ll f(int x) { //返回函数值 } int main() { srand(time(0)); int pos=rand()-RAND_MAX/2;ll ans=f(pos); while(T>T_0) { int x=pos+T,y=pos-T,nxt;//所谓的灵性找邻居法 if(f(x)>f(y))nxt=x; else nxt=y;//找下一个点 if(f(nxt)>f(pos)||exp((f(nxt)-f(pos))/T)*RAND_MAX>rand()) pos=nxt;//若是下一个点比当前点高或者在必定的几率以内咱们就转移 ans=max(ans,f(pos)) T*=del_T } printf("%lld\n",ans); return 0; }//没有什么是退一万次火解决不了的。若是有,那就再退一万次。
注意:上面这个乱跳的代码没有啥正确性可言,只是意思意思一下而已,不要当真。
关于登山和模拟退火,网上流传着这么两句比喻:
登山算法:兔子朝着比如今高的地方跳去。它找到了不远处的最高山峰。可是这座山不必定是珠穆朗玛峰。这就是登山算法,它不能保证局部最优值就是全局最优值。
模拟退火:兔子喝醉了。它随机地跳了很长时间。这期间,它可能走向高处,也可能踏入平地。可是,它渐渐清醒了并朝最高方向跳去。这就是模拟退火。
不知道你在阅读了个人两篇博客以后,有没有这种感觉。若是没有,那……
确定是我太菜写的太渣了。