• 模拟退火算法


    Q1:模拟退火是什么算法?

            模拟退火是模拟物理上退火方法,通过N次迭代(退火),逼近函数的上的一个最值(最大或者最小值)。
            比如逼近这个函数的最大值C点:

     

    Q2:模拟退火为什么可行?

            讨论这个问题需要理解一下物理原型是怎么样的,也就是原来是怎么“退火"的:
            模拟退火算法的思想借鉴于固体的退火原理,当固体的温度很高的时候,内能比较大,固体的内部粒子处于快速无序运动,当温度慢慢降低的过程中,固体的内能减小,粒子的慢慢趋于有序,最终,当固体处于常温时,内能达到最小,此时,粒子最为稳定

    注意标粗字体:
            1.温度高->运动速度快(温度低->运动速度慢)

            2.温度是缓慢(想象成特别慢的那种)降低的
            3.温度基本不再变化后,趋于有序(最后内能达到最小,也就是接近最优)


    我们通过模拟这个操作,使得我们需要的答案"趋于有序”,也就是最靠近需要的值(最值)。

    Q3:怎么做? 

    大方向:

    模拟退火就是一种循环算法
            1.我们先设定一个初始的温度T(这个温度会比较高,比如2000);

            2.每次循环都退火一次。(具体怎么操作后面详解);
            3.然后降低T的温度,我们通过让T和一个"降温系数“ΔT(一个接近1的小数,比如0.99)相乘,达到慢慢降低温度的效果,直到接近于0(我们用eps来代表一个接近0的数(比如0.00001),只要T


    所以总的来说,用伪代码表示退火的流程是这样的:

    1. double T=2000;//代表开始的温度
    2. double dT=0.99;//代表系数delTa
    3. doubel eps=1e-14;
    4. while(T
    5. //-----------------
    6. //这里是一堆退货操作
    7. //-----------------
    8. T=T*dT;//每次温度下降一点点
    9. }

    退火详解:

    我们要求解的答案无非是两个:自变量x和对应函数的最大值f(x).       

    那么模拟退火的是怎么做到的呢?【下面车速很快,请系好安全带!】

    1.我们先随机找一点x0,不论是哪个点都可以,随机!(不超过定义域就行)。这个点作为我们的初始值(相当于物体里的一个粒子); 

    2.再找到一点f(x0),来代表x0对应的函数值;

    3.现在正式开始退火:

            刚才我们说了zo相当于是一个粒子,所以我们会进行一个无序运动,也就是向左或者向右随机移动,是的,是随机移动,可能向左,也可能向右,但是请记住一个关键点:移动的幅度和当前的温度T有关。温度T越大,移动的幅度越大。温度T越小,移动的幅度就越小。这是在模拟粒子无序运动的状态。

    4.接受(Accept)更“好”的状态:

            假设我们移动到了x1处,那么这个点对应的f(x1)很明显答案是优于(大于)当前的f(x0)的,因此我们将答案进行更新。也就是将初始值进行替换x0= x1,f(x0)= f(x1)。这是一种贪心的思想。

    5.以一定概率接受(Accept)更差的状态——>这是退火最精彩的部分:
            为什么我们要接受一个更加差的状态呢?因为可能在一个较差的状态旁边会出现一个更加高的山峰。

            那么我们以多少的概率去接受它呢?我们用一个公式表示(这个公式我们只需记住,这是科学家推导出来的结论)︰

                                            

    C++伪代码如下:

    1. double T=2000;//代表开始的温度
    2. double dT=0.99;//代表系数delTa
    3. doubel eps=1e-14;
    4. //用自变量计算函数值,这里可能存在多个自变量对应一个函数值的情况,比如f(x, y)
    5. double func(int x,...){
    6. //这里是对函数值进行计算
    7. double ans=......
    8. return ans;
    9. }
    10. //原始值
    11. double x=rand();
    12. double f=func(x,...);//通过自变量计算出f(x0)的值
    13. while(True){
    14. //-----------------------------------------
    15. //这里是一次退火的操作
    16. //x1可以左右随机移动,幅度和温度T有关,所以*T
    17. //注意这里移动可以左右移动,但是也可以单向移动
    18. double dx=(2*rand()-RAND_MAX)*T;
    19. //让x落在定义域内,如果没在里面,就重新随机。题目有要求就写,否则不写
    20. //======================================
    21. while(x>? || x
    22. double dx=(2*rand()-RAND_MAX)*T;
    23. }
    24. //======================================
    25. //求出f(x1)的值
    26. double df=func(dx);
    27. //这里需要具体问题具体分析,我们要接受更加优秀的情况。可能是df < f(比如求最小值)
    28. if(f
    29. f=df;x=dx;[...,y=dy;]//接受,替换值,如果多个自变量,那么都替换掉
    30. }
    31. //否则概率接受,注意这里的df-f也有具体问题具体分析
    32. else if(exp((df-f)/T)*RAND_MAX>rand()){
    33. f=df;x=dx;[...,y=dy;]//接受,替换值,如果多个自变量,那么都替换掉
    34. }
    35. //-----------------------------------------
    36. T=T*dT;//温度每次下降一点点,dT=0.99
    37. }
    38. //最后输出靠近最优的自变量x的值和函数值f(x)
    39. cout<" "<<f(x)<

  • 相关阅读:
    ubuntu从1804LTS升级至2004LTS
    LeetCode第13题:罗马数字转整数
    企业架构LNMP学习笔记58
    重学 JavaSE 基础
    微信小程序点击icon实现分享功能
    QT简单串口通信终端实现
    机器学习实战(5)——支持向量机
    3.6 空值的处理
    java-性能排查工具
    【博主推荐】html好看的爱心告白源码
  • 原文地址:https://blog.csdn.net/m0_58086930/article/details/126025855