什么是退火?

来自百度百科
退火是一种金属热处理工艺,指的是将金属缓慢加热到一定温度,保持足够时间,然后以适宜速度冷却。目的是降低硬度,改善切削加工性;降低残余应力,稳定尺寸,减少变形与裂纹倾向;细化晶粒,调整组织,消除组织缺陷。准确的说,退火是一种对材料的热处理工艺,包括金属材料、非金属材料。而且新材料的退火目的也与传统金属退火存在异同。

此时的你大概是这样的

???

看不懂不要紧 毕竟不会让你真的去退火的,让我们一步一步的学习。


在此之前,如果你了解 爬山算法,那你大概可以知道模拟退火的过程了。

模拟退火是一个随机化算法,每次随机都会有一个结果,我们要维护最优结果,但有时最优结果是由当前的不优结果推来的,模拟退火可以让我们有一定的概率接受不优的结果,使得最后可以得到最优解。

过程

模拟退火有三个变量,初始温度 \(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;
}