Q:模拟退火是什么?
A:一种随机化算法,用于水分。
Q:模拟退火可以解决什么问题?
A:可以处理多峰或单峰函数的求值,这时函数没有二分性,只能动态规划,想不出正解时便可尝试使用模拟退火。
Q:模拟退火的原理是什么?
A:结合物理学,模拟生活中金属从高温降温的情况,故曰 “ 退火 ” ,当温度高时分子运动速率快,不稳定,可以接纳新答案大幅变化,温度降下后分子变得稳定,答案的变化量便随之稳定下来。
Q:模拟退火的优缺点有什么?
A:
优点:对比于爬山和一般的贪心来说,模拟退火不会拘泥于局部最优解,有比较好看的概率接受更优解,同时模拟退火一般码量较小,思维难度较低,考场上容易想到进而水分甚至能拿到接近满分的分数,可以说是十分实用的技巧了。
缺点:当答案值域较大或数据比较刁钻 (出题人卡退火 )时随机的缺陷就会显示出来,它会WA。
Q:模拟退火的关键是什么?
A:如何在快速的时间内统计新答案以及参数的设置便是模拟退火的重中之重。初始温度,温度变化,末温度都会在一定程度上影响你的结果,要学会在大数据的调试中设置一个帅气的参数才能获得更高的分数。
下面介绍模拟退火的流程
1.设定初始状态
初始状态是什么其实无所谓,只需要是一个合法方案就可以了。
2.设定退火过程的参数
参数有三个 :初温 , 温度变化量 , 末温 。
设置这三个参数时的学问便是:《如何同时保证时间复杂度和正确率》
下面放出我一般的设置参数。
double T=1000000000.0/*初温*/,dt=0.9997/*温度变化量*/; while (T>0.0001/*末温*/)
根据不同题目需要coder自身调整参数值从而获取最优解。
3.新旧答案的统计与交替
这一步流程如下:
1.随机一个新状态,并在此状态下统计一个新的答案。
2.将新答案与上一次的答案作比较并更新最终答案(我们需要三个表示答案的变量,分别表示这次的答案,上一次的答案以及最终的最优答案)。
下面便是模拟退火的关键
3.对于比上次更优的答案,毫无疑问我们选择接纳并改变我们当前的状态。
而对于不优的答案,普通的贪心或爬山算法可能会就此作罢从而陷入局部最优解。
而模拟退火则选择一个与当前温度和答案变化幅度相关的概率来判断是否接纳此不优答案
简单地说,我们有一定概率接纳这个新的不优的答案,从而避免答案陷入局部最优解。
具体实现如下
//这里以答案更小为更优作为参考
//ans 表示这次的答案 lans 表示上一次的答案 maxn 表示最终的最优解
if (ans<lans) swap(id[x],id[y])/*这是更改状态的语句*/,lans=ans,maxn=min(maxn,lans);
//若答案更优我们直接接纳
else if (((double)(rand()%32767))/32767.0<=exp(-(ans-lans)/T)) //答案不优我们计算概率,你细品
swap(id[x],id[y]),lans=ans,maxn=min(maxn,lans);
//若答案不优概率接纳新答案,这个概率由当前温度和答案差来控制,可以细品一下这个语句
这里的 e x p ( N ) exp(N) exp(N)是指自然对数e的 N 次方 N次方 N次方,即: e x p ( N ) exp(N) exp(N) = = = e N e^N eN
到此为止,模拟退火已经基本结束,但还有最后一步。
4.降温
最后我们需要手动降温
T*=dt;
到此为止,模拟退火流程结束。
对于完整的CODE,参考以下例题
模拟退火板题:P2210 Haywire
题目大意:
给出n个数和一些双向边,边长为两个端点在排列中的距离差,答案为所有边长的和,求一个使答案最小的排列,输出答案。
n < = 12 n<=12 n<=12 ,每个点度数为3。
solution
观察到n很小,所以值域很小,在不想思考DP的情况下考虑直接模拟退火,每次 O ( 3 ) O(3) O(3)即可修改答案。
code
//Lemt_lzx
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include