模拟退火
https://mp.weixin.qq.com/s?src=11×tamp=1633564978&ver=3359&signature=oeLMbNZi60Gx-baHgTPJS4QIeh8PLL8Nheac1YVk4WDZnidYsaoJp2MMfodUApT1ndZxC35VP87Ks3TGHZA*bfzejguOpSKZ5cqX6XGUnM–AUQqrr50Hprkf8Qy&new=1
1.金属退火的原理
金属退火是将金属加热到一定温度,保持足够时间,然后以适宜速度冷却(通常是缓慢冷却,有时是控制冷却)的一种金属热处理工艺。模拟退火算法来源于固体退火原理,将固体加温至充分高,再让其徐徐冷却,加温时,固体内部粒子随温度升高变为无序状,内能增大;而徐徐冷却时粒子渐趋有序,在每个温度都达到平衡态,最后在常温时达到基态,内能减为最小。
2.模拟退火核心思想
模拟退火算法从某一较高初温出发,伴随温度参数的不断下降,结合一定的概率突跳特性在解空间中随机寻找目标函数的全局最优解,即在局部最优解能概率性地跳出并最终趋于全局最优。
这里的“一定的概率”的计算参考了金属冶炼的退火过程,这也是模拟退火算法名称的由来。将温度T当作控制参数,目标函数值f视为内能E,而固体在某温度T时的一个状态对应一个解,然后算法试图随着控制参数T的降低,使目标函数f(内能E)也逐渐降低,直至趋于全局最小值(退火中低温时的最低能量状态)。
3.模拟退火数学原理
从上面我们知道,会结合概率突跳特性在解空间中随机寻找目标函数的全局最优解,那么具体的更新解的机制是什么呢?如果新解比当前解更优,则接受新解,否则基于Metropolis准则判断是否接受新解。接受概率为:
y = { 1 E t + 1 < E t e − ( E t + 1 − E t ) k T E t + 1 >= E t y = \left\{ \begin{array}{ll} 1 & \textrm{$E_{t+1}$<$E_{t}$}\\ e^{-\frac{(E_{t+1}-E_{t})}{kT}} & \textrm{$E_{t+1}$>=$E_{t}$} \end{array} \right. y={1e−kT(Et+1−Et)Et+1<EtEt+1>=Et
如上公式,假设当前时刻搜索的解为 x t x_{t} xt,对应的系统能量(目标函数)为 E t E_{t} Et,对搜索点施加随机扰动,产生新解 x t + 1 x_{t+1} xt+1,相应地,系统能量为 E t + 1 E_{t+1} Et+1,那么系统对搜索点从 x t x_{t} xt到 x t + 1 x_{t+1} xt+1转变的接受概率就为上公式。
4.模拟退火的流程
算法实质分两层循环,在任一温度水平下,随机扰动产生新解,并计算目标函数值的变化,决定是否被接受。由于算法初始温度比较高,这样,使E增大的新解在初始时也可能被接受,因而能跳出局部极小值,然后通过缓慢地降低温度,算法就最终可能收敛到全局最优解,具体流程为:
TSP
https://www.cnblogs.com/larryking/p/5734459.html
TSP问题(Traveling Salesman Problem,旅行商问题),由威廉哈密顿爵士和英国数学家克克曼T.P.Kirkman于19世纪初提出。
1.问题描述如下:
有若干个城市,任何两个城市可以互通,且城市之间的距离都是确定的。现要求一旅行商从某城市出发必须经过每一个城市且只在一个城市逗留一次,最后回到出发的城市,如何确定一条最短的线路以保证其旅行的费用最少。
2.数学模型表示:
https://blog.csdn.net/u011561033/article/details/93405538
建立一个赋权无向完全图 G = ( C , X , D ) G=(C, X, D) G=(C,X,D),其中城市集合 C = { c 1 , c 2 , ⋅ ⋅ ⋅ , c n } C=\{c_{1}, c_{2}, ···, c_{n}\} C={c1,c2,⋅⋅⋅,cn},集合 X = { x i j ∣ i , j = 0 , 1 , ⋅ ⋅ ⋅ , n } X=\{x_{ij}| i, j=0,1, ···, n\} X={xij∣i,j=0,1,⋅⋅⋅,n}, x i j x_{ij} xij表示是否搭建城市 c i c_{i} ci 到城市 c j c_{j} cj 的边,距离集合 D = { d i j ∣ i , j = 0 , 1 , ⋅ ⋅ ⋅ , n } D=\{d_{ij}| i, j=0,1, ···, n\} D={dij∣i,j=0,1,⋅⋅⋅,n}, d i j d_{ij} dij表示城市 c i c_{i} ci 到城市 c j c_{j} cj 的距离。
目标函数:
求解一条路径,使得
m i n ∑ i = 1 n ∑ j = 1 n ( x i j ∗ d i j ) min \sum_{i=1}^{n} \sum_{j=1}^{n}(x_{ij}*d_{ij}) mini=1∑nj=1∑n(xij∗dij)
约束条件:
∑ j = 1 n x i j = 1 i = 0 , 1 , ⋅ ⋅ ⋅ , n ∑ i = 1 n x i j = 1 j = 0 , 1 , ⋅ ⋅ ⋅ , n ∀ k 阶 主 子 式 ( k = 1 , ⋅ ⋅ ⋅ , n − 1 ) r ( k 阶 主 子 式 ) < k \sum_{j=1}^{n} x_{ij}=1\quad i=0,1, ···, n \\ \sum_{i=1}^{n} x_{ij}=1 \quad j=0,1, ···, n\\ \forall k阶主子式(k=1, ···, n-1) \quad r(k阶主子式)
所有城市经过且只经过一次,并且构成一个环,因此任一城市的出度等于入度等于1,即需要满足如下前两个约束条件。其中, r ( A ) r(A) r(A)表示矩阵A的秩,约束条件三表示最优路径中不存在 k k k个城市形成的子环。
补充知识:
定义1 k k k阶子式
在 m ∗ n m*n m∗n矩阵中,任取 k k k行与 k k k列 ( k ≤ m , k ≤ n ) (k\leq m,k\leq n) (k≤m,k≤n),位于这些行列交叉处的 k 2 k^{2} k2个元素,不改变它们在矩阵中的相对位置次序而得到的 k k k阶行列式,称为矩阵的 k k k阶子式.
定义2 k k k阶主子式
在 m ∗ n m*n m∗n矩阵中,任取 k k k行与相应的 k k k列 ( k ≤ m , k ≤ n ) (k\leq m,k\leq n) (k≤m,k≤n),位于这些行列交叉处的 k 2 k^{2} k2个元素,不改变它们在矩阵中的相对位置次序而得到的 k k k阶行列式,称为矩阵的 k k k阶主子式.
模拟退火解决TSP问题
1.问题分析
2.算法流程
3.python代码
https://github.com/sunxueliang96/Machine-learning-stuffs/tree/master/%E8%AE%A1%E7%AE%97%E6%99%BA%E8%83%BD%E7%9B%B8%E5%85%B3%E7%AE%97%E6%B3%95
import pandas as pd
import numpy as np
import matplotlib.pylab as plt
import math
citys = [[1304, 2312], [3639, 1315], [4177, 2244], [3712, 1399], [3488, 1535], [3326, 1556], [3238, 1229], [4196, 1004],
[4312, 790], [4386, 570], [3007, 1970], [2562, 1756], [2788, 1491], [2381, 1676], [1332, 695], [3715, 1678],
[3918, 2179], [4061, 2370], [3780, 2212], [3676, 2578], [4029, 2838], [4263, 2931], [3429, 1908], [3507, 2367],
[3394, 2643], [3439, 3201], [2935, 3240], [3140, 3550], [2545, 2357], [2778, 2826], [2370, 2975]]
a = pd.DataFrame(citys)
l = len(citys)
# 计算任意两地之间的距离,得到31*31的矩阵
def dist(x1, y1, x2, y2):
return np.sqrt(np.power(x1 - x2, 2) + np.power(y1 - y2, 2))
# c为距离矩阵
b = []
[b.append(dist(citys[i][0], citys[i][1], citys[j][0], citys[j][1])) for i in range(l) for j in range(l)]
c = np.reshape(b, (l, l))
# 初始路径为0-1-2-3-···-30
global path
path = list(range(l))
# 计算转移概率 Metropolis准则以一定的概率接受恶化解,从而使算法具有逃脱局部极值和避免过早收敛的全局优化能力
def possibilities(t, d_old, d_new):
if d_new < d_old:
return 1
elif (d_new == d_old):
return 0
else:
return math.exp((d_old - d_new) / t)
def get_random():
return np.random.uniform(low=0, high=1, size=1)[0]
def sum_path(path): # 计算总路程
sum = 0
for i in range(31):
sum = sum + c[path[i], path[(i + 1) % 31]]
return sum
def get_new_path(list_a): # 用二交换法产生一条新路径
random_numbers = np.random.randint(low=0, high=31, size=2)
u, v = np.sort(random_numbers)
list_b = list_a.copy()
list_b[u], list_b[v] = list_b[v], list_b[u]
return list_b
def plot(path): # 绘制一次路径
a.plot(x=0, y=1, kind='scatter', color='b')
x = []
y = []
for i in range(l + 1):
x.append(citys[path[i % 31]][0])
y.append(citys[path[i % 31]][1])
plt.plot(x, y, color='r')
if __name__ == '__main__':
t = 150
s = 0
T = []
while True:
flag = False
for i in range(20000):
fi = sum_path(path)
new_path = get_new_path(path)
fj = sum_path(new_path)
rand = get_random()
poss = possibilities(t, fi, fj)
if rand < poss:
path = new_path
flag = True
t = t * 0.9
T.append(fi)
print('T:', T)
if (flag == False):
s += 1
else:
s = 0
if s == 2:
print(path)
break
plot(path)
plt.show()
4.运行结果
程序求解的最佳路径:
[18, 22, 15, 3, 7, 8, 9, 1, 6, 4, 5, 10, 12, 11, 13, 14, 0, 28, 30, 29, 26, 27, 25, 21, 20, 19, 24, 23, 17, 2, 16, 18]
闭环路径总长度:15754.841456197199