模拟退火算法

模拟退火算法

应用

通过模拟物体退火的过程,寻找问题的最优解,可用用来求解函数最值或者旅行商问题(旅行商问题,常被称为旅行推销员问题,是指一名推销员要拜访多个地点时,如何找到在拜访每个地点一次后再回到起点的最短路径)。由于这个算法基于蒙特卡洛方法,所以每次求出的结果可能不一致,只能求出近似的最优值。

原理

简单来说模拟退火算法有点像一个贪心算法,只是贪心算法是将比当前好的解来替代当前的解,直到找到最优解,但这就有容易陷入局部最优解。而模拟退火算法以一定的概率来接受一个比当前解要差的解,因此有可能会跳出这个局部的最优解,达到全局的最优解。
在这里插入图片描述
关于此这篇文章提到了有一个有趣的比喻:

  • 普通算法:兔子朝着比现在低的地方跳去。它找到了不远处的最低的山谷。但是这座山谷不一定最低的。
  • 模拟退火:兔子喝醉了。它随机地跳了很长时间。这期间,它可能走向低处,也可能踏入平地。但是,它渐渐清醒了并朝最低的方向跳去。这就是模拟退火。

过程

  1. 设定一个初始温度,求出当前温度的解 f i f_i fi,这位最优解
  2. 进行降温,再算出现在的解 f i + 1 f_{i+1} fi+1
  3. 如果 f i + 1 f_{i+1} fi+1要比 f i f_i fi更好,接受 f i + 1 f_{i+1} fi+1为最优解;如果 f i + 1 f_{i+1} fi+1要比 f i f_i fi更差,有一定概率接受 f i + 1 f_{i+1} fi+1,这个概率随着温度降低越来越低
  4. 重复以上步骤,知道温度到达最低

