一、什么是模擬退火算法
1、爬山算法
在了解模擬退火算法之前,先來看一下爬山算法:爬山算法是一種貪心算法,該算法每次從當前的解空間中選取一個解作為最優解,直到達到一個局部最優解。假設函數f(x)的圖像如下圖:
現在使用爬山算法來求f(x)的最大值,若C為當前最優解,則爬山算法搜索到A就會停止搜索,這會獲得一個局部最優解,而不是全局最優解。
2、模擬退火算法
繼續考慮尋找f(x)最大值的問題,爬山算法搜索到A點時就會停止搜索,原因是A點左右的值均小於A點的值。模擬退火算法采用的解決辦法是以一定的概率選擇A兩邊的點,盡管A兩邊的點並不是局部最優解,這樣就有一定的概率搜索到D點,從而搜索到B點,最終獲得了全局最優解。
上文中的一定概率來自於固體退火原理:當固體溫度較高時,物質內能較大,固體內部分子運動劇烈;當溫度逐漸降低時,物體內能也隨之降低,分子運動趨於平穩;當固體溫度降到常溫時,固體內部分子運動最終平穩。根據Metropolis准則,粒子在溫度T時趨於平衡的概率為e^(-ΔE/(kT)),其中E為溫度T時的內能,ΔE為其改變量,k為Boltzmann常數。
二、模擬退火算法步驟
三、模擬退火算法流程圖
四、模擬退火算法偽代碼
/*
* J(y):在狀態y時的評價函數值
* Y(i):表示當前狀態
* Y(i+1):表示新的狀態
* r: 用於控制降溫的快慢
* T: 系統的溫度,系統初始應該要處於一個高溫的狀態
* T_min :溫度的下限,若溫度T達到T_min,則停止搜索
*/
while( T > T_min )
{
dE = J( Y(i+1) ) - J( Y(i) ) ;
if ( dE >=0 ) //表達移動后得到更優解,則總是接受移動
Y(i+1) = Y(i) ; //接受從Y(i)到Y(i+1)的移動
else
{
// 函數exp( dE/T )的取值范圍是(0,1) ,dE/T越大,則exp( dE/T )也
if ( exp( -dE/T ) > random( 0 , 1 ) )
Y(i+1) = Y(i) ; //接受從Y(i)到Y(i+1)的移動
}
T = r * T ; //降溫退火 ,0<r<1 。r越大,降溫越慢;r越小,降溫越快
/*
* 若r過大,則搜索到全局最優解的可能會較高,但搜索的過程也就較長。若r過小,則搜索的過程會很快,但最終可能會達到一個局部最優值
*/
i ++ ;
}
五、使用模擬退火算法解決旅行商問題
旅行商問題(TravelingSalesmanProblem,TSP)是一個經典的組合優化問題。經典的TSP可以描述為:一個商品推銷員要去若干個城市推銷商品,該推銷員從一個城市出發,需要經過所有城市后,回到出發地。應如何選擇行進路線,以使總的行程最短。
該問題可以使用模擬退火算法解決,python代碼如下:
import math
import time
import random
# 初始溫度
T = 50000
# 最低溫度
T_end = 1e-8
# 在每個溫度下的迭代次數
L = 100
# 退火系數
delta = 0.98
# 31個城市的坐標
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]]
# 存儲兩個城市之間的距離
d = [[0 for i in range(31)] for j in range(31)]
# 存儲一條路徑
ans = []
# 計算降溫次數
cnt=0
# 計算兩個城市之間的距離
def get_city_distance():
for i in range(len(citys)):
for j in range(i, len(citys)):
d[i][j] = d[j][i] = math.sqrt((citys[i][0]-citys[j][0])**2 + (citys[i][1]-citys[j][1])**2)
# 使用隨機交換路徑中兩個城市的位置來產生一條新路徑
def create_new(a):
i = random.randint(0, len(a)-1)
j = random.randint(0, len(a)-1)
a[i], a[j] = a[j], a[i]
return a
# 獲取路徑的長度
def get_route_distance(a):
dist = 0
for i in range(len(a)-1):
dist+=d[a[i]][a[i+1]]
return dist
def saa():
get_city_distance()
cnt = 0
ans = range(0, len(citys))
t = T
result = 0
while t >= T_end:
for i in range(0, L):
ans_new = create_new(ans)
d1, d2 = get_route_distance(ans), get_route_distance(ans_new)
de = d2 - d1
result = d1
if de<0:
ans = ans_new
result = d2
else:
if(math.e**(-de/T) > random.random()):
ans = ans_new
result = d2
t = t * delta
cnt+=1
print "路徑如下:"
print ans
print "路徑長度:"
print result
print "降溫次數:"
print cnt
start = time.time()
saa()
end = time.time()
print "運行耗時:" + str(end-start) + "s"
輸出:
路徑如下:
[5, 18, 29, 28, 6, 24, 12, 17, 15, 27, 21, 1, 22, 9, 8, 23, 19, 25, 30, 16, 26, 13, 14, 7, 11, 20, 4, 2, 3, 0, 10]
路徑長度:
40605.6088359
降溫次數:
1448
運行耗時:2.93400001526s
六、參考
1、http://www.cnblogs.com/heaad/archive/2010/12/20/1911614.html
2、https://www.cnblogs.com/lyrichu/p/6688459.html