一种随机搜索算法,跟遗传算法差不多,简单看看吧

 

通俗解释

假设有下面这样一个函数,现在想求函数的(全局)最优解(最小值)。

如果采用Greedy策略,那么从A点开始试探,如果函数值继续减少,那么试探过程就会继续。而当到达点B时,显然我们的探求过程就结束了(因为无论朝哪个方向努力,结果只会越来越大)。最终我们只能找到一个局部最后解B。这就是爬山算法

模拟退火其实也是一种Greedy算法,但是它的搜索过程引入了随机因素。模拟退火算法以一定的概率来接受一个比当前解要差的解,因此有可能会跳出这个局部的最优解,达到全局的最优解。

以下图为例,模拟退火算法在搜索到局部最优解B后,会以一定的概率接受向右继续移动。也许经过几次这样的不是局部最优的移动后会到达B 和C之间的峰点,于是就跳出了局部最小值B    【一旦到达BC间的峰点,也就意味着跳出局部最优B,因为它向右会一直减小,最终到达C点】

 

 

算法评价

缺点:模拟退火算法不一定能找到全局最优;

优点:搜索效率较高

 

示例

整段代码主要看 solve 方法,这个方法才是 模拟退火,其他都是配料,solve 的思路就是 给 x 一个 很小的扰动,看看 扰动前,扰动后 哪个好,然后以 一定概率 接受 那个不好的解,否则接受更好的解,就这么简单

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


class SA(object):

    def __init__(self, interval, tab='min', T_max=10000, T_min=1, iterMax=1000, rate=0.95):
        self.interval = interval                                    # 给定状态空间 - 即待求解空间 x的范围
        self.T_max = T_max                                          # 初始退火温度 - 温度上限
        self.T_min = T_min                                          # 截止退火温度 - 温度下限
        self.iterMax = iterMax                                      # 定温内部迭代次数
        self.rate = rate                                            # 退火降温速度
        #############################################################
        self.x_seed = random.uniform(interval[0], interval[1])      # 解空间内的种子,初始解
        self.tab = tab.strip()                                      # 求解最大值还是最小值的标签: 'min' - 最小值;'max' - 最大值
        #############################################################
        self.solve()                                                # 完成主体的求解过程
        self.display()                                              # 数据可视化展示

    def solve(self):
        temp = 'deal_' + self.tab                                   # 采用反射方法提取对应的函数
        if hasattr(self, temp):
            deal = getattr(self, temp)
        else:
            exit('>>>tab标签传参有误:"min"|"max"<<<')

        x1 = self.x_seed    # 随机产生初始解
        T = self.T_max
        
        # if 1:
        # for _ in range(10000):    # 这样也可以得到最优解
        while T >= self.T_min:    # T 和 x y 没有任何关系,其实就是指定了迭代次数
            
            for i in range(self.iterMax):
                f1 = self.func(x1)
                delta_x = random.random() * 2 - 1   # [-1, 1] 之间的随机数
                if x1 + delta_x >= self.interval[0] and x1 + delta_x <= self.interval[1]:   # 将随机解束缚在给定状态空间内
                    x2 = x1 + delta_x
                else:
                    x2 = x1 - delta_x
                f2 = self.func(x2)
                delta_f = f2 - f1
                x1 = deal(x1, x2, delta_f, T)       # deal = deal_min or deal_max
                
            T *= self.rate
        self.x_solu = x1                                            # 提取最终退火解

    def func(self, x):                                              # 状态产生函数 - 即待求解函数
        value = np.sin(x**2) * (x**2 - 5*x)
        return value

    def p_min(self, delta, T):                                      # 计算最小值时,容忍解的状态迁移概率
        # 产生一个概率,想简单点就设置为固定值
        probability = np.exp(-delta/T)
        return probability

    def p_max(self, delta, T):
        # 产生一个概率,想简单点就设置为固定值
        probability = np.exp(delta/T)                               # 计算最大值时,容忍解的状态迁移概率
        return probability

    def deal_min(self, x1, x2, delta, T):
        if delta < 0:                                               # 更优解
            return x2
        else:                                                       # 容忍解
            P = self.p_min(delta, T)
            if P > random.random(): return x2
            else: return x1

    def deal_max(self, x1, x2, delta, T):
        if delta > 0:                                               # 更优解
            return x2                   # 如果是更优解,就返回 x2
        else:                                                       # 容忍解
            P = self.p_max(delta, T)    # 如果不是更优解,随机返回 x1 或者 x2
            if P > random.random(): return x2
            else: return x1

    def display(self):
        print('seed: {}\nsolution: {}'.format(self.x_seed, self.x_solu))
        # plt.figure(figsize=(6, 4))

        x = np.linspace(self.interval[0], self.interval[1], 300)
        y = self.func(x)

        plt.plot(x, y, 'g-', label='function')
        plt.plot(self.x_seed, self.func(self.x_seed), 'bo', label='seed')
        plt.plot(self.x_solu, self.func(self.x_solu), 'r*', label='solution')

        plt.title('solution = {}'.format(self.x_solu))
        plt.xlabel('x'); plt.ylabel('y')
        plt.legend()
        # plt.savefig('SA.png', dpi=500)
        plt.show()
        plt.close()


if __name__ == '__main__':
    SA([-5, 5], 'max')

输出

 

特点

模拟退火算法 经常与其他算法 结合使用,用于 跳出 局部最优 

 

 

 

参考资料:

http://blog.chinaunix.net/uid-9162199-id-5840190.html  模拟退火算法(Simulated Annealing,SA)的全面讲解及python实现

https://www.cnblogs.com/xxhbdk/p/9192750.html  模拟退火算法(1) - Python实现

https://zhuanlan.zhihu.com/p/47375952  模拟退火算法从原理到实战【基础篇】

https://www.cnblogs.com/heaad/archive/2010/12/20/1911614.html  大白话解析爬山算法、模拟退火算法