模拟退火算法的原理与应用【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)=Ck∗exp(Tk−Ei)
又因为处于各个状态的概率之和为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=1∑nPj(Tk)=1j=1∑nCk∗exp(Tk−Ei)=1Ck=∑j=1nexp(Tk−Ei)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(Tk−Ej)exp(Tk−Ei)
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−(Ej−Ei)大于随机数,则仍接受 j j j为当前状态;若小于随机数,则不接受 j j j为当前状态。
3.为什么模拟退火可以找到最优值?
对玻尔兹曼方程进行进一步分析,可以得出如下结论:
-
同一温度下,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)=Ck∗exp(Tk−E2)Ck∗exp(Tk−E1)=exp(TkE2−E1)>1
故 P 1 ( T k ) > P 2 ( T k ) P_1(T_k)>P_2(T_k) P1(Tk)>P2(Tk),即物体总是大概率趋向于能量较低的方向,求解时搜索范围向目标函数的最小值靠拢。 -
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})] ∂Tk∂Pi(Tk)=Tk2[∑j=1nexp(Tk−Ei)]2exp(Tk−Ei)[j=1∑n(Ei−Ej)∗exp(Tk−Ei)]
由于 exp ( − E i T k ) > 0 \exp(\dfrac{-E_i}{T_k})>0 exp(Tk−Ei)>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=1n∗exp(Tk−Ei)]2>0, exp ( − E j T k ) > 0 \exp(\dfrac{-E_j}{T_k})>0 exp(Tk−Ej)>0。若 i ∗ i^* i∗表示 S S S中唯一的最低能量的状态,则有 E i ∗ − E j < 0 E_{i^*}-E_j<0 Ei∗−Ej<0,所以 ∂ P i ∗ ( T k ) ∂ T k < 0 \dfrac{\partial P_{i^*}(T_k)}{\partial T_k}<0 ∂Tk∂Pi∗(Tk)<0恒成立。因此, P i ∗ ( T k ) P_{i^*}(T_k) Pi∗(Tk)是关于温度 T k T_k Tk单调递减。随着降温过程的进行,温度 T k T_k Tk单调递减,搜索区域收敛到目标函数最小值的概率增大。 -
当温度 T k T_k Tk很大时, E i T k → 0 \dfrac{E_i}{T_k}\rightarrow 0 TkEi→0, P i ( T k ) ≈ 1 n P_i(T_k) \approx \dfrac{1}{n} Pi(Tk)≈n1,这意味着各状态的概率几乎相等。
当 T k → 0 T_k \rightarrow 0 Tk→0时, 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低,局域搜索。
算法框架
- 选择一个初始点 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
- 随机产生邻域解,计算目标值增量 Δ f = f ( j ) − f ( i ) \Delta f = f(j)-f(i) Δf=f(j)−f(i)
- 若 Δ 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。
- 若达到热平衡(内循环停止准则),转 S t e p 5 Step 5 Step5;否则,转 S t e p 2 Step 2 Step2;
- 若 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
问题求解
- 选择一个初始解 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;
- 随机产生邻域解,由于这是一个连续问题,所以在邻域解的生成上,采用生成伪随机数,在某一半径的圆内随机生成的策略;
- 计算目标值增量 Δ 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;否则保持原解。
- 若 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;
- 若达到热平衡(内循环停止准则),转 S t e p 6 Step 6 Step6;否则,转 S t e p 2 Step 2 Step2;
- 按照一定比例更新 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()