【模板】模拟退火 Simulated Annealing

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;
}
posted @ 2022-11-23 18:45  caijianhong  阅读(63)  评论(0编辑  收藏  举报