網上的絕大部分部落格,包括洛谷題解原來都是錯的寫法……具體看轉移部分qwq
終於成為了少數派,這是好的
模擬退火演算法(simulate anneal,sa)是一種通用概率演演算法,用來在乙個大的搜尋空間內找尋命題的最優解。
模擬退火是由s.kirkpatrick, c.d.gelatt和m.p.vecchi在2023年所發明的。v.černý在2023年也獨立發明此演演算法。模擬退火演算法是解決tsp問題的有效方法之一。
模擬退火的出發點是基於物理中固體物質的退火過程與一般組合優化問題之間的相似性。
模擬退火演算法是一種通用的優化演算法,其物理退火過程由加溫過程、等溫過程、冷卻過程這三部分組成。
簡單說,模擬退火是一種隨機化演算法。當乙個問題的方案數量極大(甚至無窮)而且不是單峰函式時,常使用模擬退火求解
我其實一直覺得模擬退火挺玄學的。(後來發現還真就靠rp吃飯
每次隨機出乙個新解,如果這個解更優,則接受它,否則以乙個與溫度和與當前最優解的差相關的概率接受它。
設這個新的解與最優解的差為 \(\delta e\),溫度為 \(t\) , \(k\) 為乙個隨機數,那麼這個概率為 \(e^}\)
具體來說:設 \(p\) 為接受當前這個解的概率,那麼
若 \(\delta e >0\) ,那麼 \(p=1\) ,也就是必定接受更優解;
若 \(\delta e <0\),那麼 $p=exp( -\frac ) $,也就是以乙個和差相關的概率接受這個不優的解。
對第二種情況的具體做法將在後面進行詳細解釋。
所需引數: 初始溫度 \(tb\),降溫的係數 \(d\),最終溫度 \(te\)
(注:一般來講 \(tb\) 會比較大,\(d\) 是乙個小於 \(1\) 且接近 \(1\) 的 \(double\) ,比如 \(0.97\) , \(te\) 是乙個接近 \(0\) 的正值,如 \(1e-10\) ,這些都是程式中自定的常數,但是在實際情況中這些引數需要進行不斷調優,也就是所謂的調參。這或許也是模擬退火很難應用到 oi 賽制中的原因,畢竟不能實時檢視結果的話就真的很靠 rp ,當然調參也是有跡可循的,比如 \(tle\) 的時候可以考慮把初溫調小,\(wa\) 的時候調大)
流程如下:
令 \(t=tb\)
進行轉移
\(t*=d\)
重複步驟 \(2、3\) 直到 \(t
設估價函式為 \(f(x)\) (其實就是對你隨機出來的方案計算答案的乙個函式),\(x\)為原先得到的解,\(x1=x+t0*rand()\) 為新得到的隨機出來的解( \(-1<=r<=1\),且\(r!=0\) ),\(\delta e =f(x1)-
f(x)\)
雖然我覺得到這裡還是一頭霧水,但是看到**應該就比較清楚
放個偽**
if ( dele>0 ) 更新解;
else if ( exp(dele/sqrt(t))>(double)rand()/1215 ) 更新解;
然後發現乙個奇怪的東西:if ( exp(dele/sqrt(t))>(double)rand()/1215 )
這是幹嘛用的?
exp(dele/sqrt(t))
,由於前面的if
語句,dele/sqrt(t)
顯然小於 \(0\) ,\(exp\) 之後也就是乙個 \(0\sim 1\) 的小數。下面來分析為什麼這句話能達到 「以和溫度、最優解相關的概率接受這個不優的解」。
根據簡單的數學知識我們知道,dele/sqrt(t)
越大,\(exp\) 之後的結果越大。而dele/sqrt(t)
的結果,當dele
較大或者t
較大的時候都會變大,也就是說,當退火過程剛剛開始,或者當前結果和目前的最優解差距較小時,exp(dele/sqrt(t))
越大,也就更容易使>rand()/1215
成立,這個解更容易被接受,反之亦然。
注意 :if(exp(-del / ts) < (double)rand() / rand_max)
這樣的寫法是錯誤的!顯然exp(-del/ts)
大於 \(1\) ,而rand()/rand_max
小於 \(1\) ,一定不會成立。但是不知道為什麼就是能過去,反正是錯的 感謝 @cjlworld 指出這個寫法的問題。
隨機的初始化真的很重要
調參這個東西有時候很考經驗
其實模擬退火可以解決基本所有最優解問題 (所以快去,用sa全做一遍
有時候為了求得最優解,需要多次sa,這裡有個不tle的好辦法:
while ( (double)clock()/clocks_per_sec<=0.8 ) sa();
\(0.8\) 根據時限而定,一般比時限小一點點
luogu p5544 炸彈攻擊1
//author:ringweeh
#include #include #include #include #include #define ll long long
#define db double
using namespace std;
int min( int a,int b )
void bmin( int &a,int b )
int read()
while ( ch<='9' && ch>='0' )
return x*w;
}const int n=11,m=1010;
const double delta=0.996,te=1e-10; //d和最終溫度
int n,m,x[n],y[n],r[n],p[m],q[m],r,ansout=0;
double ansx,ansy;
double dis( double ax,double ay,double bx,double by ) //求兩點間距離
int calc( double xx,double yy ) //估價函式
void sa()}
int main()
uva10228-a star not a tree?
給定乙個 \(n\) 邊形所有頂點座標 \(x,y\) ,求其費馬點到所有頂點距離和。費馬點是指到多邊形所有頂點距離和最小的點
solution
也是比較板子的題,但是注意多組資料,計時的clock
是總時間。
如果用while ( (double)clock()/clocks_per_sec<=0.8 )
,這樣的話就會出問題。所以要改成固定次數。
//author:ringweeh
const int n=110;
const double delta=0.998,te=1e-10;
int n,m,x[n],y[n];
double ansx,ansy;
double dis( double ax,double ay,double bx,double by )
double calc( double xx,double yy )
void sa()
}int main()
{ srand(time(0)); int case=read();
for ( int cas=0; cas蒟蒻初學sa,若有誤希望指正qwq
模擬退火學習筆記
博主這個暴力騙分選手get到了 人生的本質 有一類函式,我們要求其的最低點 最高點 二分?三分?四五六七 分?哦涼涼了 先介紹乙個爬山 我們隨機撒點,然後讓這些點去做類似現實的爬山 即右邊優就去右邊,左邊優就去左邊 直到沒有比他更優秀的了!也就是相當於到山峰了吧 那麼這個貪心的演算法顯然非常容易卡在...
學習筆記 模擬退火
一談到模擬退火,大家都知道是玄學演算法,但是他是如何 a 題的呢?模擬退火,即模擬金屬退火這一過程,來實現最優解的尋找。金屬退火,對於我們似乎很遙遠了,那我們舉個實際點的例子吧。學過化學的都知道,在蒸發結晶時,我們會在蒸發皿還有部分溶劑時停止加熱,用餘熱蒸乾剩餘液體。這就是乙個退火過程,它的實質就是...
學習筆記 模擬退火
什麼是模擬退火?選自 oi wiki 模擬退火是一種隨機化演算法。當乙個問題的方案數量極大 甚至是無窮的 而且不是乙個單峰函式時,我們常使用模擬退火求解。模擬退火,顧名思義,是模擬 退火 的過程。當我們使用爬山演算法的時候,對於非單峰函式的情形容易陷入次優解。爬山演算法省略了最優解附近的非最優解從而...