模拟退火(SA)详解
SA——优雅的暴力

-
随机出一个答案:根据题目而变,但一般是随机出的,但是和简单的随机不同,这个答案一般是根据先有的解(注意,不一定是最优解,因为有一定概率我们接受了一个可能更接近最优答案的答案),在此基础随机出的
-
确定接受新状态的概率:见下面代码
模拟退火算法进阶
- 关于参数
模拟退火的参数基本上决定了代码的准确性,一般的, \(T_0\) 越高, \(T_k\) 越低( \(T_k >0\) ),\(d\) 越接近 \(1\) ,模拟退火越准确, 但时间越长,建议在写模拟退火是要卡卡参数
- 关于随机数
模拟退火的精髓就在随机数上,随机数的随机性,循环周期基本上决定了准确度,随机性差,循环周期短的很可能将模拟退火卡掉,建议参考这来优化随机数
-
关于时间
模拟退火是一个不太稳定的算法
(谁叫他随机),单次可能找不到最优解,一般的要多次运行,有一个clock()函数,返回程序运行时间,建议在不超时的情况下尽可能多跑一般的,可以这样
while((double)clock()/CLOCKS_PER_SEC<=0.8) SA();(进行模拟退火); -
关于随机化种子
srand((unsigned)time(0)) or srand((int)time(NULL)) -
关于概率接受劣解概率
\[ \triangle E表示新解-原解\\ P(\triangle E)=\begin{cases} 1 & 新解更优\\ e^{\frac{-\triangle E}{T}} & otherwise \end{cases} \]
模板快餐
void SA()
{
double T = 2000;//初始温度
while(T > 1e-10)//退出温度
{
随机生成一个新解
T *= 0.987;//降温
double delta = 原解 - 新解;
if(更优||exp(-(double)delta*RAND_MAX/T) < rand())
/*
关于概率接受劣解的判断条件
exp(-Delta/t)*RAND_MAX<rand() ->> 最大值
exp(-Delta/t)*RAND_MAX>rand() ->> 最小值
*/
{
res = new_res;//接受/一定概率接受
}
}
ans = min(ans,res);//去最值(或其他)
}
最后一点
SA时间复杂度:\(O(-F\times(\log_{delta}T_0/T_{min}))\)
写题经验
-
求一个坐标,使其 \(\dots\) 最大/小
这样退火: double tx=sx+(rand()*2-RAND_MAX)*T; double ty=sy+(rand()*2-RAND_MAX)*T; -
交换顺序,使其 \(\dots\) 最大/小
这样退火: int x=rand()%n+1; int y=rand()%n+1; swap(a[x],a[y]); -
NOIP2021方差,这个比较特殊(退火能拿满分)。

浙公网安备 33010602011771号