算法由来
对于一个非晶体的固体,
我们可以通过将其加热到某一温度,使得其分子活化,
在徐徐降温,使其分子排布趋向有序。
我们也可以通过模拟固体降温的过程,不断的靠向最优值。
用途
这是一个用于求解一些最优化问题的随机算法。
算法流程
首先,因为这是一个随机算法,为了保险,我们多做几次。
下面我们只考虑某一次的算法过程:
- 我们先选定一个初值(初始的答案)(Ans_0)。
- 我们再选定一个初始的温度(T)和每次的降温幅度(D)。
- 我们考虑每一次降温之后,生成一个新解,
对于生成一个新解,我们不太可能直接在可行域中随机,这显然不太科学。
我们只考虑当前解关于温度(T)的“邻域”,即变化范围和(T)正相关。
这也是模拟退火温度越低,越稳定的原因之一。
- 一般上都是从当前解通过一些简单变换得到新解(Ans_1)。
此时,我们考虑设计一个估价函数(f(x))表示将(x)作为答案的优劣程度,
习惯上(f(x))越大,(x)作为答案越不优秀。
-
有了估价函数,我们就可以考虑是否接受新解了:
- 如果(f(Ans_1) < f(Ans_2)),那么直接接受。
- 如果(f(Ans_1) > f(Ans_2)),此时我们需要一定概率的接受劣解,
借此“跳出”局部最优解,达到全局最优解,具体如下:根据(Metropolis)准则,粒子在温度(T)时趋于平衡的概率为(exp(-Delta E/(kT))),
其中(Delta E)为当前解与新解之间的“内能差”,(k)为(Boltzmann)常数,当作(1)就好,
那么,我们生成一个劣解之后,就有(exp(-frac{f(Ans_1)-f(Ans_2)}{T}))的概率接受它了。
这就是温度(T)越低,接受劣解的概率越小,越稳定。
-
最后我们设置一个温度下限,当温度达到这个值之后就输出答案。
-
那么我们就可以求出近似最优解了。
模板代码((HDU2899))
#include <bits/stdc++.h>
using namespace std;
typedef double db;
db Y;
db func(db x, db res = 0.0) {
res += 5.0 * x * x;
res += 7.0 * x * x * x;
res += 8.0 * x * x * x * x * x * x;
res += 6.0 * x * x * x * x * x * x * x;
return res - Y * x;
}
const db lim = RAND_MAX;
db P() { return (db)(rand()) / lim; }
db Sgn[2] = {1, -1};
db Delta(db T) { return Sgn[rand() & 1] * T * P(); }
const db eps = 1e-10, D = 0.95;
db calc() {
db Ans_0 = P() * 100.0, F_0 = func(Ans_0);
for(db T = 101.0; T > eps; T *= D) {
db Ans_1 = Ans_0 + Delta(T), F_1 = func(Ans_1);
if(Ans_1 < 0.0 || Ans_1 > 100.0) continue;
if(F_1 < F_0 || P() < exp((F_0 - F_1) / T)) Ans_0 = Ans_1, F_0 = F_1;
}
return Ans_0;
}
int main() {
srand(time(0));
int T = 0;
scanf("%d", &T);
while(T--) {
scanf("%lf", &Y);
db Ans = 1e15;
for(int i = 1; i <= 100; i++)
Ans = min(Ans, func(calc()));
printf("%.4lf
", Ans);
}
return 0;
}