笔记——模拟退火

蓝月の笔记——模拟退火篇

摘要

随机化乱搞,记得洗脸

Part 1 原理

随机答案,在其中寻找最优解

Part 2 实现

首先对于买一个状态定义一个能量函数 \(E(x)\),为假设最优点为 \(x\) 时的答案。定义当前温度 \(tpr\),初始温度 \(T_s\),降温系数 \(\Delta T\),结束温度 \(T_e\)

我们随机一个答案作为备选答案,随机的不确定性由当前温度决定。若当前备选答案由于当前最优解,则替换最优解。否则以一定概率接受这个解,具体地,这个概率为 \(e^{\frac{-\Delta E}{tpr}}\),其中 \(\Delta E\) 为当前最优解与备选答案的差

\(tpr=T_s\) 开始运行,在 \(tpr < T_e\) 时结束,每次 \(tpr \gets tpr \times \Delta T\) 来降温

为了使答案更准确,可以使用卡时技巧,时间没到就一直跑

Part 3 例题

Luogu P1337 [JSOI2004] 平衡点 / 吊打XXX

考虑能量函数,显然对于一个点来说,每一根线对它造成的势能总和越结晶 \(0\),它就越有可能时答案。那么定义 \(E([x,y])=\displaystyle\sum_{i=1}^{n}dis([x,y],[a_{i_x},a_{i_y}]) \times a_{i_w}\)

然后按模拟退火过程处理即可

代码:

// BLuemoon_
#include <bits/stdc++.h>

using namespace std;
using DB = double;

const int kMaxN = 1e3 + 5;
const DB kTpr = 1e4, kDlt = 0.99;

struct P {
  DB x, y, we;
};

int n;
P e[kMaxN];
DB X, Y, ans = 1e18, tpr;

DB E(DB l, DB r, DB s = 0, DB dx = 0, DB dy = 0) {
  for (int i = 1; i <= n; i++) {
    dx = l - e[i].x, dy = r - e[i].y;
    s += (sqrt(dx * dx + dy * dy) * e[i].we);
  }
  return s;
}
DB D() {
  return (rand() * 2 - RAND_MAX) * tpr;
}
void SA(DB nx = 0, DB ny = 0) {
  for (tpr = kTpr, nx = X, ny = Y; tpr > 1e-13;) {
    DB xt = X + D(), yt = Y + D(), E_ = E(xt, yt), dlt = E_ - ans;
    if (dlt < 0) {
      X = nx = xt, Y = ny = yt, ans = E_;
    } else {
      if (exp(-dlt / tpr) * RAND_MAX > rand()) {
        nx = xt, ny = yt;
      }
    }
    tpr *= kDlt;
  }
}

int main() {
  srand(time(0));
  cin >> n;
  for (int i = 1; i <= n; i++) {
    cin >> e[i].x >> e[i].y >> e[i].we;
  }
  for (; clock() * 1.0 / CLOCKS_PER_SEC <= 0.95; SA()) {
  }
  cout << fixed << setprecision(3) << X << ' ' << Y << '\n';
  return 0;
}

注意参数调整和洗脸

特别注意洗脸

习题

Luogu P2210 Haywire 利用随机化来扰动排列

Luogu P3878 [TJOI2010] 分金币 同样时扰动排列,注意 \(E\) 函数计算方式

Code

posted @ 2024-09-03 20:00  BluemoonQwQ  阅读(14)  评论(0编辑  收藏  举报