模拟退火

模拟退火,算法如其名,真的就是个大模拟,模拟的是高温物体徐徐降温然后达到结晶态的过程,结晶态是内能最低的状态,而我们利用模拟退火,要找的也正是最值。

过程

模拟退火一般是用于在一个函数上找最值,也可以干一些奇奇怪怪的事情,这里就以函数为例子了。

既然是模拟退火,肯定要有个温度呀!所以要有一个温度 T,以及还要有个初始点 x,我们再给定一个跳跃的范围 dirdirdirdir 因题目而异,你也可以用 TTT 来代替 dirdirdir,但个人认为 dirdirdir 根据题目手动设定比较好)就可以开始退火了。

为了方便,我们设问题是:要在一个函数上找最大值。

因为要退火,所以温度要降低,因为是徐徐降温,所以 TTT 减小得要慢。于是我们设一个 δ\deltaδ,每次使 T∗δT*\deltaTδ 即可。(δ\deltaδ 一般是一个 0.950.950.95 ~ 0.990.990.99 间的小数)

每次在 [x−dir,x+dir][x-dir,x+dir][xdir,x+dir] 这个范围内随机找一个点 x′x'x,(下文为了方便,将当前点的值设为 yyyx′x'x 点的值为 y′y'y)假如 y>y′y>y'y>y,那么就贪心地跳过去(即 x=x′x=x'x=x),否则,就按照一定的几率,决定是否跳过去,这个奇妙几率的计算公式是这样的:
e(y−y′)/T e^{(y-y')/T} e(yy)/T

至于这个公式怎么来的,还是去研究研究热力学吧。。

可以发现,当温度越来越低时,这个几率也会越来越小,那么在最后就有很大几率停在最优解上了。

但是为了防止最后还在往一些奇奇怪怪的地方跑,可以考虑记录沿途经过的所有点中的最优解。

并且因为这是个随机算法,所以为了保证正确率,多退几次火就好了。

代码如下:

#define delta 0.99
#define del 0.995
void fire()
{
    double T=100;
    double x=l+(r-l)*((double)rand()/RAND_MAX),xx=kk(x),dir=(r-l)/2;
	//x为初始点,这个点在l~r范围内随机选一个,设kk()为求值函数
    while(T>1e-4)
    {
        double y=(double)rand()/RAND_MAX*(dir*2.0)+x-dir;
        if(y>r||y<l)continue;//如果越界就不要了
        double yy=kk(y);
        if(yy>xx)x=y,xx=yy;
        else if(exp((xx-yy)/T)>(double)rand()/RAND_MAX)x=y,xx=yy;
        T*=delta;dir*=del;
        //dir要和T一样慢慢减小,防止搜到最大值附近之后还乱跑
   		if(xx>anss)ans=x,anss=xx;//记录答案
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值