模擬退火算法(SA,Simulated Annealing)及其PHP實現

一、SA的算法描述
        模擬退火算法(Simulated Annealing,SA)來源於固體退火原理,將固體加溫至充分高,再讓其徐徐冷卻,加溫時,固體內部粒子隨溫升變爲無序狀,內能增大,而徐徐冷卻時粒子漸趨有序,在每個溫度都達到平衡態,最後在常溫時達到基態,內能減爲最小。
        模擬退火算法最早由Kirkpatrick等應用於組合優化領域,它是基於Monte-Carlo迭代求解策略的一種隨機尋優算法,其出發點是基於物理中固體物質的退火過程與一般組合優化問題之間的相似性。模擬退火算法從某一較高初溫出發,伴隨溫度參數的不斷下降,結合概率突跳特性在解空間中隨機尋找目標函數的全局最優解,即在局部最優解能概率性地跳出並最終趨於全局最優。模擬退火算法是一種通用的優化算法,理論上算法具有概率的全局優化性能,目前已在工程中得到了廣泛應用,諸如VLSI、生產調度、控制工程、機器學習、神經網絡、信號處理等領域。爬山法在計算的過程中可能在局部某個凸點產生局部最優解,而不能夠跳出局部解獲得真正的最優解。模擬退火算法是通過賦予搜索過程一種時變且最終趨於零的概率突跳性,從而可有效避免陷入局部極小並最終趨於全局最優的串行結構的優化算法。
這裏的“一定的概率”的計算參考了金屬冶煉的退火過程,這也是模擬退火算法名稱的由來。
  根據熱力學的原理,在溫度爲T時,出現能量差爲dE的降溫的概率爲P(dE),表示爲:
    P(dE) = exp( dE/(kT) )
  其中k是一個常數,exp表示自然指數,且dE<0。這條公式說白了就是:溫度越高,出現一次能量差爲dE的降溫的概率就越大;溫度越低,則出現降溫的概率就越小。又由於dE總是小於0(否則就不叫退火了),因此dE/kT < 0 ,所以P(dE)的函數取值範圍是(0,1) 。
  隨着溫度T的降低,P(dE)會逐漸降低。我們將一次向較差解的移動看做一次溫度跳變過程,我們以概率P(dE)來接受這樣的移動。


二、TSP問題描述
        旅行商問題描述的是一個旅行商要在N之間遊走來販賣貨物,而路費的成本與距離成正比,因此他想在N個城市之間找到僅一條最短路徑,即能去所有城市一次且僅一次,最後回到出發點。這個簡單的問題被證明爲NP問題,對於N個城市的問題計算量爲O(N!),對於N=40時的計算量就已是目前全世界最強的計算機都難以解決。因此必須尋找其它的可行的算法,模擬退火算法就是其中一種。百度中有一個例子如下:


三、SA的算法思想
        考慮因素:初始溫度T、初始解S、解空間、目標函數、新解的產生或者擾動方法
        模擬退火算法可以分解爲解空間、目標函數和初始解三部分。
  模擬退火的基本思想:
  升溫最高(1) 初始化:初始溫度T(充分大),初始解狀態S(是算法迭代的起點), 每個T值的迭代次數L
  能量平衡(2) 對k=1,……,L做第(3)至第6步:
  狀態轉移(3) 基於上一次計算的解,利用擾動產生新解S′
  能量計算(4) 計算增量Δt′=C(S′)-C(S),其中C(S)爲評價函數
  狀態轉移(5) 若Δt′<0則接受S′作爲新的當前解,否則以概率exp(-Δt′/T)接受S′作爲新的當前解.
  平衡狀態(6) 如果滿足終止條件則輸出當前解作爲最優解,結束程序。終止條件通常取爲連續若干個新解都沒有被接受時終止算法。
  溫度降低(7) T逐漸減少,且T->0,然後轉第2步。

 


四、TSP問題的SA算法PHP實現

1、

<?php
class SimulatedAnnealing
{
	private $map;//地圖,按照矩陣的方式
	private $n;//地圖規模
	private $T;//初始溫度T
	private $L;//每個溫度下達到降溫狀態的迭代次數
	private $l;//降溫常數小於卻接近於1的常數。λ通常的取值在0.8至0.99之間。在每一溫度下,實驗足夠多的轉移次數
	private $ord_temp;//降溫終止條件,相當於降溫到常溫
	private $path;//輸出結果
	
	public function __construct($_map,$_n,$_T,$_L)
	{
		$this->map = $_map;
		$this->n = $_n;
		$this->T = $_T;
		$this->L = $_L;
		$this->l = 0.95;
		$this->ord_temp = 1;
	}
	
	function randomFloat($min = 0, $max = 1) 
	{
		return $min + mt_rand() / mt_getrandmax() * ($max - $min);
	}
	
	public function output()
	{
		foreach($this->path as $key => $value)
		{
			echo $value."->";
		}
	}
	
	//第二個元素向右隨機移動1至n-2位
	public function new_S($_S)
	{
		$temp_S = $_S;
		$shift = rand(1,$this->n-2);
		
		$ts = $temp_S[1];
		$temp_S[1] = $temp_S[1+$shift];
		$temp_S[1+$shift] = $ts;
		
		return $temp_S;
	}
	
	public function cost($_S)
	{
		$_cost = 0;
		for($i=0;$i<$this->n-1;$i++)
		{
			$_cost += $this->map[$_S[$i]][$_S[$i+1]];
		}
		$_cost += $this->map[$_S[$i]][0];
		return $_cost;
	}
	
	public function calculate()
	{
		//初始解
		$S = array();
		
		for($i=0;$i<$this->n;$i++)
		{
			$S[$i] = $i;
		}
		$S[] = 0;
		$this->path = $S;
		//進入降溫過程,初始溫度爲T
		$t = $this->T;
		while($t > $this->ord_temp)
		{
			for($i=0;$i<$this->L;$i++)
			{ 
				//產生新解
				$S1 = $this->new_S($S);
				//判斷新解的價值
				$K = $this->cost($S1) - $this->cost($S);
				if($K < 0)
				{
					$S = $S1;
					if($this->cost($S) < $this->cost($this->path))
						$this->path = $S;
				}
				else
				{
					//不好的解根據Metropolis準則接受
					if($this->randomFloat(0,1) < exp(-$K/$t))
					{
						$S = $S1;
					}
				}
			}
			//這裏可以按照如果連續幾次降溫能量不變作爲終止循序條件
			//本例按照降溫到常溫終止,不計算是否能量不變,需要初始溫度給定足夠好
			$t = $t * $this->l;
		}
		//輸出結果
		$this->output();
		echo '<br>The min cost is '.$this->cost($this->path);
	}
}
?>


調用過程:

$map = array(
		array(0,3,6,7),
		array(5,0,2,3),
		array(6,4,0,2),
		array(3,7,5,0),
	);
	$n = 4;
	$T = 20;
	$L = 100;
	$SA = new SimulatedAnnealing($map,$n,$T,$L);
	$SA->calculate();

結果輸出:

0->1->2->3->0->
The min cost is 10


發佈了34 篇原創文章 · 獲贊 7 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章