一談到模擬退火,大家都知道是玄學演算法,但是他是如何\(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 模擬退火是一種隨機化演算法。當乙個問題的方案數量極大 甚至是無窮的 而且不是乙個單峰函式時,我們常使用模擬退火求解。模擬退火,顧名思義,是模擬 退火 的過程。當我們使用爬山演算法的時候,對於非單峰函式的情形容易陷入次優解。爬山演算法省略了最優解附近的非最優解從而...