模擬退火
算法簡介
模擬退火算法是一種基於蒙特卡洛思想設計的近似求解最優化問題的方法。
它是一種適合解大規模組合優化問題的通用有效近似算法,是局部搜索算法的擴展,從理論上來說它是一個能求得全局最優化結果的算法,它是一種啓發式算法,源於對固體退火的研究。它是利用問題的求解過程與融化物體退火過程的相似性,採用隨機模擬物體退火過程來完成問題的求解,也就是在控制溫度的作用下對參數的值進行調整,知道所選取的參數值最終使能量函數(目標函數)達到全局最小值。
思想
既然模擬退火算法思想源於固體退火,那我們簡單瞭解一下固體退火。
- 退火:退火是指將固體加熱到非常高的溫度之後,讓其中的分子成隨機排列狀態,然後以一定的速率逐步降溫,其中在降溫的過程中每個狀態下分子都會隨機運動,因此在這個過程中會出現很多種不同的排列狀態,當固體緩慢冷卻之後分子往往能以低能狀態排列,使得固體內部達到內能最小的穩定狀態。
因此類別固體退火,模擬退火就是指從某一初始溫度開始,讓溫度以某一適宜速率緩慢下降,同時結合概率突跳特性在目標函數的解空間內隨機尋找全局最優解。
模擬退火算法和其它的搜索算法相比有一些特點:
1.以一定的概率接受惡化解。
我們知道,一些傳統的隨機搜索算法往往在搜索過程中只接受是目標函數解**變“好”**的解,這樣在很多情況下總會不可避免的陷入局部最優解,而對模擬退火而言,它會以一定的概率接受惡化解,使得它具有更強的靈活性,可以跳出局部最優解。
如上圖,假設圖中的藍色曲線爲目標函數,當用模擬退火算法搜索最優解時,假設初始點爲A,在搜索過程中它可能很容易就搜索到了C點,但顯然這只是個局部最優解,能夠以一定概率接受惡化解的選擇使得它將可能跳出這個局部最優解進而繼續搜索到全局最優G點。
2.引進了算法控制參數
模擬退火引入了類似於退火溫度的算法控制參數,它將優化過程分爲幾個階段,並決定各個階段下的取捨標準,它依據的是Metroplis準則,具體來說是什麼意思呢,我們看一下Metroplis準則公式:
其中,Xnew
爲得到的新解,Xold
爲當前解,E()
爲目標函數法則,上面這個公式是什麼意思呢?簡單來說,**就是在某個溫度下固體分子從一個狀態轉移到另一個狀態時(後面會講到某個溫度下轉移狀態的次數爲馬爾科夫鏈的長度,也就是每個溫度下不止有一個狀態),如果新狀態的內能小,則無條件直接接受新解,而如果新狀態的內能小,則以一定概率接受它。這個一定概率滿足的條件就是指圖中的那個公式。**經過對這個公式的分析我們發現,當新解沒有當前解好的時候,接受的概率主要跟兩解的差值和當前溫度有關,我們來分析一下,假如當前溫度是固定的,那麼差值越小那麼加上負號的分子就越大,總體接受的概率就越大,假如差值一定,那麼當前溫度越小,分數越大,加上符號就越小,總體接受的概率就越小,也就是說,差值越小我越可能接受你,而當前溫度越高,我可以搜索的時間就比較充裕也越可能接受新解,當前溫度越低,就越接近算法的搜索上限了,快要停止搜索了,我就儘可能保證不接受惡化解。大致就是這麼個意思哈哈。
- 3.使用對象函數值進行搜索
傳統搜索算法往往在使用目標函數的前提下還需要額外使用目標函數的導數值等一些其它輔助信息才能確定搜索方向,如果輔助信息很少或者沒有,那麼算法就失效了,而模擬退火僅使用目標函數轉換來的適應度函數值,就可以確定搜索方向和範圍。
- 4.隱含並行性
並行性往往能夠提高一個算法的效率,而將模擬退火改造成並行算法還是比較容易的,如可以使用操作並行策略、試演並行策略、區域分裂策略和混亂鬆弛策略等,這些改造在不同程度上對解的質量、收斂速度上比模擬退火更優,這部分內容比較深入,想了解的小夥伴可以自行了解,本文不再介紹。
算法步驟
初始化參數
在模擬退火算法中有一系列的參數需要我們自行設置,如
- 1.初始溫度
- 2.初始解
- 3.馬爾科夫鏈的長度
- 4.控制溫度衰減的函數參數
- 5.算法停止溫度
1 其中初始溫度就是降溫開始的溫度,一般要求它足夠高,但實際上問題規模不同它的需求也不同,這要根據實際問題進行分析處理,實在不行多試幾下就好了。
2 初始解指算法開始運行的最開始的解,對模擬退火算法而言初始解的設定對最終搜索結果影響不大,因此這個用隨機函數什麼的設置一個即可,但是對有些算法比如遺傳算法,就可能需要找到儘可能好的初始解,因此它經常和神經網絡進行結合使用進行算法優化。
3 馬爾科夫鏈的長度也就是指任意溫度下的迭代次數,算法在馬爾可夫鏈的長度內持續進行產生新解、判斷、接受/捨棄的迭代過程。
4 控制溫度衰減的函數參數也成爲退火策略。我們往往需要溫度以適當的方式進行降溫,降溫越快搜索的解空間越小越不容易找到最優解,降溫越慢越影響算法效率,實際上我們往往使用兩種方法進行溫度衰減:
t(k+1) = a*t(k)
t(k) = (L-k)/L - t
,k=1,2,3…
對於第一種方法,a爲一個接近1的常數,一般取0.5-0.99,比較好理解,就是根據前一溫度依次下降一點。
對於第二中方法,L爲算法控制參數下降的總次數,也就是馬爾科夫鏈的長度,也是以一定規律下降的。
5 算法停止溫度即當溫度下降到此時算法搜索停止,採用當前最優解爲全局近似最優解。
實際上,經過算法的理解我們應該知道了,設置的初始溫度儘量足夠高,馬爾可夫鏈迭代次數儘量足夠大,終止溫度進行足夠低,降溫過程儘量足夠緩慢,這樣的設置我們更可能保證獲得全局最優解,但在使用過程中肯定要平衡好效率來設置參數,使得算法效率更好。
產生新解
產生新解是由一個產生函數從當前解產生一個位於解空間的新解,一般是從當前解進行擾動產生的,爲便於後續的計算和接受,減少算法耗時,通常選擇由當前新解經過簡單地變換即可產生新解的方法,如對構成新解的全部或部分元素進行置換、互換等。因爲模擬退火能解決的問題有很多種,這個要根據具體問題進行分析選擇。
判斷新解是否接受
這一步就根據前邊的 Metroplis準則進行判斷選擇了。
循環
即在某一溫度下,重複馬爾可夫鏈長度的擾動和接受過程,即不斷產生新解並判斷是否接受
降溫
每次循環過後按照降溫的函數來進行降溫,就這樣不斷降溫,不斷循環迭代。
找到最優解
在溫度降到我們設置的終止溫度後算法停止搜索,採用當前最優解爲全局近似最優解,算法結束。
算法實現流程圖
實例及代碼
算法學的差不多了,那怎麼能不看個實例和相關代碼呢,下面給出一個TSP問題的實際例子及其求解代碼,很多地方都已經註釋了,如果有不懂的同學可以私信我哦。
問題:一旅行小夥想去國內34個城市旅遊,要求一次走完不重複且走的總距離最短,有已知的34個城市的經緯度,求最短總距離。
clc;clear; %清空工作區及命令行
data=[116.28,39.54; 121.29,31.14; 117.11,39.09; 106.32,29.32; 126.41,45.45;
125.19,43.52; 123.24,41.50; 111.48,40.49; 114.28,38.02; 112.34,37.52;
117.00,36.38; 113.42,34.48; 108.54,34.16; 103.49,36.03; 106.16,38.20;
101.45,36.38; 87.36,43.48; 117.18,31.51; 118.50,32.02; 120.09,30.14;
113.00,28.11; 115.52,28.41; 114.21,30.37; 104.05,30.39; 106.42,26.35;
119.18,26.05; 113.15,23.08; 110.20,20.02; 108.20,22.48; 102.41,25.00;
90.08,29.39; 114.10,22.18; 113.35,22.14; 121.31,25.03];
%data爲經緯度矩陣,第一列爲經度,第二列爲緯度
lon = data(:,1); %lon爲經度矩陣
lat = data(:,2); %lat爲緯度矩陣
picture=plot([lon;lon(1)],[lat;lat(1)],'.r-','markersize',15);
lon = (lon/180)*pi; %將經緯度化爲弧度制便於利用公式求距離
lat = (lat/180)*pi;
len = size(lon,1) %得到城市的數量
Distance = zeros(len) %產生len*len的0矩陣用來存放各個城市的距離
R = 6378.137 %地球半徑
for i =1:len
for j = 1:len
Dlon = lon(i)-lon(j);
Dlat = lat(i)-lat(j);
Distance(i,j)=2*R*asin(sqrt(sin(Dlat/2)^2+sin(Dlon/2)^2*cos(lat(i))*cos(lat(j))));
end %上面這一長串是用的公式計算兩城市的距離並存在距離矩陣中
end
%下面開始初始化參數
T0 = 2000; %初始溫度
T = 1; %終止溫度
a = 0.99; %溫度衰減係數
n = len; %n爲城市總數量,爲了程序更清晰新賦值一個變量
L = 100*n; %馬爾可夫鏈的長度
path = randperm(n); %產生初始路徑,這個函數是將1-34隨機排列成一維矩陣,我們將其作爲初始解
Dpath = 0; %Dpath爲初始解的路線長度
for i = 1:n-1
Dpath = Dpath+Distance(path(i),path(i+1));
end
Dpath = Dpath + Distance(path(n),path(1));%初始解的長度爲此
while T0>T %降溫過程
for i = 1:L %馬爾可夫鏈迭代過程
value1 = ceil(n*rand);
value2 = ceil(n*rand);
val1 = min(value1,value2);
val2 = max(value1,value2);
pathvalue = fliplr(path(val1:val2));%fliplr是一個逆置函數,將兩參數之間數逆置
newpath = [path(1:(val1-1)),pathvalue,path((val2+1):n)];%這就是新產生的路徑矩陣
newDpath = 0;
for i = 1:n-1
newDpath = newDpath+Distance(newpath(i),newpath(i+1));
end
newDpath = newDpath + Distance(newpath(n),newpath(1));
%newDpath是新產生的路徑的總長度
if newDpath<Dpath %如果新路徑比原路徑小,直接接受
path = newpath;
Dpath = newDpath;
elseif exp((Dpath-newDpath)*2/T0)>rand %如果新路徑比原路徑大,以一定概率接受
path = newpath;
Dpath = newDpath;
else %負責路徑不改變
path = path;
Dpath = Dpath;
end
end
T0 = a*T0;
set(picture,'xdata',lon([path path(1)]),'ydata',lat([path path(1)]));
xlabel(sprintf('總距離=%.1f',Dpath),'fontsize',10);
title(sprintf('當前溫度T=%.1f',T0),'fontsize',10);
drawnow;
end
最後注意一點,因爲CSDN不支持Matlab代碼塊顯示,我用的Java代碼塊,原語句中的註釋爲%我並沒有改變,爲的是保證大家複製粘貼即可運行觀看結果。
值得一提的是,有時候在解決問題的時候需要的不僅僅是最終的解,掌握好的作圖方法能將結果給完美的展示出來也非常重要,我寫過一篇Matlab繪圖方法的博文,有興趣的大家可以參考一下:
Matlab繪圖方法整理(超完整版)
最終,非常感謝你能看到這裏,如果覺得文章對你有幫助,不妨點個贊喲,祝大家學業有成、工作順利!