模拟退火
为什么叫退火请自行百度
过程
首先我们要知道爬山算法是什么:爬山就是单纯的贪心,遇到更优的解时才更新答案,但这样一来就可能陷入局部最优解中
模拟退火就通过一定概率取非最优值,尝试跳出这个坑
模拟退火有三个重要的参数:\(T_0\)(初始温度,要足够高),\(T_E\)(结束温度,略略大于 \(0\) 的正数)和 \(d\)(降温系数,略略小于 \(1\) 的数)
我们让初始温度为 \(T=T_0\),每次退火后 \(T\times d\),直到 \(T<T_E\) 结束
根据我们现在的 \(T\),我们得到一个解 \(E\),设它与目前最优解 \(E_0\) 的差为 \(\Delta\),如果 \(\Delta<0\),那么我们就接受这个解;否则,我们就有 \(e^{\frac{-\Delta}{T}}\) 的概率接受这个解(通过随机数实现)
例题
本题正解是计算几何,我不会
一个体系中,其能量越小,就越稳定
计算能量的方法为:\(dist\times w\),其中 \(dist\) 是物体到结点的距离,\(w\) 是物体的重量
这样我们就可以开始退火,亲测 \(T_0=3000,T_E=1e-14,down=0.997\),随机数种子为 \(time(0)+52071\),初始能量取 \(1e18\) 更大几率能过
#include<bits/stdc++.h>
#define down 0.997
#define LL long long
inline int reads()
{
int sign = 1, re = 0; char c = getchar();
while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
return sign * re;
}
int n;
struct thing
{
int x, y, w;
}a[1005];
double ansx, ansy, answ;
inline double energy(double x, double y)
{
double re = 0;
for(int i = 1; i <= n; i++)
{
double dx = x - a[i].x, dy = y - a[i].y;
re += sqrt(dx * dx + dy * dy) * a[i].w;
}
return re;
}
inline void anneal()
{
double t = 3000, rx = ansx, ry = ansy;
while(t > 1e-14)
{
double nx = rx + ((rand() << 1) - RAND_MAX) * t, ny = ry + ((rand() << 1) - RAND_MAX) * t;
double nw = energy(nx, ny);
double delta = nw - answ;
if(delta < 0) ansx = rx = nx, ansy = ry = ny, answ = nw;
else if(exp(-delta / t) * RAND_MAX > rand()) rx = nx, ry = ny;
t *= down;
}
}
signed main()
{
#ifndef ONLINE_JUDGE
freopen("test.in", "r", stdin);
freopen("test.out", "w", stdout);
#endif
srand(time(0) + 52071);
n = reads();
for(int i = 1; i <= n; i++) a[i] = (thing){reads(), reads(), reads()}, ansx += a[i].x, ansy += a[i].y;
ansx /= n, ansy /= n; answ = 1e18;
anneal(); anneal(); anneal();
printf("%.3lf %.3lf", ansx, ansy);
return 0;
}