學習筆記 模擬退火

2022-04-30 01:36:12 字數 4072 閱讀 9866

一談到模擬退火,大家都知道是玄學演算法,但是他是如何\(a\)題的呢?

模擬退火,即模擬金屬退火這一過程,來實現最優解的尋找。

金屬退火,對於我們似乎很遙遠了,那我們舉個實際點的例子吧。

學過化學的都知道,在蒸發結晶時,我們會在蒸發皿還有部分溶劑時停止加熱,用餘熱蒸乾剩餘液體。

這就是乙個退火過程,它的實質就是降溫

這樣的好處是,在達到目的的前提下,儘量減少了能源(熱量)消耗。

這是乙個雙贏過程。

模擬退火是一種通用的演算法,特別是對於最優解問題。

所以所有最優解問題都可以用模擬退火,它是一種實用性較高的演算法。

對於每個最優解問題,只要套上模擬退火,即使不能\(ac\),也能得到乙個可觀的的分數。

還記得三分法這個題嗎?

我們如果不能保證在\([l,r]\)內只有乙個峰值,該如何尋找最值呢?

考慮乙個無腦的解法,我們維護乙個掃瞄線,按精度從一邊向另一邊掃,找到峰值後就貪心的認為是最優解。

不過這樣顯然是錯的

最優解可能要先翻過這座山。

考慮全掃一遍,這樣的複雜度**。

那我們以一定的概率向前掃,當然是在儲存現有最優解的前提下。

當然,這樣還會被卡,如果峰值很遠,那就會\(tle\)。

既然已經很玄學了,那就更玄學一點吧:

我們考慮直接隨機生成點,進行測試。

似乎正確性提高了不少

偷偷地告訴你,這就是模擬退火的一般過程。

就像演算法的名字一樣,直接模擬就好了。

我們形象的設引數:

\(t\)代表退火的初始溫度,\(\mathrm\delta(0

t*=delta;
這些引數在隨機數生成時起著至關重要的作用,一般來說有兩個:

\(1\).生成變數:

我們在退火的過程中要記錄乙個基準值,即退火的標準。

我們假設要生成乙個變數\(x\),那麼就要在基準數\(x\)的基礎上加上乙個隨機的值,通常是這樣實現的:

x=x+((rand()<<1)-rand_max)*t;
慢慢來說,此處的rand_max是指隨機數的值域,即rand()\(\in[0,rand\_max]\),rand_max大概是32768左右,那麼前面那一大坨的值域就成了

\[[-rand\_max,rand\_max]

\]\(2\).退火標準的確定(即上文中的\(x\)):

考慮貪心。

如果我們找到乙個可以碾壓現有最優解的值,我們就貪心的認為:可能有更優解在此解周圍,我們就希望以這個點基準進行隨機找點,把基準值設成他。

如果並無法碾壓暫時性的最優解呢?

那我們不能完全拋棄(如果完全拋棄就淪落成無腦貪心了),只以乙個概率接受此基準,但並不改變最優解(當然要儲存最優解了)。

不知是哪位大神提出了這個概率的最佳值:

\[e^}

\](公式好醜啊......

這裡的\(\mathrm\delta x\)指現在解與現有最優解的差值,我們這裡保證\(\mathrm\delta x<0\),\(t\)指現在的溫度。

用程式實現如下:

if(exp((now-ans)/t)*rand_max>rand()) x=x;
\(ps\):\(\exp x\)指\(e^x\)。

考慮一下為什麼這樣實現。

應該都可以理解的。

廢話,那麼多隨機數怎麼會不玄學

我們在這裡討論如何使其正確性提高。

\(1.\)卡時間

我們可以多跑幾次模擬退火演算法,來提高正確性。

就是這樣(時限\(1s\)):

while((double)clock()/clocks_per_sec<0.9)sa();

//模擬退火演算法英文簡稱sa。

//上面那一句將時間的單位由計算機時間單位轉化為秒

\(2\).調參

模擬退火最刺激的就是調參了

一般要調的引數\(t,\mathrm\delta,t_0,srand()\)值。

多隨機幾次,比如:

srand(19260817),srand(rand()),srand(rand());
隨機引數好玩

有rp作保障

這時候我們就要注意退火引數的實際意義了。

觀察:\(1\).生成實驗變數:

就是這一句:

x=x+((rand()<<1)-rand_max)*t;

//注意位運算的優先順序,括號不要忘打。

我們發現隨著退火,隨機量變動變小,準確的說,我們大範圍隨機了多次,認為越來越接近最優解,效果是這樣的:

很形象吧。

\(2.\)接受概率:

if(exp((now-ans)/t)*rand_max>rand()) x=x;
顯然\(t\)越小,\(\dfrac\)越小(注意分子是負值),我們接受的概率就越小,還是貪心,溫度越低,越接近最優解。

這就是它的神奇之處。

我們首先考慮一次\(sa\)的複雜度。

顯然有方程:

\[t×\mathrm\delta^x=t_0

\]易得:

\[x=\log_\dfrac

\]我們設驗證解的時間複雜度是\(\mathcal t(n)\),那麼一次模擬退火的時間複雜度是:

\[\mathcal o(\mathcal t(n)\log_\dfrac)

\]如果像上面那樣卡時限:

while((double)clock()/clocks_per_sec<0.9)sa();
時間複雜度就是\(\mathcal o(\text)\)好了。

#include#include#include#include#includeusing namespace std;

int n;

double fuc[20],l,r;

double ansx,ans=-1e10;

double f(double g,int n)

void sa()

return;

}void work()

//注意時限只有100ms

int main()

模擬退火奶一口。

根據乙個神奇的能量最小原理-->戳我看百科

我們只需計算系統內的能量合即可,即確定繩結點座標\((x_0,y_0)\),最小化:

\[\sum\limits_^n\sqrt×m_i

\]分析:

隨機化座標,模擬退火即可:

#include#include#include#include#includeusing namespace std;

#define maxn 1005

double ansx,ansy,ans=1e18;

int n;

double xx[maxn],yy[maxn],m[maxn];

double sumx=0,sumy=0;

double check(double x,double y)

void sa()

return;

}void work()

