1、理論概述
1.1、TSP問題
旅行商問題,即TSP問題(旅行推銷員問題、貨郎擔問題),是數學領域中著名問題之一。假設有一個旅行商人要拜訪n個城市,他必須選擇所要走的路徑,路徑的限制是每個城市只能拜訪一次,而且最后要回到原來出發的城市。路徑的選擇目標是要求得的路徑路程為所有路徑之中的最小值。TSP問題是一個組合優化問題。該問題可以被證明具有NP計算復雜性,迄今為止,這類問題中沒有一個找到有效解決算法,因此我們經常用一些近似求解算法,遺傳算法、蟻群算法、粒子群算法等等。
1.2、蟻群算法
蟻群算法是一種仿生學算法,是由自然界中螞蟻覓食的行為而啟發的。在自然界中,螞蟻覓食過程中,蟻群總能夠按照尋找到一條從蟻巢和食物源的最優路徑。以下是蟻群覓食過程:
在圖1(a)中,一群螞蟻從A到E覓食,由於是直線路徑,所以螞蟻直接從源到目的地。在A和E之間多了一個障礙物(b)圖,那么在H點或者C點的螞蟻將要做選擇,由於一開始路上沒有前面螞蟻留下的信息素,螞蟻朝着兩個方向行進的概率是相等的。但是當有螞蟻走過時,它將會在它行進的路上釋放出信息素,並且這種信息素會以一定的速率散發掉。它后面的螞蟻通過路上信息素的濃度,做出決策,往左還是往右。很明顯,沿着短邊的的路徑上信息素將會越來越濃,隨着時間的增長,右邊路徑的信息素濃度會因為路徑的長度而散發掉,所以會有越來越多的螞蟻沿着較短的路徑前行。如圖c。
蟻群算法的缺點是收斂速度慢,容易陷入局部最優解。
2、算法流程
假設蟻群中所有螞蟻的數量為m,所有城市之間的信息素用矩陣pheromone表示,最短路徑為bestLength,最佳路徑為bestTour。每只螞蟻都有自己的內存,內存中用一個禁忌表(Tabu)來存儲該螞蟻已經訪問過的城市,表示其在以后的搜索中將不能訪問這些城市;還有用另外一個允許訪問的城市表(Allowed)來存儲它還可以訪問的城市;另外還用一個矩陣(Delta)來存儲它在一個循環(或者迭代)中給所經過的路徑釋放的信息素;還有另外一些數據,例如一些控制參數(α,β,ρ,Q)用於輔助計算信息素揮發、下一個城市選中概率等等,該螞蟻行走完全程的總成本或距離(tourLength)等。假定算法總共運行maxgen次,運行時間為t。圖三是算法流程圖。(注:實驗系數的選定是多次試驗計算的結果見論文table1)
2.1、每只螞蟻行進過程
為每只螞蟻選擇下一個節點,該節點只能從未選擇節點中以某種概率搜索到,首先計算城市選中概率,之后以一定的原則計算下一步要選的城市,如果該城市沒去過,則下一個城市就是該城市。本實驗的選擇方式是,產生一個隨機數,順序計算各個城市的選中概率之和,直到大於該隨機數,則選擇循環系數代表的城市(前提是該城市沒選過。)遍歷完所有節點后,將起始節點加入到 tour中,形成一個完整回路。保留回路長度 tourlength。接下來計算每個螞蟻的信息素矩陣值。最后計算最佳路徑,比較每個螞蟻的路徑成本,然后和bestLength比較,若它的路徑成本比bestLength小,則將該值賦予bestLength,並在本次迭代中輸出最優路徑。本次迭代每只螞蟻走完后結束。只要沒有到達指定的迭代次數,則螞蟻重新隨機重置起始點,進入下一次迭代。
2.2、實驗終止
如果達到最大迭代次數maxgen,算法終止,輸出最優路徑和最優路徑長度;否則,重新初始化所有的螞蟻的信息,並且重新選擇起始位置為每只螞蟻。根據幾次的實驗結果表明,在迭代次數較大的時候(>1000)基本在250次迭代以內就能達到收斂路徑長,即達到局部最優。由於每次試驗的結果不同,也表明蟻群算法容易陷入局部最優,而非每次都可以得到全局最優,當然,迭代次數少,可能最優的路徑長還未得到就已經迭代結束。由於沒有與其他算法作比較,對於蟻群算法收斂速度慢這個缺陷無法比較。
(2)
路徑誤差:

