「学习笔记」浅入模拟退火
什么是退火?
来自百度百科
退火是一种金属热处理工艺,指的是将金属缓慢加热到一定温度,保持足够时间,然后以适宜速度冷却。目的是降低硬度,改善切削加工性;降低残余应力,稳定尺寸,减少变形与裂纹倾向;细化晶粒,调整组织,消除组织缺陷。准确的说,退火是一种对材料的热处理工艺,包括金属材料、非金属材料。而且新材料的退火目的也与传统金属退火存在异同。
此时的你大概是这样的
看不懂不要紧 毕竟不会让你真的去退火的,让我们一步一步的学习。
在此之前,如果你了解 爬山算法,那你大概可以知道模拟退火的过程了。
模拟退火是一个随机化算法,每次随机都会有一个结果,我们要维护最优结果,但有时最优结果是由当前的不优结果推来的,模拟退火可以让我们有一定的概率接受不优的结果,使得最后可以得到最优解。
过程
模拟退火有三个变量,初始温度 \(T_0\),降温系数 \(d\),终止温度 \(T_k\)。其中 \(T_0\) 是一个比较大的数,\(d\) 是一个非常接近 \(1\) 但是小于 \(1\) 的数,\(T_k\) 是一个接近 \(0\) 的正数。
首先让温度 \(T=T_0\),然后按照上述步骤进行一次转移尝试,再让 \(T=d\cdot T\)。当 \(T<T_k\) 时模拟退火过程结束,当前最优解即为最终的最优解。
注意为了使得解更为精确,我们通常不直接取当前解作为答案,而是在退火过程中维护遇到的所有解的最优值。
题目
P1337 [JSOI2004] 平衡点 / 吊打XXX - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
很好的入门题,可以在这道题学习模拟退火的大致模板。
//The code was written by yifan, and yifan is neutral!!!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))
typedef double db;
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 1100;
int n;
int x[N], y[N], w[N];
db ansx, ansy, dis;
db calc(db xx, db yy) {
db res = 0;
rep (i, 1, n, 1) {
db 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;
}
db Rand() {
return (db)rand() / RAND_MAX;
}
void sa() {
db t = 1e5;
db nowx = ansx, nowy = ansy;
while (t > 1e-3) {
db curx = nowx + t * (Rand() * 2 - 1);
db cury = nowy + t * (Rand() * 2 - 1);
db delta = calc(curx, cury) - calc(ansx, ansy);
if (exp(-delta / t) > Rand()) {
nowx = curx;
nowy = cury;
}
t *= 0.97;
}
rep (i, 1, 100, 1) {
db curx = ansx + t * (Rand() * 2 - 1);
db cury = ansy + t * (Rand() * 2 - 1);
calc(curx, cury);
}
}
void simulate() {
while (clock() < CLOCKS_PER_SEC * 0.9) {
sa();
}
}
int main() {
srand(time(0));
n = read<int>();
rep (i, 1, n, 1) {
x[i] = read<int>(), y[i] = read<int>(), w[i] = read<int>();
ansx += x[i], ansy += y[i];
}
ansx /= n, ansy /= n;
dis = calc(ansx, ansy);
simulate();
printf("%.3lf %.3lf\n", ansx, ansy);
return 0;
}
P2210 Haywire - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
也是一道很好的入门题。
//The code was written by yifan, and yifan is neutral!!!
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define bug puts("NOIP rp ++!");
#define rep(i, a, b, c) for (int i = (a); i <= (b); i += (c))
#define per(i, a, b, c) for (int i = (a); i >= (b); i -= (c))
typedef double db;
template<typename T>
inline T read() {
T x = 0;
bool fg = 0;
char ch = getchar();
while (ch < '0' || ch > '9') {
fg |= (ch == '-');
ch = getchar();
}
while (ch >= '0' && ch <= '9') {
x = (x << 3) + (x << 1) + (ch ^ 48);
ch = getchar();
}
return fg ? ~x + 1 : x;
}
const int N = 15;
int n, ans = 1e9;
int pos[N];
vector<int> frd[N];
int calc() {
int res = 0;
rep (i, 1, n, 1) {
for (int v : frd[i]) {
res += abs(pos[i] - pos[v]);
}
}
if (res / 2 < ans) {
ans = res / 2;
}
return res / 2;
}
void sa() {
db T = 1000;
while (T > 0.001) {
int x, y;
do {
x = rand() % n + 1;
y = rand() % n + 1;
} while (x == y);
swap(pos[x], pos[y]);
int res1 = calc();
swap(pos[x], pos[y]);
int res2 = calc();
int delta = res1 / 2 - res2 / 2;
if (delta <= 0 || (exp((db)delta / T) > (db)rand() / RAND_MAX)) {
swap(pos[x], pos[y]);
}
T *= 0.97;
}
}
void simulate() {
while (clock() < CLOCKS_PER_SEC * 0.8) {
sa();
}
}
int main() {
srand(time(0));
n = read<int>();
rep (i, 1, n, 1) {
rep (j, 1, 3, 1) {
frd[i].emplace_back(read<int>());
}
}
rep (i, 1, n, 1) {
pos[i] = i;
}
calc();
simulate();
cout << ans << '\n';
return 0;
}
朝气蓬勃 后生可畏