int main()

你以為沒了?

確實沒了

只是博主僅僅寫了這兩道題而已,我們還是那樣說:

模擬退火是一種通用的演算法,特別是對於最優解問題。

雖然模擬退火能解決大多數最優解問題,不過在一些情況下,不能指望用模擬退火\(ac\)。

\(1\).檢查一次的時間複雜度過大。

這時無法多次退火以達到最優解,直接自閉。

\(2\).函式模型存在數論函式

數論函式是散點形的函式,我們就不能貪心的認為更優解存在於最優解旁。

這樣可以拿一些分,不過\(ac\)希望渺茫。

模擬退火學習筆記

博主這個暴力騙分選手get到了 人生的本質 有一類函式,我們要求其的最低點 最高點 二分?三分?四五六七 分?哦涼涼了 先介紹乙個爬山 我們隨機撒點,然後讓這些點去做類似現實的爬山 即右邊優就去右邊,左邊優就去左邊 直到沒有比他更優秀的了!也就是相當於到山峰了吧 那麼這個貪心的演算法顯然非常容易卡在...

模擬退火學習筆記

網上的絕大部分部落格,包括洛谷題解原來都是錯的寫法 具體看轉移部分qwq 終於成為了少數派,這是好的 模擬退火演算法 simulate anneal,sa 是一種通用概率演演算法,用來在乙個大的搜尋空間內找尋命題的最優解。模擬退火是由s.kirkpatrick,c.d.gelatt和m.p.vecc...

學習筆記 模擬退火

什麼是模擬退火?選自 oi wiki 模擬退火是一種隨機化演算法。當乙個問題的方案數量極大 甚至是無窮的 而且不是乙個單峰函式時,我們常使用模擬退火求解。模擬退火,顧名思義,是模擬 退火 的過程。當我們使用爬山演算法的時候,對於非單峰函式的情形容易陷入次優解。爬山演算法省略了最優解附近的非最優解從而...