一、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、
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;$in-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;$in;$i++)
{
$S[$i] = $i;
}
$S[] = 0;
$this->path = $S;
//进入降温过程,初始温度为T
$t = $this->T;
while($t > $this->ord_temp)
{
for($i=0;$iL;$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 '
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