模拟退火学习

模拟退火,很奇妙!

原理

现在要求答案 \(f(S)\) 的最值。(假设要求最小值,函数为实值函数。)
设有一个 \(S'\) 是与 \(S\) 接近的状态。不妨令 \(\Delta=f(S')-f(S)\)。那么更新答案让 \(S\) 变成 \(S'\) 的概率为:

\[P(\Delta)=\left\{\begin{matrix}1&\Delta\le0\\e^{-\frac\Delta T}&\Delta> 0\end{matrix}\right. \]

其中温度为 \(T\),让 \(T\) 逐渐减小,即是 \(T\leftarrow T\times d\),直到 \(T\le T_k\)
若要求最大值,将公式中的 \(-\Delta\) 改成 \(\Delta\) 即可。
通常取 \(T=10^9\)\(T_k=10^{-9}\)\(d=0.999\)

代码

随机数

inline double rnd(double l,double r){
    return (double)rand()/RAND_MAX*(r-l)+l;
}
inline int rnd(int l,int r){
    return rand()%(r-l+1)+l;
}

模退多次

void work(){
    srand(time(0));
    for(;(double)clock()/CLOCKS_PER_SEC<limit;)sa(T,Tk,d);
}

接下来是不同的模退类型。
小技巧:\(P(\Delta)=P(e^{-\frac\Delta T}>rand(0,1))\)。其中 \(rand(0,1)\) 为在 \(0,1\) 范围内随机生成的实数。

模退 \(S\) 为实数

double sa(double T,double Tk,double d){
    double pos=rnd(-A,A),val=calc(pos),ans=val;
    for(;T>Tk;T*=d){
        double nwpos=rnd(pos-T,pos+T),nwval=calc(nwpos),delta=nwval-val;
        ans=min(ans,nwval);
        if(exp(-delta/T)>rnd(0,1))pos=nwpos,val=nwval;
    }
    return ans;
}

模退 \(S\) 为向量(点)

double sa(double T,double Tk,double d){
    Point pos=Point(rnd(-A,A),rnd(-A,A));
    double val=calc(pos),ans=val;
    for(;T>Tk;T*=d){
        Point nwpos=Point(rnd(pos.x-T,pos.x+T),rnd(pos.y-T,pos.y+T));
        double nwval=calc(nwpos),delta=nwval-val;
        ans=min(ans,nwval);
        if(exp(-delta/T)>rnd(0,1))pos=nwpos,val=nwval;
    }
    return ans;
}

模退 \(S\) 为排列(排列为 \(1\)\(n\) 的排列)

double sa(double T,double Tk,double d){
    for(int i=1;i<=n;i++)p[i]=i;
    random_shuffle(p+1,p+n+1);
    double val=calc(),ans=val;
    for(;T>Tk;T*=d){
        int pos1=rnd(1,n),pos2=(1,n);
        swap(p[pos1],p[pos2]);
        double nwval=calc(),delta=nwval-val;
        ans=min(ans,nwval);
        if(exp(-delta/T)>rnd(0,1))val=nwval;
        else swap(p[pos1],p[pos2]);//相当于没有操作
    }
    return ans;
}

模退 \(S\) 为集合(集合元素为 \(1\)\(n\) 的整数)

double sa(double T,double Tk,double d){
    for(int i=1;i<=n;i++)v[i]=rnd(0,1);
    double val=calc(),ans=val;
    for(;T>Tk;T*=d){
        int pos=rnd(1,n);
        v[pos]^=1;
        double nwval=calc(),delta=nwval-val;
        ans=min(ans,nwval);
        if(exp(-delta/T)>rnd(0,1))val=nwval;
        else v[pos]^=1;
    }
    return ans;
}
posted @ 2024-10-22 16:17  junjunccc  阅读(12)  评论(3编辑  收藏  举报