Simulate Anneal模拟退火算法,是一种用于获得最优解的随机化算法。c++
若是能够打一手漂亮的随机化搜索,也许当你面对束手无策的神仙题时就有一把趁手的兵器了。算法
这篇题解将教你什么?SA的基本思路,何时能用SA。函数
标题是浅谈,因此本篇博客参杂了些许我的简介,如有疑问或异议,欢迎提出指正。学习
我也很感谢大家给出的建议,它们真的能让我变好、变强。优化
那么咱们进入本篇正题。spa
1. 什么是模拟退火:设计
模拟退火是一种在广大的搜索空间寻找最优解的随机化算法。咱们看名字就明白,这个算法实在模拟物理中退火的过程。知识不少时候都是相通的,咱们学习的大部分知识都是有用的。code
为何经过模拟退火的过程能够得出最优解呢?在物理中,固体物质的退火过程和通常的组合优化问题有着很高的类似度。blog
2. 模拟退火基本要素:游戏
知足这些你就能够将SA算法愉快地嵌入你的程序里了。
第一个要素就是状态空间和状态的产生函数,咱们须要有一个搜索空间,并且范围较大。
什么是搜索空间呢?咱们学习搜索的时候应该接触过,咱们的搜索算法本质就是在问题的求解空间中进行遍历,寻找最优解。模拟退火的状态空间也相似,它就是咱们自定义出来的最优解的有限集合。
咱们光有状态空间还不够啊,咱们真正须要的是状态。学函数的时候,老师就说过了,一个函数要有“域”。咱们的状态空间就是状态产生函数的“域”。而咱们若是想写一个好的退火,咱们的搜索空间要足够大,足够让函数生成不一样的新解。
第二个就是候选解,咱们在状态空间内经过生成的随机数在必定密度内随机选择咱们的候选解。
其实还有一条是几率分布,大部分采用均匀分布,少数状况咱们会用到指数分布。
至于状态转移几率,我目前接触到的SA算法通通采用了Metropolis准则,我接下来会介绍它。
3. 模拟退火基本流程:
一. 由一个状态产生函数从当前解产生一个新解,通常采用增量构造(即由原解加上/减去产生函数产生的值)来获得。
二. 计算新解的目标函数差Δt'。
三. 判断新解是否被接受,
这里介绍Metropolis准则:若Δt'<0,咱们接受它,不然以exp(-Δt'/T)的多项式几率接受它。
**T是咱们模拟退火过程当中的温度**
四. 当新解被接受时,用新解代替当前解,不然继续下一轮试验。
4. 模拟退火中的参数控制:
调参能够说是SA算法最难的部分,也是决定你的算法得分率的部分。
这里分享一下神仙FlashHu(LCT导师Orz)写SA时调参的经验,我总结了一下就是这样:
对于eps,能够根据数据范围和精度要求粗略获得eps的大概大小,手动微调能够获得最终的eps。
对于T和ΔT(温度的变化率,通常在0.95-0.99间),先开大一点跑出最优解,而后一边退火一边输出当前的T、ans等信息,大体感觉解的降低速率,越均匀表示参数越好(神仙称之为观察法)
5. 注意事项:
SA可能会陷入当前解卡在局部最小的状况,也就是这样:
图中,红色是咱们的当前解,蓝色是咱们的最优解,红色的线表明SA算法获得的新解,咱们会发现,若是参数不佳,咱们就有可能出现卡在局部最优解的状况。自己算法的设计就有避免这一种状况,还记得吗?Metropolis准则,就算这不是最优解,咱们也以必定几率接受它(答案不更新),就是为了防止这种状况,然而在参数不佳的状况下,它仍可能发生。因此咱们要改进对温度的控制方式。
这里仍是以那道经典到不能再经典的模拟退火模板作例题:平衡点/吊打xxx
这里直接贴上代码,关于算法中须要注意的地方我会打上注释。
#include<bits/stdc++.h> #define down 0.997//ΔT,模拟徐徐降温 using namespace std; inline int read(){ int data=0,w=1;char ch=0; while(ch!='-' && (ch<'0'||ch>'9'))ch=getchar(); if(ch=='-')w=-1,ch=getchar(); while(ch>='0' && ch<='9')data=data*10+ch-'0',ch=getchar(); return data*w; } int n; struct point{ int x,y,w; }object[2019];//物体信息 double ansx,ansy,answ;//答案 double energy(double x,double y){//物理学知识:能量总和越小越稳定 double r=0,dx,dy; for(int i=1;i<=n;i++){ dx=x-object[i].x;dy=y-object[i].y; r+=sqrt(dx*dx+dy*dy)*object[i].w;//力臂乘重力 } return r; } void SA(){ double t=3e3+10;//初始温度要高 while(t>1e-15){//exp略大于0 double ex=ansx+(rand()*2-RAND_MAX)*t; double ey=ansy+(rand()*2-RAND_MAX)*t; double ew=energy(ex,ey); double de=ew-answ; if(de<0){//此答案更优 ansx=ex;ansy=ey;answ=ew; }else if(exp(-de/t)*RAND_MAX>rand()){//Metropolis准则,以多项式几率接受 ansx=ex;ansy=ey; }t*=down;//逐步降温 } } void solve(){ SA();SA();SA();SA();SA(); } int main(){ n=read(); for(int i=1;i<=n;i++){ object[i].x=read();object[i].y=read();object[i].w=read(); ansx+=object[i].x;ansy+=object[i].y; } ansx/=n;ansy/=n;//设平均值为初值 answ=energy(ansx,ansy); solve(); printf("%.3lf %.3lf\n",ansx,ansy); return 0; }
游戏结束。