圖三
2.3、數據來源
本次試驗數據來源於TSPLib,http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/。為方便實驗,數據只要包含城市總數,城市編號,城市橫縱坐標即可。
圖四為算法數據流程圖:

圖四
論文中的算法描述:

3、算法實現描述
工程主要包括:螞蟻類、蟻群算法、主調程序。
3.1、螞蟻類主要包括以下幾點:
順序參觀城市記錄 int[]tour,城市是否參觀記錄int[] unvisitedcity,螞蟻所走的總路長int tourlength,城市數目;方法主要有隨機城市選擇函數 void RandomSelectCity(int citycount),下一個城市選擇函數 void SelectNextCity(int index,double[][]tao,int[][]distance)和總路程長度計算函數 void CalTourLength(int [][]distance)。
3.2、蟻群算法主要包括以下幾點:
數據成員:
ant []ants; //定義螞蟻群
int antcount;//螞蟻的數量
int [][]distance;//表示城市間距離
double [][]tao;//信息素矩陣
int citycount;//城市數量
int[]besttour;//求解的最佳路徑
int bestlength;//求的最優解的長度
方法成員:
初始化函數,初始化文件信息,信息素矩陣,上述變量,螞蟻初始位置等信息void init(String filename,int antnum)。
蟻群算法主要運行程序,包括迭代次數為實驗參數,記錄每一只螞蟻的行進過程,計算本次迭代的這群螞蟻的最優路徑長,如果出現更短的路徑,更新路徑記錄變量,迭代一次之后更新信息素矩陣以及螞蟻重新初始化最初位置,知道到達指定迭代次數,輸出收斂路徑長。經過幾次試驗表明,一般在100次內可以達到一個收斂值,后續的若干次的剩余的迭代次數輸出的路徑長基本不變了。但是必須要到指定的迭代次數才會停止實驗。
3.3、主程序包括以下幾點:
調用蟻群算法,給定螞蟻數量,城市信息文件,迭代次數,最后輸出最優(局部)路徑結果。蟻群算法內部,在對螞蟻行進記錄時會調用螞蟻類的城市選擇函數。
3.4、類關系圖:

