启发式算法/模拟退火

原理

简介

爬山算法:

  • 总是将当前位置高度和前后位置高度对比,如果当前位置高度比前后位置都高,那么就认为到达了最优位置,然而他山更比此山高,这个位置很可能只是局部最优(一座小山)
  • 好比你来到了世界上最矮的山-静山之巅,左右四顾已经没有比静山之巅更高的地方
  • 静山位于山东寿光市西南8公里,孙家集街道云马家庄之间,海拔高度0.6米
    启发式算法/模拟退火_第1张图片

进一步:

  • 如果你站在静山之巅能够随性远行,离开局部最优,你会发现山东还有泰山,中国还有珠穆朗玛峰
  • 模拟退火算法内部就含有这种随机性,在某一温度下的迭代过程中,分子的能量可能比上次迭代时能量高,但仍有一定概率接收这次迭代的结果,从而跳出局部最优,走向全局最优

模拟退火算法:

  • 来自于金属高温冶炼过程中,分子能量随着温度降低而降低的过程,分子的随机移动范围也随着温度降低而减少,分子的移动具有随机性,最终温度降到一定程度,分子随机移动范围减少到一定程度,分子能量降到一定程度
  • 由于分子移动的随机性,使得分子到达局部最优能量后,仍然能够有一定概率跳出局部最优,最终达到全局最优

分子扰动

已知:

  • 温度越高分子随机移动的范围越大
  • 位于绝对0度-273.15度时,分子停止移动
    y r a n g e = k ⋅ T + b ⟹ 代 入 0 = k ⋅ ( − 273.15 ) + b y r a n g e = k ( 273.15 + T ) y_{range}=k\cdot T+b \stackrel{代入0=k \cdot (-273.15)+b}{\Longrightarrow} y_{range}=k(273.15+T) yrange=kT+b0=k(273.15)+byrange=k(273.15+T)
  • k k k控制移动范围
  • T T T当前温度

代码

步骤

  1. 初始温度;初始化种群,初始化分子(个数,多维能量);计算初始能量
  2. 两层循环,外层控制温度,内层控制此温度下的迭代次数; 邻域内随机扰动x,产生新解(温度越高,分子运动的范围越大,-273.15是绝对0度,分子不再运动),再计算新能量
  3. 判断新能量<原能量,采纳新能量分子(新解);新能量>原能量使用metropolis principle采纳新能量分子(新解)
  4. 记录每个温度每次内部迭代下的最小能量和最小能量分子
import math
import numpy as np
import matplotlib
import matplotlib.pyplot as plt

matplotlib.rcParams['font.family'] = 'STSong'
matplotlib.rcParams['font.size'] = 10


def Energy_F1(x):
    """
    能量函数
    目标函数值作为E(energy),fitness
    求目标函数最小值,因此E越小越好
    :param x: 解
    :return:
    """
    return np.sum(x ** 2)


class SimulatedAnnealing(object):
    """
    模拟退火
    """

    def __init__(self, inner_iter_num, E_end, x_min, x_max, NP, D, T0, T_end, tr, kr):
        """
        初始化
        :param inner_iter_num: 每个T温度下的迭代次数,内部迭代次数
        :param E_end: 截止能量,截止适应度
        :param x_min: 下边界
        :param x_max: 上边界
        :param NP(number population): 种群大小
        :param D(dimension): 解的维度
        :param T0: temperature0, 初始温度
        :param T_end: 截止温度
        :param tr: 温度下降速率
        :param kr: 分子扰动率,控制分子移动范围,与T0和绝对0度-273.15度有关
        """
        # 截止条件
        self.inner_iter_num = inner_iter_num
        self.E_end = E_end
        # 目标空间
        self.x_min = x_min
        self.x_max = x_max
        # 求解者
        self.NP = NP
        self.D = D
        # 算法参数
        self.T0 = T0
        self.T_end = T_end
        self.tr = tr
        self.kr = kr

    def cooling(self):
        """
        降温过程
        :return: E_list,元素{(T,i):E}={(当前温度,当前温度下迭代次数):当前最小能量}
        """
        # 记录
        idx_min = None
        E_dict = {}
        # 1. 初始温度,初始化种群,初始化分子(个数,多维能量)
        T = self.T0
        # x.shape=(NP,D)
        x = np.random.uniform(self.x_min, self.x_max, (self.NP, self.D))
        # 初始能量,所有列按行取
        Es = [Energy_F1(one) for one in x[..., :]]
        while T > self.T_end:
            # inner_iter_num 内部迭代次数
            for num in range(self.inner_iter_num):
                # 邻域内随机扰动x,产生新解
                # 温度越高,分子运动的范围越大,-273.15是绝对0度,分子不再运动
                k = np.random.rand() * self.kr
                y_range = k * (273.15 + T);
                x_new = x + np.random.uniform(-y_range, y_range, (self.NP, self.D))
                # 保证在解空间内
                for m in range(self.NP):
                    for n in range(self.D):
                        while x_new[m, n] < self.x_min or x_new[m, n] > self.x_max:
                            x_new[m, n] = x[m, n] + np.random.uniform(-0.055, 0.055) * T
                # 分子移动后新的能量
                E_news = [Energy_F1(one) for one in x_new[..., :]]
                for m in range(self.NP):
                    if E_news[m] < Es[m]:
                        x[m, :] = x_new[m, :]
                    else:
                        # metropolis principle
                        p = math.exp(-(E_news[m] - Es[m]) / T)
                        # r属于[0,1)
                        r = np.random.rand()
                        if r < p:
                            x[m, :] = x_new[m, :]
                    # 记录新的能量
                    Es[m] = E_news[m]
                # 记录每次迭代的最小能量
                E_min = min(Es)
                idx_min = Es.index(E_min)
                E_dict[(T, num)] = E_min
                print("温度T=", T, " 内部迭代次数=", num + 1, " 能量=", E_min)
                # 达到截止能量退出
                if E_min < self.E_end:
                    return x[idx_min, :], E_dict
            # 温度
            T = self.tr * T
        pass
        return x[idx_min, :], E_dict

    def show(self, x_best, E_dict):
        """
        展示
        :param x_best: 最优解
        :param E_dict: 最低能量数组
        :return:
        """
        print("最优分子:", str(x_best))
        print("最优解:", str(list(E_dict.values())[-1]))

        plt.title("迭代过程")
        plt.xlabel("迭代次数")
        plt.ylabel("能量E")
        x = range(1, len(E_dict) + 1)
        y = list(E_dict.values())
        plt.plot(x, y, label="SA")
        plt.legend()
        plt.show()


if __name__ == '__main__':
    sa = SimulatedAnnealing(10, 1e-4, -30, 30, 50, 20, 1000, 10, 0.9, 0.001)
    x_best, E_dict = sa.cooling()
    sa.show(x_best, E_dict)

你可能感兴趣的:(启发式算法,算法,启发式算法,python)