模拟退火
模拟退火
概述:求解最优化问题,example:TSP,函数max/min
一、理论:
算法认识:基于爬山算法(每次朝着当前上升最快的方向爬,但是初始化不同可能会得到不同的局部最优值,模拟退火可能跳出局部最优值)
流程:初始高温-->温度降低-->终止在低温
本质:贪心+随机化
二、算法描述:
\[\begin{cases}
\text{if f[x+1] > f[x] 移动后更优,那么总是移动} \\
\text{if f[x+1]< f[x]移动后更差,那么以一定的概率移动,并且这个概率随时移动}
\end{cases} \]
$P(dE)=exp(\frac{dE}{kT})$
dE<0,说明:温度越高出出现的能量差为dE的降温概率越大,温度越低
伪CODE:
/*
F(h[x]) 为估价函数
h[x] 为当前的状态
delta 控制降温的快慢
T 系统的温度,初始温度比较高
T_min 温度的下限,若温度达到T_min,停止搜索
*/
while(T>T_min){
y=x+rand()*t;
dE=F(h[y])-F(h[x]);
if(dE>=0)
h[y]=h[x];//转移更优,那么转移
else {
//dE的越大,那么次语句的条件成立概率越大,T越小exp(dE/T)越小
if(exp(dE/T)>random(0,1)){//exp(dE/T)表示以e(自然常数)为底的(dE/T)次方
h[y]=h[x];
}
}
T*=delta//delta 一般取0.98/0.99
}
三·、经典例题:
1.[JSOI2004]吊打XXX
#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<ctime>
#include<cstdlib>
using namespace std;
const int MAXX=1010;
int x[MAXX],y[MAXX],w[MAXX];
double ansx,ansy;
int sumx,sumy,n;
inline double cal(double x0,double y0){
double ans=0;
for(int i=1;i<=n;++i){
double xx=(double)x[i]-x0;
double yy=(double)y[i]-y0;
ans+=(double)sqrt(xx*xx+yy*yy)*w[i];
}
return ans;
}
inline void SA(){
ansx=(double)sumx/n;
ansy=(double)sumy/n;
double t=1926;
double t_min=1e-14;
double delta=0.98;
while(t>t_min){
double randx=ansx+(rand()%20000-10000)*t;
double randy=ansy+(rand()%20000-10000)*t;
double dE=cal(randx,randy)-cal(ansx,ansy);
if(dE<=0){
ansx=randx;
ansy=randy;
}else {
if(exp(-dE/t)*RAND_MAX>rand()){
ansx=randx;
ansy=randy;
}
}
t*=delta;
}
}
int main(){
srand(time(NULL));
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d%d",&x[i],&y[i],&w[i]);
sumx+=x[i];
sumy+=y[i];
}
SA();SA();SA();
printf("%.3lf %.3lf",ansx,ansy);
return 0;
}
/*
INPUT:
3
0 0 1
0 2 1
1 1 1
OUTPUT:
0.577 1.000
*/