POJ2069 最小球覆蓋 模擬退火算法

     之前對模擬退火一直沒能較好的理解,今天借別人的代碼分析了一波。

     https://www.cnblogs.com/lfri/p/11604701.html 這篇博客的第二份代碼就是這題模擬退火的解法。有了自己的理解。

     這份代碼的思路實際上是先通過求平均找到一個起始點P0。然後將這個P0不斷蹦來蹦去找到距離答案最近的點。它這個蹦來蹦去不是亂蹦,是利用了退火函數的優點來蹦。

     先對一個比較簡單的情況分析,假如問題是:一個單調函數(區間[a,b]),現在讓你找出最大值時的x的值,如下圖

     答案顯而易見是b,那麼利用模擬退火是怎麼找到答案的呢。

     這次還是先說解法,再理解這個解法。首先 隨便找一個初始點P0(P0屬於[a,b]),那麼下一步P0往左蹦和往右蹦的概率相等(通過Rand()*2.0-1 就可以實現)。

    現在加一個限制,假如下一個隨機的點的函數值f(next)大於當前函數值f(cur)就一定蹦(那麼只要隨機的是右邊,一定會蹦過去);

    如果小於當前函數值f(cur)就給它一個概率選擇蹦或不蹦(這裏限制的是再隨機一個值rand1,如果

rand1<e^{-(f(cur)-f(next))/T} 就蹦)。根據下圖1可以發現,如果我們(f(cur)-f(next))大於0,那麼整個函數是隨T的增加而增加的。對了,這裏的T就是模擬退火中的溫度值,T的初始值爲b-a比較合適,每次退火T*=0.9999,就是說T在退火過程中不斷減少。

    那麼我們先看T不變時(即討論當前選擇隨機點時蹦的概率),可以發現(f(cur)-f(next))越大,蹦的概率越小(下圖2),換言之,越往左概率越低。

    現在從整體角度去看,你從a+delta*0點開始蹦(delta是大於0的無窮小),你從a+delta*1點開始蹦,你從a+delta*2點開始蹦,你從a+delta*3點開始蹦,你從a+delta*4開始點蹦,你從a+delta*5點開始蹦,......,你從b開始蹦。把所有這些點的貢獻值加起來,會發現蹦到b的概率最高,蹦到a的概率最低。

     而且,隨着溫度T的降低,每次蹦的步長會越來越小。

     現在可以爲這條線腦補一個概率(會蹦到某點的概率)分佈,可以感覺到最高點的概率最大,最低點概率最小。也就是說,假設你現在給的時間複雜度爲1e6級別,我把1e6個檢測點按照這個概率分佈撒在這條線上,最高點附近撒的點會很多,最低點就很少。然後在你撒的點中找到最大的值,自然結果就很接近正確答案。


 

     然後將問題引申到更復雜的情況,如下圖。還是和之前一樣分析,會發現高處概率高,低處概率低。

     最後總結一下,這種方法適合於連續函數,不適合於分段函數或在某段極小(相對於你撒點的密度)的區間內有極大的變化(如下圖情況),因爲你仍然用你原來的撒點密度可能就忽略了這個及其細小的高峯,導致該處的概率和附近的低谷差不多,該處撒的點就少了,可能就撒不到這個細區間內,就更新不了該處的答案。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章