posted on 2021-05-04 18:16:24 | under 学术 | source
模拟退火适用于:
- 你不会正解
- 能写出估价函数,而且最优解的估价最大/小
- 估价函数不单调,不能二分
- 人品好
由于是个带随机数的算法,所以使用需谨慎。
首先需要这几个常量:
const double t0=/*???*/,
delta=0.996,
eps=1e-13,
srd=/*???*/;
分别为:初温、降温系数、eps、随机数种子。一般来说初温越高越准确,降温系数 \(\in[0,1)\)。
接着一个随机数生成:
double rd(){return rand()*2-RAND_MAX;}
生成一个 \([-\texttt{RAND\_MAX},\texttt{RAND\_MAX}]\) 的随机数。
估价函数:
double f(...){
...
}
要保证最优解的估价最低。
模拟退火:
void sa(){
while(clock()<CLOCKS_PER_SEC*0.9){
double t=t0;
while((t*=delta)>eps){
double tmp=ans+rd()*t;//生成随机数,可以改
double noww=f(tmp);
double dt=noww-answ;
if(dt<0||exp(-dt/t)*RAND_MAX>rand()) ans=tmp,answ=noww;
}
}
}
注意是 \(e^{-\frac{\Delta ans}{t}}>rnd()\),其中 \(0\leq rnd()<1\)。
而且这个是求最小值的,最大值那个负号要拿掉。
主函数:
int main(){
srand((int)srd);
...
ans=0;//赋初值
answ=f(ans);
sa();
printf("%.3lf",ans);//输出自己改
return 0;
}
实际例子:
#include <ctime>
#include <cmath>
#include <cstdio>
#include <cstdlib>
using namespace std;
const double t0=10000,
delta=0.996,
eps=1e-13,
srd=1919810;
double rd(){return rand()*2-RAND_MAX;}
struct Dot{double x,y;Dot(double a=0,double b=0):x(a),y(b){}};
Dot ans,a[1010];
double w[1010],answ;
int n;
double f(double x,double y){
double ans=0;
for(int i=1;i<=n;i++){
double dx=a[i].x-x,dy=a[i].y-y;
ans+=sqrt(dx*dx+dy*dy)*w[i];
}
return ans;
}
void sa(){
while(clock()<CLOCKS_PER_SEC*0.9){
double t=t0;
while((t*=delta)>eps){
Dot tmp(ans.x+rd()*t,ans.y+rd()*t);
double noww=f(tmp.x,tmp.y);
double dt=noww-answ;
if(dt<0||exp(-dt/t)*RAND_MAX>rand()) ans=tmp,answ=noww;
}
}
}
int main(){
srand((int)srd);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf%lf%lf",&a[i].x,&a[i].y,&w[i]);
ans.x+=a[i].x,ans.y+=a[i].y;
}
ans.x/=n,ans.y/=n,answ=f(ans.x,ans.y);
sa();
printf("%.3lf %.3lf",ans.x,ans.y);
return 0;
}
标签:tmp,int,double,Simulated,answ,Annealing,模拟退火,ans,noww
From: https://www.cnblogs.com/caijianhong/p/template-Simulated-Annealing.html