粒子在温度 T T T时趋于平衡的概率为 e − Δ E k T e^{\frac{-ΔE}{kT}} ekTΔE,其中 E E E为温度 T T T时的内能, Δ E ΔE ΔE为其改变数,为 E ( x n e w ) − E ( x o l d ) E(x_{new})-E(x_{old}) E(xnew)E(xold) k k k为常数。 M e t r o p o l i s Metropolis Metropolis准则常表示为
p = { e − Δ E k T , Δ E ≥ 0 1 , Δ E &lt; 0 p=\left\{ \begin{aligned} e^{\frac{-ΔE}{kT}} &amp; ,ΔE≥0 \\ 1 &amp; ,ΔE&lt;0 \end{aligned} \right. p={ekTΔE1,ΔE0,ΔE<0
这条公式就表示:温度越高,出现一次能量差为 Δ E ΔE ΔE的降温的概率就越大;温度越低,则出现降温的概率就越小。又由于 Δ E ΔE ΔE总是小于0(因为退火的过程是温度逐渐下降的过程),因此 Δ E k T &lt; 0 \frac{ΔE}{kT} &lt; 0 kTΔE<0 ,所以 P ( Δ E ) P(ΔE) P(ΔE)的函数取值范围是(0,1) 。随着温度T的降低, P ( Δ E ) P(ΔE) P(ΔE)会逐渐降低。

问题

[0 , 0 ], [1 , 10 ], [2 , 7.2 ], [15 , 5 ], [4.3 , 5 ], [12.5, 9 ], [3.4 , 3 ], [6 , 3.4 ], [9 , 6.8 ], [8.1 , 2 ], [15 , 16.5], [13 , 18.8], [11.7, 18.4], [2.9 , 14.7], [9.6 , 20.4]这是几个城市的坐标,现在要如何走才能将这些城市都经过一次且没有重复的路线,要这个距离最短。

python代码

# 模拟退火算法

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

city_id = np.array([[0   , 0   ],
                    [1   , 10  ],
                    [2   , 7.2 ],
                    [15  , 5   ],
                    [4.3 , 5   ],
                    [12.5, 9   ],
                    [3.4 , 3   ],
                    [6   , 3.4 ],
                    [9   , 6.8 ],
                    [8.1 , 2   ],
                    [15  , 16.5],
                    [13  , 18.8],
                    [11.7, 18.4],
                    [2.9 , 14.7],
                    [9.6 , 20.4]])  # 城市坐标

# 计算城市的总长度
def len_dic(city, city_n):
    dic = 0
    for i in range(city_n-1):
        dic += math.sqrt(np.sum(np.power(city[i+1,:]-city[i,:], 2)))
    return dic

# 交换城市位置
def updata(city, city_n):
    i, j = 0, 0
    while True:
        i = math.floor(random.random()*city_n)
        j = math.floor(random.random()*city_n)
        if i != j:
            break  # 随机获取两个城市
    city[[i,j],:] = city[[j,i],:]  # 两行交换
    return city
    
# 准则
def rule(dlt, tmp):
    pr = 0
    if dlt < 0:
        pr = 1
    else:
        p = math.exp(-dlt/tmp)
        if p > random.random():
            pr = 1
        else:
            pr = 0
    return pr

# 处理流程
def SA(city, tmp=1e6, tmp_min=1e-3, alpha=0.95, up_rate=0.75):
    ct_n = len(city_id)  # 城市数量
    city_old = city.copy()
    dic_old = len_dic(city_old, ct_n)
    city_new = city.copy()
    dic_new = dic_old
    
    k = 0  # 循环次数
    road_dic = []
    tmps = []
    tmps.append(tmp)

    while tmp >= tmp_min and k < 5000000:
        city_new = updata(city_old, ct_n)  # 更换路线
        dic_new = len_dic(city_new, ct_n)
        dlt = dic_new - dic_old
        if rule(dlt, tmp) == 1:
            road_dic.append(dic_new)
            city_old = city_new
            dic_old = dic_new
        if dlt < 0:
            tmp = tmp*alpha
            tmps.append(tmp)
        k = k + 1
        if k % 100000 == 0:
            print('循环第'+str(int(k/100000))+'次——本时刻的温度为:'+str(tmp)+',当前最短距离为:'+str(dic_old))  # 记没10W次为1次
    
    return city_old, dic_old, road_dic, temps
            
# 画图
def plot_road(record_length, tmps):
    plt.plot(record_length, label='distance')
    plt.legend()
    plt.show()
    plt.plot(tmps, label='temperature')
    plt.legend()
    plt.show()

cities, dics, road_dic, tmps = SA(city_id)
print('最短距离为:'+str(dics))
print('路径为:')
print(cities)
plot_road(road_dic, tmps)

# 路线
plt.scatter(cities[:,0], cities[:,1], label='city')
for i in range(len(cities)-1):
    plt.plot([cities[i][0], cities[i+1][0]], [cities[i][1], cities[i+1][1]], 'r--')
plt.legend()
plt.show()

结果如下

循环第1次——本时刻的温度为:1.3497229680547014,当前最短距离为:80.35644966452612
循环第2次——本时刻的温度为:1.3497229680547014,当前最短距离为:80.35644966452612
循环第3次——本时刻的温度为:1.3227285086936074,当前最短距离为:76.91166987167794
循环第4次——本时刻的温度为:1.3227285086936074,当前最短距离为:79.6010711793955
循环第5次——本时刻的温度为:1.3227285086936074,当前最短距离为:79.6010711793955
循环第6次——本时刻的温度为:1.2962739385197353,当前最短距离为:78.927264413122
循环第7次——本时刻的温度为:1.2962739385197353,当前最短距离为:78.927264413122
循环第8次——本时刻的温度为:1.2962739385197353,当前最短距离为:78.927264413122
循环第9次——本时刻的温度为:1.2962739385197353,当前最短距离为:78.927264413122
循环第10次——本时刻的温度为:1.2703484597493406,当前最短距离为:78.0900639357398
循环第11次——本时刻的温度为:1.2703484597493406,当前最短距离为:79.24609883302985
循环第12次——本时刻的温度为:1.2703484597493406,当前最短距离为:79.24609883302985
循环第13次——本时刻的温度为:1.2703484597493406,当前最短距离为:79.24609883302985
循环第14次——本时刻的温度为:1.2449414905543539,当前最短距离为:76.3729975348194
循环第15次——本时刻的温度为:1.2200426607432668,当前最短距离为:75.52671953418854
循环第16次——本时刻的温度为:1.2200426607432668,当前最短距离为:75.52671953418854
循环第17次——本时刻的温度为:1.2200426607432668,当前最短距离为:75.52671953418854
循环第18次——本时刻的温度为:1.2200426607432668,当前最短距离为:75.52671953418854
循环第19次——本时刻的温度为:1.2200426607432668,当前最短距离为:75.52671953418854
循环第20次——本时刻的温度为:1.2200426607432668,当前最短距离为:75.52671953418854
循环第21次——本时刻的温度为:1.2200426607432668,当前最短距离为:75.52671953418854
循环第22次——本时刻的温度为:1.2200426607432668,当前最短距离为:75.52671953418854
循环第23次——本时刻的温度为:1.1956418075284014,当前最短距离为:78.05300602025468
循环第24次——本时刻的温度为:1.1956418075284014,当前最短距离为:78.05300602025468
循环第25次——本时刻的温度为:1.1956418075284014,当前最短距离为:78.05300602025468
循环第26次——本时刻的温度为:1.1956418075284014,当前最短距离为:79.13880140384566
循环第27次——本时刻的温度为:1.1956418075284014,当前最短距离为:79.13880140384566
循环第28次——本时刻的温度为:1.1717289713778334,当前最短距离为:78.32208672838217
循环第29次——本时刻的温度为:1.1717289713778334,当前最短距离为:78.32208672838217
循环第30次——本时刻的温度为:1.1717289713778334,当前最短距离为:78.32208672838217
循环第31次——本时刻的温度为:1.1717289713778334,当前最短距离为:78.32208672838217
循环第32次——本时刻的温度为:1.1717289713778334,当前最短距离为:78.38225229736688
循环第33次——本时刻的温度为:1.1717289713778334,当前最短距离为:78.38225229736688
循环第34次——本时刻的温度为:1.1717289713778334,当前最短距离为:78.38225229736688
循环第35次——本时刻的温度为:1.1482943919502766,当前最短距离为:79.90575188198459
循环第36次——本时刻的温度为:1.1482943919502766,当前最短距离为:79.90575188198459
循环第37次——本时刻的温度为:1.1482943919502766,当前最短距离为:79.90575188198459
循环第38次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第39次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第40次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第41次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第42次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第43次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第44次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第45次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第46次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第47次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第48次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第49次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
循环第50次——本时刻的温度为:1.125328504111271,当前最短距离为:70.7238412425356
最短距离为:70.7238412425356
路径为:
[[ 8.1  2. ]
 [13.  18.8]
 [ 9.6 20.4]
 [ 2.   7.2]
 [ 1.  10. ]
 [15.   5. ]
 [ 2.9 14.7]
 [ 0.   0. ]
 [ 3.4  3. ]
 [ 9.   6.8]
 [ 6.   3.4]
 [12.5  9. ]
 [15.  16.5]
 [ 4.3  5. ]
 [11.7 18.4]]

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在多次运行得到过的最小距离大致都在70.几到75.几之间。然鹅事实上最短是多少不知道。模拟退火算法只能求出近似的最优值,而且每次求出的结果可能不一致。从distance这个图我们还是可以看到随着温度下降距离逐渐趋向最小。应用时可以改变参数看能不能得到更好的解。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值