1. 程式人生 > >演算法理解-模擬退火

演算法理解-模擬退火

求解函式

以求解以下這麼一個函式為例子,實現程式碼為R語言

f(x)=xsin(10πx)+2x[1,2]
其函式影象為:
suitFun

求解流程與概念

初始解的產生

又稱做狀態產生函式,通常由兩部分組成:
1)第一次產生候選解的分佈函式。
2)第一次產生候選解不滿足條件的情況下,再次產生解的分佈函式。
下面採用的是標準差為2的正態分佈、標準差為3的正態分佈。其中要注意的是狀態產生函式(領域函式)的出發點應該是儘可能保證產生的候選解遍佈全部的解空間。

劣解接受概率(Metropolis準則)

又稱做狀態接受函式,這裡是模擬退火法這個名字的來源,模仿固體退火原理,隨著溫度(迭代次數上升)的下降,能量逐漸穩定。即劣解的接受概率p

逐漸下降,其公式為:

p={1,eE(xnew)E(xold)T,E(xnew)<E(xold)E(xnew)E(xold)
從公式中可以看出這是求最小值時的分佈,因為當新的解小於舊解時百分百接受。又可以看出當E(xnew)E(xold)時,p恆在【0,1】之間。其概率變化情況:
這裡寫圖片描述
關於初始溫度,初溫越大,獲得高質量解的機率越大,但花費較多的計算時間,原理可以從上面的圖中或者結合下面溫度隨時間變化進行理解、解釋,溫度越高對於劣解的接受能力越大(搜尋範圍越大),下降速度越慢(求解速度下降)。
關於初始解如何設定一般有以下方法:
(1)按照某一概率分佈抽樣一組K個解,以K個解的目標值的方差為初溫(下面使用的時正態分佈);。
(2)隨機產生一組K個狀態,確定兩兩狀態間的最大目標差值,根據差值,利用一定的函式確定初溫。
(3)利用經驗公式。

溫度更新

溫度更新函式:

T0=T0ln(1+t)
這裡寫圖片描述
溫度可以看出不管初始溫度如何,都有一個先加熱後散熱的過程。即先提高搜尋範圍,再慢慢的減少搜尋範圍。至於為什麼會在差不多時刻大家都把溫度降低,我是這麼進行理解的,他們只是在不斷的接近,而不會重疊,同一時刻初始溫度越大,其當前時刻溫度也越大,都接近在同一刻收斂是因為他們總是在以一個倍數在進行減少。

程式碼

演算法流程圖:
這裡寫圖片描述
程式碼:

# 定義目標函式
sloveFun <- function(x){
  (x*sin(10*pi * x) + 2)
}

limitX <- c(-1, 2)
#1. 初始化以及引數調整
stmp <- runif(100
, limitX[1], limitX[2]) # 用於求初始溫度設定 t0 <- var(sloveFun(stmp)) # 初始溫度 s0 <- runif(1, limitX[1], limitX[2]) # 初始解 iters <- 1000 # 迭代次數 ccnt <- 200 # 終止條件,連續ccnt個新解都沒有接受時終止演算法 hisBest <- limitX[2] # 預設取上界 ccntVc <- NULL #2.迭代 for (i in 1:iters) { #2.1 在s0附近產生新解,但又能包含定義內所有值 s1 <- rnorm(1, mean = s0, sd = 2) while (s1 < limitX[1] || s1 > limitX[2]){ s1 <- rnorm(1, mean = s0, sd=3) } #2.2 計算能量增量 delta_t <- sloveFun(s0) - sloveFun(s1) #2.3 根據增量判斷是否接受解 if (delta_t > 0) { # 接受更好解 s0 <- s1 ccntVc <- c(ccntVc, 1) }else{ # 較差的解以一定概率接受 p <- exp(delta_t / t0) p1 <- sample(x = c(0, 1), size = 1, prob = c(1-p, p)) # 以一定概率生成 0,1:0 不接受劣解,1 接受劣解 if( p1 == 1 ) { s0 <- s1 ccntVc <- c(ccntVc, 1) } else { ccntVc <- c(ccntVc, 0) } } #2.4 更新最優值 hisBest <- ifelse(sloveFun(s1) < sloveFun(hisBest), s1, hisBest) hisBest <- ifelse(sloveFun(s0) < sloveFun(hisBest), s0, hisBest) #2.5 更新溫度 t0 <- t0/log(1 + i) # 溫度隨時間的推移而下降 #2.6 檢查終止條件 ccntVcLen <- length(ccntVc) if (ccntVcLen > ccnt && # 模擬次數大於ccnt sum(ccntVc[(ccntVcLen - ccnt +1):ccntVcLen]) == 0) { # 連續ccnt次沒有接受新解,ccntVc末尾200個全為0 cat("連續", ccnt, "次沒有接受新解,演算法終止!","\n") cat("迭代次數為:", i, "\n") cat("最優解為:", hisBest, "\n") break() } } # 連續 200 次沒有接受新解,演算法終止! # 迭代次數為: 257 # 最優解為: 1.848313

在看了遊皓麟一書的某些程式碼塊後,越發感覺利用R語言學習演算法的效率之高,程式碼簡短,感覺就像可以執行的流程圖。是一個學習的快速工具。