1. 就是求函数最值,求出来近似值,近似值(与代码有很大关系);
2. 基于物理原型:一个固体温度高,内能大,内部粒子在运动,温度慢慢降低,粒子趋于有序,达到常温后,粒子稳定运动,内能减小;
通过模拟这个操作,使得答案区域有序,也就是最靠近需要的值。
3. 退火有温度,慢慢降温,每次降当前温度的1%,降到什么时候为止:eps = 1e-14, 也是迭代的循环算法;
double T = 2000; //代表开始的温度
double dT = 0.99; //代表系数delta T
double eps = 1e-14; //相当于0.0000000000000001
while(T > eps) {
//--------------
//这里是每一次退火的操作
//--------------
T = T * dT; //温度每次下降一点点, T * 0.99
}
温度下降越慢,结果越精细;
4. 代码段中“退火操作”
随机找一点 (不超过定义域就可),对应找到函数值, 相当于是粒子,相当于无序运动,向左或向右随机运动,移动幅度与当前温度有关,温度高,运动幅度大,温度低,运动幅度小;
随机移动到另外一个地方,得到, 得到,若比好,就直接替换 ,;
但是,该方法会有一定的概率接受更差的状态;
因为温度在越来越小,运动的幅度也在越来越小,如果粒子一直在右侧移动,随着幅度的变小,它就不可能找到函数的最大值,但若以一定概率接受较差的状态,粒子就可能会跳到左边,在运动幅度减小之前,可能会找到函数的最优解。
概率大小:
,这里的k代表一个超参,代码中可以当作1来使用,T是温度,上述代码第一可以看到;
在这里应该是负值,这是由于概率值在[0,1]范围内,又由于的图像为:
所以 在这里应该是负值,其是一个差值,;
但是具体情况要具体分析,因为当函数值变好了,是一定接受的,函数值变差了,是以一定概率接受的,所以只有当时,才使用令。
5. 具体要定义的函数:
(1)要进行模拟退火的函数;
(2)定义好初始值;
(3)定义好每次运动的幅度 (让选取的新值,要落在定义域中,若没在要重新运算);
T越大,运动幅度越大;
对rand()解释:
可以默认拿到[0,32767]内的随机整数;
RANDMAX = 32767,可以看作常量。本质是宏定义: #define RANDMAX 32767;
rand() * 2 的范围是[0,32767 * 2];
rand() * 2 - RAND_MAX 的范围是[-32767, 32767];
(4)求出新的函数值,若变大直接接受,变小以一定概率接受
目的是要一定概率接受,但是是个准确值,所以从理论上可以生成一个(0,1)的随机数,如果比(0,1)这个随机数要大,那么我们就接受;
但是由于rand()只能产生[0,32767]内的随机整数,化成小数太过麻烦。所以可以把左边乘以RANDMAX(也就是把概率同时扩大32767倍),效果就等同于在(0,1)了.
6. 伪代码
double T = 2000; //代表开始的温度
double dT = 0.99; //代表系数delta T
double eps = 1e-14; //相当于0.0000000000000001
//用自变量计算函数值,这里可能存在多个自变量对应一个函数值的情况,比如f(x,y)
double func(int x, ... ) {
//这里是对函数值进行计算
double ans = .......
return ans;
}
//原始值
double x = rand(); //x0取随机值
double f = func(x,...); //通过自变量算出f(x0)的值
while(T > eps) {
//--------------
//这里是每一次退火的操作
//x1可以左右随机移动,幅度和温度T正相关,所以*T
//注意这里移动可以左右移动,但是也可以单向移动
//关于rand()详细见开头注的①
double dx = (2*rand() - RAND_MAX) * T;
//让x落在定义域内,如果没在里面,就重新随机。题目有要求需要写,否则不用写
// ================
while(x > ? || x < ? ...) {
double dx = (2*rand() - RAND_MAX) * T;
}
// ================
//求出f(x1)的值
double df = func(dx);
//这里需要具体问题具体分析,我们要接受更加优秀的情况。可能是df < f(比如求最小值)
if(f < df) {
f = df; x = dx; [...,y = dy;] // 接受,替换值,如果多个自变量,那么都替换
}
//否则概率接受,注意这里df-f也要具体问题具体分析。
//详细见开头注的②③
else if(exp((df - f) / T) * RAND_MAX > rand()) {
f = df; x = dx; [...y = dy;] // 接受,替换值,如果多个自变量,那么都替换
}
//--------------
T = T * dT; //温度每次下降一点点, T * 0.99
}
//最后输出靠近最优的自变量x值,和函数值f(x)
cout << x << " " << f << endl;
参考: B站视频:大学生速通模拟退火算法_哔哩哔哩_bilibili