模拟退火算法(SA)的原理理解与应用【python实现】

算法原理

模拟退火算法(Simulated Algorithm,SA)一开始是由Metropolis等人于1953年提出,但在当时未引起反响。1983年Kirkpatrick等人将其应用于组合优化,才得到广泛的应用。算法的基本思想是模拟热力学中的退火过程,其意义是克服了优化过程中陷入局部最优解和初值依赖的弊端,搜索范围与“爬山策略”相比有了很大的提高。下面对原理进行简要阐述:

1.什么是退火?

退火是指将固体加热到足够高的温度,使分子呈随机排列状态,然后逐步降温使之冷却,最后分子以低能状态排列,固体达到某种稳定状态,可以理解为是从高温到低温的过程。用数学语言可描述为:

设热力学系统 S S S 中有 n n n 个有限且离散的状态,其中状态 i i i 的能量为 E i E_i Ei ,在温度 T k T_k Tk 下,经过一段时间达到热平衡,此时处在状态 i i i的概率为
P i ( T k ) = C k ∗ exp ⁡ ( − E i T k ) P_i(T_k)=C_k*\exp(\dfrac{-E_i}{T_k}) Pi(Tk)=Ckexp(TkEi)

又因为处于各个状态的概率之和为1,所以有
∑ j = 1 n P j ( T k ) = 1 ∑ j = 1 n C k ∗ exp ⁡ ( − E i T k ) = 1 C k = 1 ∑ j = 1 n exp ⁡ ( − E i T k ) \sum_{j=1}^{n}P_j(T_k)=1 \sum_{j=1}^{n}C_k*\exp(\dfrac{-E_i}{T_k})=1 C_k=\dfrac{1}{\sum_{j=1}^{n}\exp(\dfrac{-E_i}{T_k})} j=1nPj(Tk)=1j=1nCkexp(TkEi)=1Ck=j=1nexp(TkEi)1
C k C_k Ck代入(1)中,得到玻尔兹曼(Bolzman)方程:
P i ( T k ) = exp ⁡ ( − E i T k ) ∑ j = 1 n exp ⁡ ( − E j T k ) P_i(T_k)=\dfrac{\exp(\dfrac{-E_i}{T_k})}{\sum_{j=1}^{n}\exp(\dfrac{-E_j}{T_k})} Pi(Tk)=j=1nexp(TkEj)exp(TkEi)

2.退火过程与优化问题的对应

组合优化问题退火过程
状态
目标函数能量函数
最优解最低能量状态
基于Metropolis准则搜索等温过程
温度参数下降冷却过程

表中的Metropolis准则为以概率接受新状态,若在温度 T k T_k Tk,当前状态为 i i i,新状态为 j j j,若 E i > E j E_i>E_j Ei>Ej,则无条件接受 j j j为当前状态;若 E i < E j E_i<E_j Ei<Ej,则产生区间[0,1)的随机数,如果概率 P = exp ⁡ − ( E j − E i ) T k P=\exp \dfrac{-(E_j-E_i)}{T_k} P=expTk(EjEi)大于随机数,则仍接受 j j j为当前状态;若小于随机数,则不接受 j j j为当前状态。

3.为什么模拟退火可以找到最优值?

