模拟退火算法(Simulated Annealing,SA)

        好久没有写博客了,打了很多比赛有的时候又没有补题,昨天进一步了解了模拟退火算法,在这里写下我的总结。

        初中的时候就听说过模拟退火的大名,直至上个学期还不会用模拟退火,看大佬碰见题目就说模拟退火觉得很牛,所以我也来学习一下模拟退火算法。

        在网上搜模拟退火能搜出很多概念,什么物理学上的退火,然后内能什么都出来了,我这里写下我的理解。

        模拟退火,顾名思义就是用程序来模拟一个高温度的,慢慢降温下来成稳定态的算法。而在程序里确实也是这样的,首先设置温度T很高,然后设置循环次数(循环次数由起始温度T和退火系数r组成,还与eps有关,一般在1e-12都行),一般就是用while循环

double T=4000,r=0.995;
while (T>eps)
{
    //....
}

        在这期间,我们要给当前的最优解一个扰动(这个扰动关于T,当温度很高的时候扰动很大,但是温度低的时候就趋于稳定)

double T=4000,r=0.995;
while (T>eps)
{
    double nowx=ansx+(rand()*2-RAND_MAX)*T;//随机产生新的答案 
    double nowy=ansy+(rand()*2-RAND_MAX)*T;//以JSOJ平衡点为例
    /*
        judge
    */
    T*=r;
}

        对于每次答案,与原先进行比较,设置delta=f(tmp)-f(ans),假设我们需要求一个最大值,如果delta大于(或大于等于)0的话,那么这个tmp我们接受概率就是1,这个很好理解,因为你的f(x)值比我之前搜到的还要大,那么我肯定接受,重点在下面。

        如果对于每次的tmp,delta<0,也就是比我们之前搜到的答案还小,那么我们接不接受呢?这里注意,是可能要接受的,因为万一这时候不是最优点,但是接受之后它下一次进行扰动,或者几次之后,它就能达到最高点呢;如果这个时候直接放弃,那么很可能最后的答案就只在极值而不是最值处,只会在它附近轻微扰动,这肯定是不可取的。

        所以模拟退火要求以一定概率接受,也就是如下:

模拟退火算法(Simulated Annealing,SA)_第1张图片

         delta 前面是否加负号依据于是否是求最大值还是最小值。

         例如下列while循环的后面

while(T>eps)
{
    /*
    */
    delta=tmp-ans;
    if (delta<0) ans=tmp,x=nowx;
    else if (exp(-delta/T)*RAND_MAX>rand())//以一定概率接受这个解
        ans=tmp,x=nowx;
    T*=0.995;
}

 

简单题目例子:Problem - 2899 (hdu.edu.cn)

模拟退火算法(Simulated Annealing,SA)_第2张图片

        给出y,求f(x)在x在[0,100]中的最小值。

        按照当时学习二分、三分的时候方法,这个题应该是那么做,先求导,再求等于0的时候就是极值点,然后求最值。

        那么这个题目用模拟退火来写的话就可以用,而且时间也不是很多。

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define ll long long
#define endl "\n"
#define debug(a) cout<<#a<<"="<eps)
    {
        nowx=(rand()*2-RAND_MAX)*T;
        while (abs(nowx)>=100) nowx/=10;  
        nowx+=x;
        if (nowx>100) nowx-=100;
        if (nowx<0) nowx+=100;
        //这里不知道如何求得在[0,100]之间的小数,就暴力求解了
        tmp=f(nowx);
        delta=tmp-ans;
        if (delta<0) ans=tmp,x=nowx;
        else if (exp(-delta/T)*RAND_MAX>rand())
            ans=tmp,x=nowx;
        T*=0.995;
    }
}
inline void Case_Test()
{
    cin>>y;
    T=4000;ans=(double)inf;
    for (int i=1;i<=5;i++) SA();
    cout<>_;
    
    while (_--)
    {
        Case_Test();
    }

    #ifndef ONLINE_JUDGE
        end = clock();
        cout << endl << "Runtime: " << (double)(end - start) / CLOCKS_PER_SEC << "s\n";
    #endif
}

        还有一个很经典的例题,待补 JSOJ平衡点

你可能感兴趣的:(算法,算法)