前置技能——登山: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; }//沒有什麼是退一萬次火解決不了的。若是有,那就再退一萬次。
注意:上面這個亂跳的代碼沒有啥正確性可言,只是意思意思一下而已,不要當真。
關於登山和模擬退火,網上流傳着這麼兩句比喻:
登山算法:兔子朝着比如今高的地方跳去。它找到了不遠處的最高山峯。可是這座山不必定是珠穆朗瑪峯。這就是登山算法,它不能保證局部最優值就是全局最優值。
模擬退火:兔子喝醉了。它隨機地跳了很長時間。這期間,它可能走向高處,也可能踏入平地。可是,它漸漸清醒了並朝最高方向跳去。這就是模擬退火。
不知道你在閱讀了個人兩篇博客以後,有沒有這種感覺。若是沒有,那……
確定是我太菜寫的太渣了。