模拟退火——一种玄学算法

模拟退火介绍

模拟退火算法来源于固体退火原理,是一种基于概率的算法,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温升变为无序状,内能增大,而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。

来自百度百科

什么乱七八糟的

说白了就是求最小解。

看来学习此算法,还要学习炼钢(玩匠魂的就当我没说)。

\(\Huge \color{red} \textbf{注意!模拟退火找的是最小解!}\)

过程

选定一个温度

炼钢当然要有温度啦!(好像匠魂冶炼炉不需要设定温度,不管他了)。

我们姑且设这个温度为 \(T\) ( \(\large \textbf{T}\) \(emperature\) )。

大于边界值的处理

随机变化坐标,变化幅度为 \(T\)

计算新解与当前解的差 \(\delta\)

如果新解比当前解优(\(\delta > 0\)),就用新解替换当前解。

否则以 \(e^{\frac{DE} {T}}\) 的概率用新解替换当前解。

温度乘上一个小于\(1\)的系数,即降温。

参考资料:SuperJvRuo 的博客

至于那个概率的底数为什么是\(e\),因为概率统计中常用的正态分布中经常以\(e\)为底。(纯属猜测,就当我胡说八道吧)

洛谷不能上传\(svg\),这是我在这里转换的。

代码实现

const double eps=1e-14;
double xans,yans;
double ans=1e14;
void simulate_anneal(double delta,int t){
	double xx=xans;
	double yy=yans;
	while(t>eps){
		double xtemp=xans+(rand()*2-RAND_MAX)*t;
		double ytemp=yans+(rand()*2-RAND_MAX)*t;
		double new_ans=f(xtemp,ytemp);
		double DE=new_ans-ans;
		if(DE<0){
			xx=xtemp;
			yy=ytemp;
			xans=xx;
			yans=yy;
			ans=new_ans;
		}
		else if(exp(-DE/t)*RAND_MAX>rand()){
			xx=xtemp;
			yy=ytemp;
		}
		t*=delta;
	}
}

依旧参考了上述博客。

\(eps\)的设定是为了防止浮点数误差。

\(ans\)设定的初始值无所谓,只要够大就行。(因为找的是最小解)。

提示

跑模拟退火记得要多跑几次。应为模拟退火是玄学算法(使用了\(rand\)),如果自己脸不干净很容易\(\color{red}\textbf{WA}\)

其他做法

如果单纯求最小值(极值),可以使用费马定理/罗尔定理(某些题不适用)。

\(x\)\(f\)函数的极值(最小值,最大值,拐点),则\(f^{'}(x)=0\)

推荐

如果你想了解更多,可以看洛谷日报

posted @ 2022-01-26 15:17  蒟蒻xiezheyuan  阅读(158)  评论(0编辑  收藏  举报