模拟退火


模拟退火

我觉得这是个 useless 的算法,只能说正解肯定和这算法毫无关系,你用这算法也别想拿满分,顶多是个不会做的题浪费你时间去赌这么些运气,而且是真的看脸。。。

先看张 oi-wiki 的图:

模拟退火

简单点想:

我们模拟分子运动的过程,随机选取答案,每次令答案跳跃一个距离,也就是随机移动,温度越高,跳的距离就可能越 浮躁 也就是大,温度越低,随机移动的距离就会慢慢减小,不断更新答案得到 “可能” 的最优解。

显然你开始的温度越高你就越可能得到答案,但是相对应的是时间的代价。

算法流程:

  • 固定一个起始的 /(t/) 代表跳跃的种子,也就是温度 ,还有一个系数 /(down/) ,一般令 /(down = 0.996~0.998/) 吧, /(t/) 可以取一个不大不小的数。

  • 每次通过这个 /(t/) 随机得到一个答案,并估价这个答案,也就是检查当前答案和之前最优解的优劣性。

    • 若当前更优,那么直接选用他。
    • 若当前更劣,那么假设代价劣了 /(d/) ,就以 /(e^{/frac{d}{t}}/) 的概率选用这个答案,其实就是玄学
  • $ t /times = down$

  • 若 /(t/) 无限接近于 /(0/) ,结束退火。

然后就是有一个小优化,为了防止TLE,可以直接判断当前运行的时间,也就是 while((double)clock() / CLOCKS_PER_SEC <= 0.997)(以题目时限自定义的时间) 然后如果不超时就继续退火,否则结束程序。

PS:模拟退火看脸,所以如果觉得分不够可以申请重测,会有惊喜也会有惊吓/qd

例题

这里就向量分解一下,然后直接按刚才的流程走就好了,注意 /(e^{x}/) 可以用 /(exp(x)/) 来得到。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define Rep(i,a,b) for(int i=(a);i<(b);++i)
#define rrep(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
template <typename T>
inline void read(T &x){
    x=0;char ch=getchar();bool f=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    if(f)x=-x;
}
template <typename T,typename ...Args>
inline void read(T &tmp,Args &...tmps){read(tmp);read(tmps...);}
const double down = 0.996;
const int N = 2005;
const double eps = 1e-15;
int n;
struct qwq{
    int x,y,w;
}a[N];
mt19937 rnd(time(0));
double sx,sy,sw;
double energy(double x,double y){//代价
    double res = 0,dx ,dy;
    rep(i,1,n){
        dx = x - a[i].x;
        dy = y - a[i].y;
        res +=  sqrt(dx * dx + dy * dy) * a[i].w;
    }
    return res;
}
void tuihuo(){
    double t = 3000;
    while(t > eps){
        double ex = sx + (rand() * 2 - RAND_MAX) * t;
        double ey = sy + (rand() * 2 - RAND_MAX) * t;
        double ew = energy(ex,ey);
        double d = ew - sw;
        if(d < 0)sx = ex,sy = ey,sw = ew;//优
        else if(exp(-d / t) * RAND_MAX > rand())sx = ex,sy = ey;//劣则概率更新答案
        t *= down;
    }
}
void solve(){
    rep(i,1,4)tuihuo();
}
signed main(){
    srand(time(0));
    read(n);
    rep(i,1,n)read(a[i].x,a[i].y,a[i].w),sx += a[i].x,sy += a[i].y;
    sx /= n;sy /= n;sw = energy(sx,sy);
    solve();
    printf("%.3lf %.3lf/n",sx,sy);
}

原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/288254.html

(0)
上一篇 2022年9月8日
下一篇 2022年9月8日

相关推荐

发表回复

登录后才能评论