对玻尔兹曼方程进行进一步分析,可以得出如下结论:

  1. 同一温度下,S处在能量小的状态要比处在能量大的状态概率要大。若存在 E 1 < E 2 E_1<E_2 E1<E2,则在同一温度 T k T_k Tk下,有 P 1 ( T k ) P 2 ( T k ) = C k ∗ exp ⁡ ( − E 1 T k ) C k ∗ exp ⁡ ( − E 2 T k ) = exp ⁡ ( E 2 − E 1 T k ) > 1 \dfrac{P_1(T_k)}{P_2(T_k)}=\dfrac{C_k*\exp(\dfrac{-E_1}{T_k})}{C_k*\exp(\dfrac{-E_2}{T_k})}=\exp(\dfrac{E_2-E_1}{T_k})>1 P2(Tk)P1(Tk)=Ckexp(TkE2)Ckexp(TkE1)=exp(TkE2E1)>1
    P 1 ( T k ) > P 2 ( T k ) P_1(T_k)>P_2(T_k) P1(Tk)>P2(Tk),即物体总是大概率趋向于能量较低的方向,求解时搜索范围向目标函数的最小值靠拢。

  2. P i ( T k ) P_i(T_k) Pi(Tk)对温度 T T T求导,可以得到
    ∂ P i ( T k ) ∂ T k = exp ⁡ ( − E i T k ) T k 2 [ ∑ j = 1 n exp ⁡ ( − E i T k ) ] 2 [ ∑ j = 1 n ( E i − E j ) ∗ exp ⁡ ( − E i T k ) ] \dfrac{\partial P_i(T_k)}{\partial T_k}=\dfrac{\exp(\dfrac{-E_i}{T_k})}{T_k^2[\sum_{j=1}^{n}\exp(\dfrac{-E_i}{T_k})]^2}[\sum_{j=1}^{n}(E_i-E_j)*\exp(\dfrac{-E_i}{T_k})] TkPi(Tk)=Tk2[j=1nexp(TkEi)]2exp(TkEi)[j=1n(EiEj)exp(TkEi)]
    由于 exp ⁡ ( − E i T k ) > 0 \exp(\dfrac{-E_i}{T_k})>0 exp(TkEi)>0 T k 2 [ ∑ j = 1 n ∗ exp ⁡ ( − E i T k ) ] 2 > 0 T_k^2[\sum_{j=1}^{n}*\exp(\dfrac{-E_i}{T_k})]^2>0 Tk2[j=1nexp(TkEi)]2>0 exp ⁡ ( − E j T k ) > 0 \exp(\dfrac{-E_j}{T_k})>0 exp(TkEj)>0。若 i ∗ i^* i表示 S S S中唯一的最低能量的状态,则有 E i ∗ − E j < 0 E_{i^*}-E_j<0 EiEj<0,所以 ∂ P i ∗ ( T k ) ∂ T k < 0 \dfrac{\partial P_{i^*}(T_k)}{\partial T_k}<0 TkPi(Tk)<0恒成立。因此, P i ∗ ( T k ) P_{i^*}(T_k) Pi(Tk)是关于温度 T k T_k Tk单调递减。随着降温过程的进行,温度 T k T_k Tk单调递减,搜索区域收敛到目标函数最小值的概率增大。

  3. 当温度 T k T_k Tk很大时, E i T k → 0 \dfrac{E_i}{T_k}\rightarrow 0 TkEi0 P i ( T k ) ≈ 1 n P_i(T_k) \approx \dfrac{1}{n} Pi(Tk)n1,这意味着各状态的概率几乎相等。

    T k → 0 T_k \rightarrow 0 Tk0时, E i T k → ∞ \dfrac{E_i}{T_k}\rightarrow \infty TkEi,这意味着 E i E_i Ei E j E_j Ej的小差别带来 P i ( T k ) P_i(T_k) Pi(Tk) P j ( T k ) P_j(T_k) Pj(Tk)的巨大差别。 T k T_k Tk高,广域搜索, T k T_k Tk低,局域搜索。

算法框架

  1. 选择一个初始点 i i i,给定初始温度 T 0 T_0 T0,终止温度 T f T_f Tf,令 T k = T 0 T_k=T_0 Tk=T0,外循环迭代指标 k = 0 k = 0 k=0
  2. 随机产生邻域解,计算目标值增量 Δ f = f ( j ) − f ( i ) \Delta f = f(j)-f(i) Δf=f(j)f(i)
  3. Δ f < 0 \Delta f<0 Δf<0,则接受新解,令 i = j i=j i=j;否则,产生随机数 ξ ∈ U ( 0 , 1 ) \xi \in U(0,1) ξU(0,1),若 exp ⁡ ( − Δ f T k ) > ξ \exp(\frac{-\Delta f}{T_k})>\xi exp(TkΔf)>ξ,则令 i = j i=j i=j
  4. 若达到热平衡(内循环停止准则),转 S t e p 5 Step 5 Step5;否则,转 S t e p 2 Step 2 Step2
  5. k = k + 1 k=k+1 k=k+1,更新 T k T_k Tk,若达到外循环停止条件,则停止,否则,转 S t e p 2 Step 2 Step2

算法举例与实现过程

问题描述

求下面函数的最小值。
f = 6 x 1 2 + x 1 2 + x 2 2 + 5 sin ⁡ ( x 1 ) + 3 cos ⁡ ( x 2 ) + 50 f=\frac{6x_1}{2+x_1^2+x_2^2}+5\sin(x_1)+3\cos(x_2)+50 f=2+x12+x226x1+5sin(x1)+3cos(x2)+50
其中
− 5 < x 1 < 5 ; − 5 < x 2 < 5 -5<x_1<5;-5<x_2<5 5<x1<5;5<x2<5

问题求解

  1. 选择一个初始解 x 1 , x 2 x_1,x_2 x1,x2,给定初始温度 T 0 T_0 T0,终止温度 T f T_f Tf,令 T k = T 0 T_k=T_0 Tk=T0,外循环迭代次数 i t e r a t i o n 1 = 60 iteration1 = 60 iteration1=60,内循环迭代次数 i t e r a t i o n 2 = 10 iteration2 = 10 iteration2=10
  2. 随机产生邻域解,由于这是一个连续问题,所以在邻域解的生成上,采用生成伪随机数,在某一半径的圆内随机生成的策略;
  3. 计算目标值增量 Δ f = f ( x n e w ) − f ( x n o w ) \Delta f = f(x_{new})-f(x_{now}) Δf=f(xnew)f(xnow),若 Δ f < 0 \Delta f<0 Δf<0,则接受新解,令 x n o w = x n e w x_{now}=x_{new} xnow=xnew;否则,产生随机数 ξ ∈ U ( 0 , 1 ) \xi \in U(0,1) ξU(0,1),若 exp ⁡ ( − Δ f T k ) > ξ \exp(\frac{-\Delta f}{T_k})>\xi exp(TkΔf)>ξ,则令 x n o w = x n e w x_{now}=x_{new} xnow=xnew;否则保持原解。
  4. f ( x n o w ) < f ( x n e w ) f(x_{now})<f(x_{new}) f(xnow)<f(xnew),记录最优解 x b e s t x_{best} xbest;
  5. 若达到热平衡(内循环停止准则),转 S t e p 6 Step 6 Step6;否则,转 S t e p 2 Step 2 Step2
  6. 按照一定比例更新 T k T_k Tk,若达到外循环停止条件,则停止,否则,转 S t e p 2 Step 2 Step2

