模拟退火
简介¶
模拟退火是一种随机化算法。当一个问题的方案数量极大(甚至是无穷的)而且不是一个单峰函数时,我们常使用模拟退火求解。
实现¶
根据爬山算法的过程,我们发现:对于一个当前最优解附近的非最优解,爬山算法直接舍去了这个解。而很多情况下,我们需要去接受这个非最优解从而跳出这个局部最优解,即为模拟退火算法。
什么是退火? (选自百度百科)
退火是一种金属热处理工艺,指的是将金属缓慢加热到一定温度,保持足够时间,然后以适宜速度冷却。目的是降低硬度,改善切削加工性;消除残余应力,稳定尺寸,减少变形与裂纹倾向;细化晶粒,调整组织,消除组织缺陷。准确的说,退火是一种对材料的热处理工艺,包括金属材料、非金属材料。而且新材料的退火目的也与传统金属退火存在异同。
由于退火的规律引入了更多随机因素,那么我们得到最优解的概率会大大增加。于是我们可以去模拟这个过程,将目标函数作为能量函数。
模拟退火算法描述¶
先用一句话概括:如果新状态的解更优则修改答案,否则以一定概率接受新状态。
我们定义当前温度为
注意 :我们有时为了使得到的解更有质量,会在模拟退火结束后,以当前温度在得到的解附近多次随机状态,尝试得到更优的解(其过程与模拟退火相似)。
如何退火(降温)?¶
模拟退火时我们有三个参数:初始温度
首先让温度
引用一张Wiki - Simulated annealing的图片(随着温度的降低,跳跃越来越不随机,最优解也越来越稳定)。
代码¶
此处代码以「BZOJ 3680」吊打 XXX(求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | #include <cmath> #include <cstdio> #include <cstdlib> #include <ctime> const int N = 10005; int n, x[N], y[N], w[N]; double ansx, ansy, dis; double Rand() { return (double)rand() / RAND_MAX; } double calc(double xx, double yy) { double res = 0; for (int i = 1; i <= n; ++i) { double dx = x[i] - xx, dy = y[i] - yy; res += sqrt(dx * dx + dy * dy) * w[i]; } if (res < dis) dis = res, ansx = xx, ansy = yy; return res; } void simulateAnneal() { double t = 100000; double nowx = ansx, nowy = ansy; while (t > 0.001) { double nxtx = nowx + t * (Rand() * 2 - 1); double nxty = nowy + t * (Rand() * 2 - 1); double delta = calc(nxtx, nxty) - calc(nowx, nowy); if (exp(-delta / t) > Rand()) nowx = nxtx, nowy = nxty; t *= 0.97; } for (int i = 1; i <= 1000; ++i) { double nxtx = ansx + t * (Rand() * 2 - 1); double nxty = ansy + t * (Rand() * 2 - 1); calc(nxtx, nxty); } } int main() { srand(time(0)); scanf("%d", &n); for (int i = 1; i <= n; ++i) { scanf("%d%d%d", &x[i], &y[i], &w[i]); ansx += x[i], ansy += y[i]; } ansx /= n, ansy /= n, dis = calc(ansx, ansy); simulateAnneal(); printf("%.3lf %.3lf\n", ansx, ansy); return 0; } |
习题¶
build本页面最近更新:,更新历史
edit发现错误?想一起完善? 在 GitHub 上编辑此页!
people本页面贡献者:
copyright本页面的全部内容在 CC BY-SA 4.0 和 SATA 协议之条款下提供,附加条款亦可能应用