原理
简介
爬山算法:
- 总是将当前位置高度和前后位置高度对比,如果当前位置高度比前后位置都高,那么就认为到达了最优位置,然而他山更比此山高,这个位置很可能只是局部最优(一座小山)
- 好比你来到了世界上最矮的山-静山之巅,左右四顾已经没有比静山之巅更高的地方
- 静山位于山东寿光市西南8公里,孙家集街道云马家庄之间,海拔高度0.6米
进一步:
- 如果你站在静山之巅能够随性远行,离开局部最优,你会发现山东还有泰山,中国还有珠穆朗玛峰
- 模拟退火算法内部就含有这种随机性,在某一温度下的迭代过程中,分子的能量可能比上次迭代时能量高,但仍有一定概率接收这次迭代的结果,从而跳出局部最优,走向全局最优
模拟退火算法:
- 来自于金属高温冶炼过程中,分子能量随着温度降低而降低的过程,分子的随机移动范围也随着温度降低而减少,分子的移动具有随机性,最终温度降到一定程度,分子随机移动范围减少到一定程度,分子能量降到一定程度
- 由于分子移动的随机性,使得分子到达局部最优能量后,仍然能够有一定概率跳出局部最优,最终达到全局最优
分子扰动
已知:
- 温度越高分子随机移动的范围越大
- 位于绝对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?=k?T+b?代入0=k?(?273.15)+b?yrange?=k(273.15+T) -
k
k
k控制移动范围
-
T
T
T当前温度
代码
步骤
- 初始温度;初始化种群,初始化分子(个数,多维能量);计算初始能量
- 两层循环,外层控制温度,内层控制此温度下的迭代次数; 邻域内随机扰动x,产生新解(温度越高,分子运动的范围越大,-273.15是绝对0度,分子不再运动),再计算新能量
- 判断新能量<原能量,采纳新能量分子(新解);新能量>原能量使用metropolis principle采纳新能量分子(新解)
- 记录每个温度每次内部迭代下的最小能量和最小能量分子
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 = {}
T = self.T0
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:
for num in range(self.inner_iter_num):
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:
p = math.exp(-(E_news[m] - Es[m]) / T)
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)
|