结果分析

下图是matlab绘制的 f ( x 1 , x 2 ) f(x_1,x_2) f(x1,x2)的函数图像,由图上可以看出问题最优解为41.3205,对应的坐标分别是 ( − 1.62 , 3.04 ) , ( − 1.62 , − 3.04 ) (-1.62,3.04),(-1.62,-3.04) (1.62,3.04),(1.62,3.04)在这里插入图片描述

在这里插入图片描述
由算法求解得到的最优解为:41.3472,此时 ( x 1 , x 2 ) (x_1,x_2) (x1,x2)为 (-1.5449,-3.1257),最优解更新次数与趋势如下图所示。在这里插入图片描述

代码如下。python实现,其中的降温策略、停止策略可根据自身需要改动。以上就是我的理解啦,不当之处欢迎指正!后面会有一篇有关遗传算法(GA)的blog(点击可跳转),也是解决这个问题,大家可以一起对比来看。

import numpy as np
import math
import matplotlib.pyplot as plt


#原函数
def f(x1,x2):
    if((-5<= x1 <=5)and(-5<= x2 <=5)):
        #F = 6*x1/(2 + x1*x1 + x2*x2) + 5*math.sin(x1) +3*math.cos(x2) + 50
        F = (x1+1)**2 + (x2+1)**2 + 10 #测试
        return F
#生成邻域解
def change(x1,x2,x1_low,x1_high,x2_low,x2_high):
    p1 = np.random.uniform(0,1)
    p2 = np.random.uniform(0,1)
    if(p1 < 0.75):
        r = 4
    else:
        r = 8
    x1_new = (p1 - 0.5)*r+x1
    x2_new = (p2 - 0.5)*r+x2
    while((x1_new < x1_low)or(x1_new > x1_high)or(x2_new < x2_low)or(x2_new > x2_high)):
        x1_new = (p1 - 0.5)*0.1+x1
        x2_new = (p2 - 0.5)*0.1+x2
    return x1_new,x2_new

# metropolis 准则接受新解
def metropolis_accept(x1_new,x2_new,F_new,x1_now,x2_now,F_now,Tk):
    if(F_new <= F_now):
        x1_now = x1_new
        x2_now = x1_new
        F_now = F_new
    else:
        difff = (F_new - F_now)
        if(np.exp( - difff / Tk) > np.random.uniform(0.9,0.95)):
            x1_now = x1_new
            x2_now = x1_new 
            F_now = F_new
    return x1_now,x2_now,F_now


def main():#开始退火
    iteration1 = 50 #外循环
    iteration2 = 3 #内循环
    T0 = 1000       #初始温度
    Tf = 1          #最低温度
    Tk = T0         #当前温度
    x1 = 0          #初始解
    x2 = -1
    F_best = f(x1,x2)#最优解
    x1_best = x1
    x2_best = x2
    F_now = F_best   #当前解
    x1_now = x1_best
    x2_now = x2_best
    best_F_all = []
    best_F_all.append(F_now)
    for j in range(iteration1):
        for i in range(iteration2):
            print('----第%s次迭代----'%(j*iteration2 + i + 1))
            x1_new,x2_new = change(x1_now,x2_now,-5,5,-5,5)
            print('(x1_new,x2_new) is (%s,%s)' %(x1_new,x2_new))
            F_new = f(x1_new,x2_new) 
            print('F_new is %s'%F_new)         
            # metropolis 准则接受新解
            x1_now,x2_now,F_now = metropolis_accept(x1_new,x2_new,F_new,x1_now,x2_now,F_now,Tk)
            #更新最优解
            
            if(F_new <= F_best):
                F_best = F_new
                x1_best = x1_new
                x2_best = x2_new
                best_F_all.append(F_best)
        Tk = Tk*0.95
    print('最优解为 : %s,此时(x1,x2)为 (%s,%s)'%(F_best,x1_best,x2_best)) 

    plt.plot(range(1, len(best_F_all)+1), best_F_all)
    plt.xlabel('最优解更新次数',fontproperties='SimHei')
    plt.ylabel('每一代的最优解', fontproperties='SimHei')
    plt.show() 

if __name__ == '__main__':
    main()
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值