Ant Algorithm(螞蟻演算法)
1991年由Dorigo首次提出
1997年提出ACS(Ant Colony System),也為較經典之版本
一種尋優演算法,原理為費洛蒙機制
費洛蒙機制:
1.pheromone記憶與更新:也就是正向及反向反饋機制
將常走路徑之pheromone變強,不常走路徑的pheromone變弱,並將較好路徑之pheromone加強
2.根據pheromone決定路徑:也就是機率尋優方法
該路徑pheromone越高,選擇該路徑機率越高
………………………………
以下規則以1997版之ACS為主
參數配置
n:節點數量
ant數量,建議設為n
b:決定pheromone和距離之相對重要性,通常設2
q0:為選擇探索或追蹤的參數,0p:用於區域更新的殘留系數,0a:用於全域更新的殘留系數,0T0:用來當一開始初路徑之pheromone,以及local update的pheromone,建議設(n*Lnn)^-1
ps:Lnn=nearest neighbor heuristic為所有節點之間最短路徑
ant步驟
大致如下
1 各ant根據state transi
tion rule決定下一個目地,但要避開之前走過的
2 各ant對剛走的路徑進行pheromone local updating 3 返回步驟1直到所有節點都完成
4 挑選最佳路徑進行pheromone global updating
5 清空本次ant路徑
6 返回步驟1直到終止條件
ps:終止條件
通常設為最大世代數,例如500
或是到最後每個世代的解都相同時
程式碼大致如下
while(terminal){//未達終止條件下不斷重複執行
while(untraved){ //將各節點都走過
while(ant){ //各ant執行state_transiton決定路徑
state_transition();
}
pheromone_local_update; //每走過一個節點就更新兩點之間pheromone
}
pheromone_global_update //全部節點走完後找出最好路徑,並只更新該路徑pheromone
}
State Transition Rule
q0為選擇探索或追蹤的參數
q為系統產生之隨機數,0 < q < 1
若q>q0則根據以下取最大值,決定的目地
MAX(ph_rs*n_rs^b)
ps:b是determines the relative importance of pheromone versus distance,通常設2
若qpb_rs=ph(r,s)*n(r,s)^b / sum(ph(r,u)*n(r,u)^b)
ph(r,s)=從r到s的pheromone
ph(r,u)=從r到u的pheromone
n(r,s)=從r到s的local heuristic,通常為1/distance_rs
n(r,u)=從r到u的local heuristic
b=同上
注意:u是未到訪的節點,r是出發節點,s是某個目地的點
區域更新費洛蒙
pheromone local updating
用在區域,每走一個節點更新一次
公式為:ph(r,s)+=(1-p)ph(r,s)+(p)ph_add(r,s)
where ph_add(r,s)=init-pheromone ,以初始pheromone做值
參數有以下
p=殘留系數
ph(r,s)=該路徑(r到s)目前的pheromone
ph_add(r,s)=當該路徑(r到s)被走過時,需增加pheromone的量,若該路徑這次沒被走過則值為0
全域更新費洛蒙
pheromone global updating
用在全域,走完全部節點在更新最好路徑上的pheromone
公式為:ph(r,s)+=(1-a)ph(r,s)+(a)ph_add(r,s)
where ph_add(r,s)= best_L^-1 , best_L為最好的路徑長度
參數有以下
a=pheromone decay parameter,0ph(r,s)同上
ph_add(r,s)同上
…………………………………………………………………….
//使用的變數如下
nodeall=所有節點數
t.max=欲執行的次數
map_d
//2維陣列型態,代表各節點之distance
//map_d=map
map_h
//2維陣列型態,代表各節點之heuristic
//map_h=map
ph
//2維陣列型態,記錄各路徑的費洛蒙
//ph=map
//ex:從a節點到b節點的費洛蒙為0.2
//ph[a][b]=0.2
antpath
//2維陣列型態,記錄各ant路徑
//antpath[ant_k][step]=node
// ex:第1隻ant走的路徑為abdc,則記錄如下
//antpath[1]={a,b,d,c}
//演算法如下
while(t for(node=0;node pb=path_probability(ph);
//根據公式(費洛蒙及各node相連之成本)產生各node去不同node的機率
dest=ant_decision(pb);
//根據機率並透過方法決定在多個目地時應選擇那一個目地
antpath=antpath_update(dest);
//更新ant路徑表以表示ant移動的狀況
ph=pheromone_update_local(antpath);
//根據實際去的node,重新計算費洛蒙的值
}
ph=pheromone_update_global(antpath);
ph=pheromone_update_global_best(antpath);
best=selectbest_antpath(antpath); //從各ant實際走的路徑中選擇最好的路徑
}
…………………………………………………………………….
acs for php example
/////////////////////////////////////////////////////////////////////////////////////////
//////// acs usage description //////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/*
1.acs() need "map distance" data and array named as "$para['map_d']", as follow
$para['map_d']=array(
'1'=>array('1'=>0,'2'=>3,'3'=>5,'4'=>5),
'2'=>array('1'=>3,'2'=>0,'3'=>4,'4'=>6),
'3'=>array('1'=>5,'2'=>4,'3'=>0,'4'=>2),
'4'=>array('1'=>5,'2'=>6,'3'=>2,'4'=>0));
2.acs($para) will return acs result with the array representation ,refer to function evaluate()
$result_array=acs($para);
*/
////////////// acs example as follow /////////////////////////////////////////////////////
//phase 1
//read data
$inputdata = file('map.txt'); //in the each line of map.txt` is x,y
while($line=each($inputdata)){
$i++;
$tmp=explode(',',$line['value']);
$node[$i]['x']=trim($tmp[0]);
$node[$i]['y']=trim($tmp[1]);
}
$nodecount=count($node);
for($i=1;$i<=$nodecount;$i++){
for($j=1;$j<=$nodecount;$j++){
$map_d[$i][$j]=0;
if($i!=$j)$map_d[$i][$j]= sprintf("%.5f",sqrt( pow($node[$i]['x']-$node[$j]['x'],2) + pow($node[$i]['y']-$node[$j]['y'],2) ));
}
}
$para['map_d']=$map_d; // data for acs()
//$para['node_xy']=$node; //the data for graph()
//phase 2
//excute acs
$para['antpath_value']=acs($para);
echo 'global best='.$para['antpath_value']['sumbest_global'].'< br>';
graph($para['antpath_value']['pathbest_global'],$node);
/////////// acs example end ////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
//////// main function ///////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////
function acs($para){
$para['a']=0.1;//0$para['b']=2;//relative importance of local heuristic or pheromone
$para['p']=0.1;//trail evaporates,the value must is < 1
$para['q0']=0.9;//explorse or tracking $para['gn']=100;//generation number
$para['gn']=500;//generation number
$para['node']=count($para['map_d']);//node sum
$para['ant']=$para['node'];//ant sum
$para['initph']=(1/($para['node']*nearest_neighbor_heuristic($para)));
$para['map_h']=init('map_h',$para);
$para['map_hb']=init('map_hb',$para);
$para['ph']=init('ph',$para);//init pheromone by random
while($t<$para['gn']){
$t++;
$para['antpath']=init('antpath',$para);
$para['unvisited']=init('unvisited',$para);
for($i=0;$i<$para['node'];$i++){
$para['unvisited']=unvisitednode($para);
reset($para['antpath']);
while($ant=each($para['antpath'])){
$para['antpath'][$ant[0]][]=state_transition($ant[0],$para);
}
$para['ph']=pheromone_update($para);
}
$para['antpath_value']=evaluate($para);
$para['ph']=pheromone_update_global($para);
}
return $para['antpath_value'];
}
/////////////////////////////////////////////////////////////////////////////////////
//////// sub function //////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
function nearest_neighbor_heuristic($para){
for($i=1;$i<=$para['node'];$i++){
$traved[$i]=0;
}
$path[]=1;
$traved[1]=1;
while(array_sum($traved)<$para['node']){
$start=end($path);
reset($traved);
$distance=null;
while($d=each($traved)){
$dst=$d[0];
if($d[1]!=1){
$distance[$dst]=$para['map_d'][$start][$dst];
}
}
asort($distance);
$n=each($distance);
$traved[$n[0]]=1;
$path[]=$n[0];
$length[]=$n[1];
}
$start=end($path);
reset($traved);
$distance=null;
while($d=each($traved)){
$dst=$d[0];
if($d[1]!=1){
$distance[$dst]=$para['map_d'][$start][$dst];
$traved[$dst]=1;
$path[]=$dst;
$length[]=$distance[$dst];
}
}
$totallength=array_sum($length);
return $totallength;
}
////////////////////////////////////////////////////////////////////////////////
function init($type,$para){
if($type=='antpath'){
for($i=1;$i<=$para['ant'];$i++){
$antpath[$i][]=$i;
}
return $antpath;
}
if($type=='unvisited'){
for($i=1;$i<=$para['ant'];$i++){
$n=1;
while($n<=$para['node']){$unvisited[$i][]=$n++;}
}
return $unvisited;
}
//compute heuristic(1/distance=heuristic)
if($type=='map_h' or $type=='ph' or $type=='map_hb'){
while($i=each($para['map_d'])){
while($j=each($i['value'])){
if($j['value']){
$map_h[$i['key']][$j['key']]=1/$j['value'];
$map_hb[$i['key']][$j['key']]=pow($map_h[$i['key']][$j['key']],$para['b']);
$ph[$i['key']][$j['key']]=$para['initph'];
}else{
$map_h[$i['key']][$j['key']]=0;
$map_hb[$i['key']][$j['key']]=0;
$ph[$i['key']][$j['key']]=0;
}
}
}
}
if($type=='map_h'){return $map_h;}
if($type=='map_hb'){return $map_hb;}
if($type=='ph'){return $ph;}
}
//////////////////////////////////////////////////////////
function unvisitednode($para){
while($ant=each($para['antpath'])){
$lastnode=end($para['antpath'][$ant[0]]);
while($u=each($para['unvisited'][$ant[0]])){
if($u[1]!=$lastnode)$unvisited_new[$ant[0]][]=$u[1];
}
}
return $unvisited_new;
}
/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////
function state_transition($k,$para){
$r=end($para['antpath'][$k]);
$dstcount=count($para['unvisited'][$k]);
if($dstcount<1){$antdest[$k]=$para['antpath'][$k][0];}
if($dstcount==1){
$lastdst=each($para['unvisited'][$k]);
$antdest[$k]=$lastdst[1];
}
if($dstcount>1){
//computer state value
$u_pb_sum=0;
while($unvisited=each($para['unvisited'][$k])){
$u=$unvisited[1];
$u_pb_each[$u]=$para['ph'][$r][$u]*$para['map_hb'][$r][$u];
}
//select explore or tracked by q0
$q=rand(0,100)/100;
if($q<=$para['q0']){
//////////////select best pheromone dest start
$ph_comp=$u_pb_each;
arsort($ph_comp);
$dstary=each($ph_comp);
$antdest[$k]=$dstary[0];
//////////////select best pheromone dest end
}else{
/////////////random select dest by probability start
$u_pb_sum=array_sum($u_pb_each);
while($s=each($u_pb_each)){
if($s[1]!=0){
$pb[$r][$s[0]]=$s[1]/$u_pb_sum;
}else{
$pb[$r][$s[0]]=0;
}
}
//select
$pbsum=array_sum($pb[$r]);
$selectpoint=(rand(0,$pbsum*10000)/10000);
while($s=each($pb[$r])){
$nowposition+=$s[1];
if($nowposition>=$selectpoint){
$antdest[$k]=$s[0];
break;
}
}
/////////////random select dest by probability end
}
}
return $antdest[$k];
}
/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
function pheromone_update($para){
reset($para['antpath']);
while($ant=each($para['antpath'])){
$j=end($para['antpath'][$ant[0]]);
$i=prev($para['antpath'][$ant[0]]);
$ph_add[$i][$j]=$para['initph'];
$ph_add_tmp=0;
//have only traveled update
$para['ph'][$i][$j]=(1-$para['p'])*$para['ph'][$i][$j]+$para['p']*$ph_add[$i][$j];
}
return $para['ph'];
}
///////////////////////////////////////////////////////////////////////////////
function pheromone_update_global($para){
$ph_add=pow($para['antpath_value']['sumbest_global'],-1);
while($node=each($para['antpath_value']['pathbest_global'])){
if($i!=null){
$j=$node[1];
$para['ph'][$i][$j]=(1-$para['a'])*$para['ph'][$i][$j]+$para['a']*$ph_add;
}
$i=$node[1];
}
return $para['ph'];
}
///////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
function evaluate($para){
$antpath_value=$para['antpath_value'];
$antpath_value['unit']=null;
while($ant=each($para['antpath'])){
$i=null;
$j=null;
while($path=each($ant['value'])){
if($i!=null){
$j=$path['value'];
$antpath_value['unit'][$ant['key']][]=$para['map_d'][$i][$j];
}
$i=$path['value'];
}
$antpath_value['sum'][$ant['key']]=array_sum($antpath_value['unit'][$ant['key']]);
}
asort($antpath_value['sum']);
$bestdata=each($antpath_value['sum']);
$bestant=$bestdata[0];
$antpath_value['sumbest']=$bestdata[1];
$antpath_value['pathbest']=$para['antpath'][$bestant];
$antpath_value['sumbest_history'][]=$antpath_value['sumbest'];
if($antpath_value['sumbest_global']==null or $antpath_value['sumbest_global']>$antpath_value['sumbest']){
$antpath_value['sumbest_global']=$antpath_value['sumbest'];
$antpath_value['pathbest_global']=$antpath_value['pathbest'];
}
$antpath_value['sumbest_global_history'][]=$antpath_value['sumbest_global'];
/*
$antpath_value['unit'] array ,each ant each cost of path node
$antpath_value['sum'] array ,each ant sum cost of path node
$antpath_value['pathbest'] array
$antpath_value['sumbest'] int
$antpath_value['sumbest_history'] array
$antpath_value['pathbest_global'] array
$antpath_value['sumbest_global'] int
$antpath_value['sumbest_global_history'] array
*/
echo $antpath_value['sumbest_global'].",";
return $antpath_value;
}
/////////////////////////////////////////////////////////////////////////////////
////////// optional ////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
function caclutime(){
$time = explode( " ", microtime());
$usec = (double)$time[0];
$sec = (double)$time[1];
return $sec + $usec;
}
//////////////////////////////////////////////////////////////////////////////////////////////
function graph($path,$node_xy){
//$path=$data['antpath_value']['pathbest_global'];
//$node_xy=$data['node_xy'];
$im = imagecreate(500,500);
$green = imagecolorallocate($im,214,235,214);
$black = imagecolorallocate ($im, 0, 0, 0);
$count=count($path)-1;
for($i=0;$i<$count;$i++){
$ix=$node_xy[$path[$i]]['x']*5;
$iy=$node_xy[$path[$i]]['y']*5;
$jx=$node_xy[$path[$i+1]]['x']*5;
$jy=$node_xy[$path[$i+1]]['y']*5;
imageline($im,$ix,$iy,$jx,$jy,$black);
}
imagepng($im, "a.png");
echo "< img src='a.png' >";
imagedestroy($im);
}
………………………………………………………………………..
其他可參考的文獻
http://sjchen.im.nuu.edu.tw/Project_Courses/ML/AntAlgo.pdf
http://bm.nsysu.edu.tw/tutorial/iylu/2008%20IQM/QT2_%C3%C6%B8s%BAt%BA%E2%AAk%A9%F3%A6v%B0t%B7~%B8%F4%BDu%B3%CC%A8%CE%A4%C6%A4%A7%AC%E3%A8s.pdf
http://ccckmit.wikidot.com/so:antcolony