1 import java.io.*; 2 /** 3 *蟻群優化算法,用來求解TSP問題 4 */ 5 public class ACO { 6 7 ant []ants; //定義螞蟻群 8 int antcount;//螞蟻的數量 9 int [][]distance;//表示城市間距離 10 double [][]tao;//信息素矩陣 11 int citycount;//城市數量 12 int[]besttour;//求解的最佳路徑 13 int bestlength;//求的最優解的長度 14 //filename tsp數據文件 15 //antnum 系統用到螞蟻的數量 16 public void init(String filename,int antnum) throws FileNotFoundException, IOException{ 17 antcount=antnum; 18 ants=new ant[antcount]; 19 //讀取數據tsp里的數據包括第I個城市與城市的X,Y坐標 20 int[] x; 21 int[] y; 22 String strbuff; 23 BufferedReader tspdata = new BufferedReader(new InputStreamReader(new FileInputStream(filename))); 24 strbuff = tspdata.readLine();//讀取第一行,城市總數(按文件格式讀取) 25 citycount = Integer.valueOf(strbuff); 26 distance = new int[citycount][citycount]; 27 x = new int[citycount]; 28 y = new int[citycount]; 29 for (int citys = 0; citys < citycount; citys++) { 30 strbuff = tspdata.readLine(); 31 String[] strcol = strbuff.split(" "); 32 x[citys] = Integer.valueOf(strcol[1]);//讀取每排數據的第2二個數字即橫坐標 33 y[citys] = Integer.valueOf(strcol[2]); 34 } 35 //計算兩個城市之間的距離矩陣,並更新距離矩陣 36 for (int city1 = 0; city1 < citycount - 1; city1++) { 37 distance[city1][city1] = 0; 38 for (int city2 = city1 + 1; city2 < citycount; city2++) { 39 distance[city1][city2] = (int) (Math.sqrt((x[city1] - x[city2]) * (x[city1] - x[city2]) 40 + (y[city1] - y[city2]) * (y[city1] - y[city2]))); 41 distance[city2][city1] = distance[city1][city2];//距離矩陣是對稱矩陣 42 } 43 } 44 distance[citycount - 1][citycount - 1] = 0; 45 //初始化信息素矩陣 46 tao=new double[citycount][citycount]; 47 for(int i=0;i<citycount;i++) 48 { 49 for(int j=0;j<citycount;j++){ 50 tao[i][j]=0.1; 51 } 52 } 53 bestlength=Integer.MAX_VALUE; 54 besttour=new int[citycount+1]; 55 //隨機放置螞蟻 56 for(int i=0;i<antcount;i++){ 57 ants[i]=new ant(); 58 ants[i].RandomSelectCity(citycount); 59 } 60 } 61 //maxgen ACO的最多循環次數 62 public void run(int maxgen){ 63 for(int runtimes=0;runtimes<maxgen;runtimes++){ 64 //每次迭代,所有螞蟻都要跟新一遍,走一遍 65 //System.out.print("no>>>"+runtimes); 66 //每一只螞蟻移動的過程 67 for(int i=0;i<antcount;i++){ 68 for(int j=1;j<citycount;j++){ 69 ants[i].SelectNextCity(j,tao,distance);//每只螞蟻的城市規划 70 } 71 //計算螞蟻獲得的路徑長度 72 ants[i].CalTourLength(distance); 73 if(ants[i].tourlength<bestlength){ 74 //保留最優路徑 75 bestlength=ants[i].tourlength; 76 //runtimes僅代表最大循環次數,但是只有當,有新的最優路徑的時候才會顯示下列語句。 77 //如果后續沒有更優解(收斂),則最后直接輸出。 78 System.out.println("第"+runtimes+"代(次迭代),發現新的最優路徑長度:"+bestlength); 79 for(int j=0;j<citycount+1;j++) 80 besttour[j]=ants[i].tour[j];//更新路徑 81 } 82 } 83 //更新信息素矩陣 84 UpdateTao(); 85 //重新隨機設置螞蟻 86 for(int i=0;i<antcount;i++){ 87 ants[i].RandomSelectCity(citycount); 88 } 89 } 90 } 91 /** 92 * 更新信息素矩陣 93 */ 94 private void UpdateTao(){ 95 double rou=0.5; 96 //信息素揮發 97 for(int i=0;i<citycount;i++) 98 for(int j=0;j<citycount;j++) 99 tao[i][j]=tao[i][j]*(1-rou); 100 //信息素更新 101 for(int i=0;i<antcount;i++){ 102 for(int j=0;j<citycount;j++){ 103 tao[ants[i].tour[j]][ants[i].tour[j+1]]+=1.0/ants[i].tourlength; 104 } 105 } 106 } 107 /* 輸出程序運行結果 108 */ 109 public void ReportResult(){ 110 System.out.println("最優路徑長度是"+bestlength); 111 System.out.println("蟻群算法最優路徑輸出:"); 112 for(int j=0;j<citycount+1;j++) 113 System.out.print( besttour[j]+">>");//輸出最優路徑 114 } 115 }
1 import java.util.Random; 2 /* 3 螞蟻類 4 */ 5 public class ant { 6 /** 7 * 螞蟻獲得的路徑 8 */ 9 public int[]tour;//參觀城市順序 10 //unvisitedcity 取值是0或1,1表示沒有訪問過,0表示訪問過 11 int[] unvisitedcity; 12 /** 13 * 螞蟻獲得的路徑長度 14 */ 15 public int tourlength;//某螞蟻所走路程總長度。 16 int citys;//城市個數 17 /** 18 * 隨機分配螞蟻到某個城市中 19 * 同時完成螞蟻包含字段的初始化工作 20 * @param citycount 總的城市數量 21 */ 22 public void RandomSelectCity(int citycount){ 23 citys=citycount; 24 unvisitedcity=new int[citycount]; 25 tour=new int[citycount+1]; 26 tourlength=0; 27 for(int i=0;i<citycount;i++){ 28 tour[i]=-1; 29 unvisitedcity[i]=1; 30 }//初始化各個變量 31 32 long r1 = System.currentTimeMillis();//獲取當前時間 33 Random rnd=new Random(r1); 34 int firstcity=rnd.nextInt(citycount);//隨機指定第一個城市 35 unvisitedcity[firstcity]=0;//0表示訪問過 36 tour[0]=firstcity;//起始城市 37 } 38 /** 39 * 選擇下一個城市 40 * @param index 需要選擇第index個城市了 41 * @param tao 全局的信息素信息 42 * @param distance 全局的距離矩陣信息 43 */ 44 public void SelectNextCity(int index,double[][]tao,int[][]distance){ 45 double []p; 46 p=new double[citys];//下一步要走的城市的選中概率 47 //計算選中概率所需系數。 48 double alpha=1.0; 49 double beta=2.0; 50 double sum=0; 51 int currentcity=tour[index-1];//螞蟻所處當前城市 52 //計算公式中的分母部分(為下一步計算選中概率使用) 53 for(int i=0;i<citys;i++){ 54 if(unvisitedcity[i]==1)//沒走過 55 sum+=(Math.pow(tao[currentcity][i], alpha)* 56 Math.pow(1.0/distance[currentcity][i], beta)); 57 } 58 //計算每個城市被選中的概率 59 for(int i=0;i<citys;i++){ 60 if(unvisitedcity[i]==0) 61 p[i]=0.0;//城市走過了,選中概率就是0 62 else{ 63 //沒走過,下一步要走這個城市的概率是? 64 p[i]=(Math.pow(tao[currentcity][i], alpha)* 65 Math.pow(1.0/distance[currentcity][i], beta))/sum; 66 } 67 } 68 long r1 = System.currentTimeMillis(); 69 Random rnd=new Random(r1); 70 double selectp=rnd.nextDouble(); 71 //輪盤賭選擇一個城市; 72 double sumselect=0; 73 int selectcity=-1; 74 //城市選擇隨機,直到n個概率加起來大於隨機數,則選擇該城市 75 for(int i=0;i<citys;i++){//每次都是順序走。。。。。 76 sumselect+=p[i]; 77 if(sumselect>=selectp){ 78 selectcity=i; 79 break; 80 } 81 } 82 if (selectcity==-1)//這個城市沒有走過 83 System.out.println(); 84 tour[index]=selectcity; 85 unvisitedcity[selectcity]=0; 86 } 87 /** 88 * 計算螞蟻獲得的路徑的長度 89 * @param distance 全局的距離矩陣信息 90 */ 91 public void CalTourLength(int [][]distance){ 92 tourlength=0; 93 tour[citys]=tour[0];//第一個城市等於最后一個要到達的城市 94 for(int i=0;i<citys;i++){ 95 tourlength+=distance[tour[i]][tour[i+1]];//從A經過每個城市僅一次,最后回到A的總長度。 96 } 97 } 98 }
1 import java.io.FileNotFoundException; 2 import java.io.IOException; 3 import java.util.logging.Level; 4 import java.util.logging.Logger; 5 //蟻群算法求解旅行商問題,TSP數據來源 6 //http://comopt.ifi.uni-heidelberg.de/software/TSPLIB95/ 7 //數據中包括城市總量,每個城市的橫縱坐標 8 public class Main { 9 /** 10 * @param args the command line arguments 11 */ 12 public static void main(String[] args) { 13 ACO aco; 14 aco=new ACO(); 15 try { 16 aco.init("att48.txt", 100);//城市信息文件,螞蟻數量 17 aco.run(1000);//迭代次數 18 aco.ReportResult(); 19 } catch (FileNotFoundException ex) { 20 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); 21 } catch (IOException ex) { 22 Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex); 23 } 24 } 25 }

