【bzoj3680】平衡點 模擬退火
阿新 • • 發佈:2018-10-14
ret += can std line i++ tro oid pri
模擬退火是一種求函數最值問題的隨機算法。
給定一個函數的某一初始坐標,可以擬定一個“溫度”(這裏主要是借用退火的物理意義),這裏的溫度可以理解成自變量可以取值的範圍。之後在當前最優解對應的自變量的基礎上,隨機產生一組附加量,用當前自變量加上附加量構成一個新的點,計算該點的函數值,若該點函數值比最優解還要優,則接受該情況,同時最優解和其對應的自變量坐標也跟著改變;若該點函數值沒有最優解優,則以一定概率接受它,避免無法跳出函數極值點而非最值點,這個接受的概率表示為\(e^{(+-)\Delta},\Delta=f_1-f_2\),若這個值大於隨機生成的概率,則接受並更新這個值,否則不接受。如此往復叠代,當最優解出現的區間範圍足夠小時(T溫度降到接近0時),結束叠代,得出的值就可能是最優解。
(粗體為該算法用到概率的地方)
代碼如下
#include <bits/stdc++.h> using namespace std; const int maxn=1010; const double eps=1e-15;//精度 const double d=0.998;//降溫幅度 struct node{ double x,y,w; }p[maxn]; int n; double ansx,ansy,t; inline double calc(double x,double y){ double tot=0; for(int i=1;i<=n;i++){ double dx=x-p[i].x,dy=y-p[i].y; tot+=sqrt(dx*dx+dy*dy)*p[i].w; } return tot; } void SA(){ double T=200; while(T>eps){ double nowx=ansx+(2*rand()-RAND_MAX)*T;//隨機生成新坐標 double nowy=ansy+(2*rand()-RAND_MAX)*T; double delta=calc(nowx,nowy)-calc(ansx,ansy); if(delta<0)ansx=nowx,ansy=nowy; else if(exp(-delta/t)*RAND_MAX>rand())ansx=nowx,ansy=nowy;//以一定概率接受 T*=d; } } int main(){ srand((unsigned int)time(0)); scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%lf%lf%lf",&p[i].x,&p[i].y,&p[i].w); ansx+=p[i].x,ansy+=p[i].y; } ansx/=(double)n,ansy/=(double)n; SA(); printf("%.3lf %.3lf\n",ansx,ansy); return 0; }
【bzoj3680】平衡點 模擬退火