算法实践-模拟退火(SA)

算法实践-模拟退火(SA)

模拟退火(SA)-原理简介

1983年,模拟退火算法(SA)被Kirkpatrick等首次提出。

‘退火’是物理学术语,指对物体加温在冷却的过程。我们知道在分子和原子的世界中,能量越大,意味着分子和原子越不稳定,当能量越低时,原子越稳定。模拟退火算法来源于晶体冷却的过程,如果固体不处于最低能量状态,给固体加热再冷却,随着温度缓慢下降,固体中的原子按照一定形状排列,形成高密度、低能量的有规则晶体,对应于算法中的全局最优解。而如果温度下降过快,可能导致原子缺少足够的时间排列成晶体的结构,结果产生了具有较高能量的非晶体,这就是局部最优解。

SA的搜索策略简单来说就是:刚开始处于高温,搜索范围宽广,有更高概率搜索到高质量解,同时每一次搜索的计算成本也很高;随着温度的下降,搜索范围不断收窄,每一次搜索的效率也提高,最后温度达到退火温度后结束搜索。

模拟退火(SA)的流程图:
在这里插入图片描述

Metropolis:
在这里插入图片描述

SA实践

为了形象的理解,这里还是以一个具体的函数求解为例。问题如下:

f ( x ) = 4 x sin ⁡ ( 10 x ) − 5 x cos ⁡ ( 3 x ) , 0 < x < 10 f(x) = 4x\sin (10x) - 5x\cos (3x),0 < x < 10 f(x)=4xsin(10x)5xcos(3x),0<x<10,求函数在定义域内极小值。函数图像如下:
在这里插入图片描述

python 实现代码如下:

import numpy as np
import matplotlib.pyplot  as plt

class SA():
    def __init__(self,T_init,T_min,count):
        self.T_init = T_init # 初始化温度
        self.Tmin = T_min    # 退火温度
        self.T = T_init      # 动态的温度变化
        self.count = count   # 每个温度下的循环次数
        self.t = 0           # 温度变化次数

    def y_fun(self,x):
        """
        目标函数 : f(x)=4*x*sin(10x)-5*x*cos(3x),0<x<10
        """
        x=np.array(x)
        return 4*x*np.sin(10*x)-5*x*np.cos(3*x)


    def generate(self,x,interval):
        """
        根据当前温度随机产生一个新的解
        input: x, 初始解,默认为0
               interval,定义域范围
        output: xNew , 新的解
        """
        xNew = x + np.random.uniform(low=-0.1,high=0.1)*self.T  # 新解的搜索范围随着温度下降而减小
        if xNew < interval[0] or xNew > interval[1] :    #如果超出边界就在定义域内随机搜索
            xNew = np.random.uniform(low=interval[0],high=interval[1])

        return xNew

    def SA_process(self,x,interval):
        """
        input: x, 初始解,默认为0
               interval,定义域范围
        output: sol,所有搜索过的解
        """
        sol = []
        while self.T >= self.Tmin:
            for i in range(self.count):
                y=self.y_fun(x)
                xNew = self.generate(x,interval)  # 在x的邻域搜索解,范围需要被指定
                if (interval[0]<=xNew and xNew<=interval[1]):
                    yNew=self.y_fun(xNew)
                    if yNew-y<0:
                        x=xNew
                    else:   #以一定概率接受劣解
                        p = np.exp(-(yNew-y)/self.T) # metropolis principle
                        r = np.random.uniform(low=0,high=1)
                        if r < p:
                            x=xNew
            sol.append(x)     
            plt.plot(x,self.y_fun(x),'*')   
            self.t+=1                        # 温度循环次数加1
            self.T=self.T_init/(1+self.t)    # 降温模式
            print('迭代次数 :',self.t)
        return sol

    def f_plot(self):
        """绘制函数图像"""
        x= np.array(range(1000))/100
        y = self.y_fun(x) 
        plt.plot(x,y)


if __name__ == "__main__":
    x = np.random.uniform(low=0,high=10)   # 初始化x
    Sa_example = SA(T_init=500,T_min=2,count=100) # 初始温度500,结束温度2度,每次迭代100次
    sol = Sa_example.SA_process(x,[0,10])  
    Sa_example.f_plot()
    print("函数最小值:",np.min(Sa_example.y_fun(sol)))
    plt.show()

总结

模拟退火算法的应用很广泛,可以用于求解NP完全问题,但其参数依赖人工经验设置,其主要问题有以下三点:

(1) 初始状态设置。

初始温度T和退火温度T_min的设置是影响模拟退火算法全局搜索性能的重要因素之一,初始温度高,温度变化范围大,则搜索到全局最优解的可能性大,但因此要花费大量的计算时间;反之,则可节约计算时间,但全局搜索性能可能受到影响。初始温度的设置一般需要依据实验结果进行若干次调整。

(2) 退火策略设计。

退火速度即温度从初始温度T变化到退火温度T_min的速度,与T和T_min相关。模拟退火算法的全局搜索性能也与退火速度密切相关。一般来说,同一温度下的“充分”搜索(退火)是相当必要的,但这需要计算时间。在实际应用中,针对具体问题的性质和特征设置合理的降温条件是主要问题之一,即从温度T降到T_min过程中是降温方式、以及需要降温的次数?
常用的降温方式:
T ( n ) = λ T ( n ) , λ < 1 T(n) = \lambda T(n),\lambda < 1 T(n)=λT(n),λ<1

T ( n ) = T ( 0 ) log ⁡ ( 1 + t ) T(n) = \frac{{T(0)}}{{\log (1 + t)}} T(n)=log(1+t)T(0),t为当前温度

T ( n ) = T ( 0 ) 1 + t T(n) = \frac{{T(0)}}{{1 + t}} T(n)=1+tT(0),t为当前温度

(3) 状态产生函数的设计
简单来说就是解的搜索范围,每一次搜索产生新解时,如何确定搜索范围和边界?

(4)避免迂回搜索
盲目的随机搜索效率低,容易重复搜索,如何提高搜索效率?

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值