1. 程式人生 > >記一次蟻群演算法解決TSP問題

記一次蟻群演算法解決TSP問題

演算法規則

1)範圍
螞蟻觀察到的範圍是一個方格世界,螞蟻有一個引數為速度半徑(一般是3),那麼它能觀察到的範圍就是3*3個方格世界,並且能移動的距離也在這個範圍之內。

2)摺疊環境
螞蟻所在的環境是一個虛擬的世界,其中有障礙物,有別的螞蟻,還有資訊素,資訊素有兩種,一種是找到食物的螞蟻灑下的食物資訊素,一種是找到窩的螞蟻灑下的窩的資訊素。每個螞蟻都僅僅能感知它範圍內的環境資訊。環境以一定的速率讓資訊素消失。

3)摺疊覓食規則
在每隻螞蟻能感知的範圍內尋找是否有食物,如果有就直接過去。否則看是否有資訊素,並且比較在能感知的範圍內哪一點的資訊素最多,這樣,它就朝資訊素多的地方走,並且每隻螞蟻都會以小概率犯錯誤,從而並不是往資訊素最多的點移動。螞蟻找窩的規則和上面一樣,只不過它對窩的資訊素做出反應,而對食物資訊素沒反應。

4)摺疊移動規則
每隻螞蟻都朝向資訊素最多的方向移,並且,當週圍沒有資訊素指引的時候,螞蟻會按照自己原來運動的方向慣性的運動下去,並且,在運動的方向有一個隨機的小的擾動。為了防止螞蟻原地轉圈,它會記住剛才走過了哪些點,如果發現要走的下一點已經在之前走過了,它就會盡量避開。

5)摺疊避障規則
如果螞蟻要移動的方向有障礙物擋住,它會隨機的選擇另一個方向,並且有資訊素指引的話,它會按照覓食的規則行為。

6)摺疊資訊素規則
每隻螞蟻在剛找到食物或者窩的時候撒發的資訊素最多,並隨著它走遠的距離,播撒的資訊素越來越少。

根據這幾條規則,螞蟻之間並沒有直接的關係,但是每隻螞蟻都和環境發生互動,而通過資訊素這個紐帶,實際上把各個螞蟻之間關聯起來了。比如,當一隻螞蟻找到了食物,它並沒有直接告訴其它螞蟻這兒有食物,而是向環境播撒資訊素,當其它的螞蟻經過它附近的時候,就會感覺到資訊素的存在,進而根據資訊素的指引找到了食物。

演算法實現

螞蟻類

屬性:
螞蟻的視野
int sightdistance = 2000;
螞蟻的路徑
public int[]tour
記錄螞蟻走過的城市

    /**
     * 表示城市的陣列,若訪問過,則為0,否則為1
     */
    int[] unvisitedcity;

方法:
1.設定螞蟻初始出現的城市
public void RandomSelectCity(int citycount)
2.螞蟻下一個出現的城市(重點)
public void SelectNextCity(int index,double[][]tao,int[][]distance)


其中index意為下一個走過的是第index城市,tao為資訊素地圖,distance為距離地圖。
主要思路:
1)查詢螞蟻周圍是否有資訊素,若有資訊素轉至3),否則轉到2)
2)螞蟻因為慣性,選擇離自己最近的城市為下一個地點,轉到5)
3)螞蟻綜合路徑的長度和資訊素的代價
4)由輪盤賭演算法,選擇下一個城市
5)灑下自己的資訊素

蟻群演算法類的實現

屬性:

    ant []ants;//螞蟻群
    int antcount;//螞蟻的數量
    int [][]distance;//表示城市間距離
    double [][]tao;//資訊素矩陣
    int citycount;//城市數量
    int[]besttour;//求解的最佳路徑
    int bestlength;//求的最優解的長度

方法:
1.初始化蟻群和TSP問題的地圖
public void init(String filename,int antnum)
filename為TSP問題檔案,antnum為螞蟻群的螞蟻
2.演算法的進行
public void run(int maxgen)
maxgen為迭代的次數

主要思路:
1)將蟻群投放到每個城市中
2)讓每一個螞蟻選擇自己要前往的下一個城市
3)更新資訊素,老的資訊素揮發,新的資訊素灑下,若走完所有城市轉向4),否則轉向2)
4)計算每隻螞蟻走過的路程
5)將路程與最近路程比較,若更近,則替換
6)返回2)繼續迭代,直到迭代完成

引數

螞蟻數量:50
迭代次數:4000
螞蟻的視野:2000
每次灑下資訊素:2.0/螞蟻走過的長度 (走的越遠,資訊素越少)
資訊素揮發:每次變為原來的0.5倍

結果

路徑: 46->19->32->45->6->27->5->36->18->26->16->42->29->35->17->43->30->37->7->0->8->39->14->11->10->22->2->21->15->40->33->28->1->25->3->34->44->23->9->41->4->47->31->38->24->13->12->20->46
最優路徑長度是33900

資料

./data.txt

48
1 6734 1453
2 2233 10
3 5530 1424
4 401 841
5 3082 1644
6 7608 4458
7 7573 3716
8 7265 1268
9 6898 1885
10 1112 2049
11 5468 2606
12 5989 2873
13 4706 2674
14 4612 2035
15 6347 2683
16 6107 669
17 7611 5184
18 7462 3590
19 7732 4723
20 5900 3561
21 4483 3369
22 6101 1110
23 5199 2182
24 1633 2809
25 4307 2322
26 675 1006
27 7555 4819
28 7541 3981
29 3177 756
30 7352 4506
31 7545 2801
32 3245 3305
33 6426 3173
34 4608 1198
35 23 2216
36 7248 3779
37 7762 4595
38 7392 2244
39 3484 2829
40 6271 2135
41 4985 140
42 1916 1569
43 7280 4899
44 7509 3239
45 10 2676
46 6807 2993
47 5185 3258
48 3023 1942

原始碼

./ant.java

import java.util.Random;
/**
 * 螞蟻類
 * @author ZKY
 *
 */
public class ant {
    /**
     * 螞蟻獲得的路徑
     */
    public int[]tour;
    /**
     * 表示城市的陣列,若訪問過,則為0,否則為1
     */
    int[] unvisitedcity;
    /**
     * 螞蟻獲得的路徑長度
     */
    public int tourlength;
    /**
     * 城市數
     */
    int citys;
    /**
     * 螞蟻的視野
     */
    int sightdistance = 2000;
    int startcity;
/**
 * 隨機分配螞蟻到某個城市中
 * 同時完成螞蟻包含欄位的初始化工作
 * @param citycount 總的城市數量
 */
    public void RandomSelectCity(int citycount){
    	sightdistance = 2000;
        citys=citycount;
        unvisitedcity=new int[citycount];
        tour=new int[citycount+1];
        tourlength=0;
   
        for(int i=0;i<citycount;i++){
        	//初始化路徑和未訪問城市
            tour[i]=-1;
            unvisitedcity[i]=1;
        }
        long r1 = System.currentTimeMillis();
        Random rnd=new Random(r1);
        int firstcity=rnd.nextInt(citycount);
        unvisitedcity[firstcity]=0;
        tour[0]=firstcity;
        startcity = firstcity;
    }
    /**
     * 選擇下一個城市
     * @param index 需要選擇第index個城市了
     * @param tao   全域性的資訊素資訊
     * @param distance  全域性的距離矩陣資訊
     */
    public void SelectNextCity(int index,double[][]tao,int[][]distance){
        double []p;
        p=new double[citys];
        double alpha=1.0;
        double beta=2.0;
        double sum=0;
        int currentcity=tour[index-1];
        //若視野範圍沒有資訊素,則hastao為0
        int hastao = 0;
        //計算公式中的分母部分
        for(int i=0;i<citys;i++){
        	
            if(unvisitedcity[i]==1 && distance[currentcity][i] <= sightdistance) {
                sum+=(Math.pow(tao[currentcity][i], alpha)*
                        Math.pow(1.0/distance[currentcity][i], beta));
            	hastao++;
            }
        }
        if (hastao == 0) {//若視野中沒有資訊素
        	int mindistance = -1;
    		int mincity = -1;
        	for (int i=0;i<citys;i++) {
        		if(unvisitedcity[i]==1) {
        			if (mindistance == -1) {
        				mindistance = distance[currentcity][i];
        				mincity = i;
        			}
        			if (distance[currentcity][i] < mindistance) {
        				mindistance = distance[currentcity][i];
        				mincity = i;
        			}
        		}
        	}
        	if (mincity == -1) 
        		mincity = startcity;
        	//螞蟻因為慣性,向最近的城市前進
        	//System.out.println("#"+mincity);jiang
        	tour[index] = mincity;
        	unvisitedcity[mincity]=0;
        	return;
        	
        }
        
        //計算每個城市被選中的概率
        for(int i=0;i<citys;i++){
            if(unvisitedcity[i]==0 || distance[currentcity][i] > sightdistance)
                p[i]=0.0;
            else{
                p[i]=(Math.pow(tao[currentcity][i], alpha)*
                        Math.pow(1.0/distance[currentcity][i], beta))/sum;
                
            }
        }
        long r1 = System.currentTimeMillis();
        Random rnd=new Random(r1);
        double selectp=rnd.nextDouble();
        selectp = Math.random();   
        //輪盤賭選擇一個城市;
        double sumselect=0;
        int selectcity=-1;
        for(int i=0;i<citys;i++){
            sumselect+=p[i];
            if(sumselect>=selectp){
                selectcity=i;
                break;
            }
        }
        if (selectcity==-1)
        	System.out.println("Error");;
        tour[index]=selectcity;
        unvisitedcity[selectcity]=0;
    }
    /**
     * 計算螞蟻獲得的路徑的長度
     * @param distance  全域性的距離矩陣資訊
     */
    public void CalTourLength(int [][]distance){
        tourlength=0;
        tour[citys]=tour[0];
        for(int i=0;i<citys;i++){
            tourlength+=distance[tour[i]][tour[i+1]];
        }    
    }
}

./ACO.java

package tspsolver;
import java.io.*;
/**
 *蟻群優化演算法,用來求解TSP問題
 * @author ZKY
 */
public class ACO {
    //定義螞蟻群
    ant []ants;//螞蟻群
    int antcount;//螞蟻的數量
    int [][]distance;//表示城市間距離
    double [][]tao;//資訊素矩陣
    int citycount;//城市數量
    int[]besttour;//求解的最佳路徑
    int bestlength;//求的最優解的長度
    /** 初始化函式
     *@param filename tsp資料檔案
     *@param antnum 系統用到螞蟻的數量
     *@throws 如果檔案不存在則丟擲異常
    */
    public void init(String filename,int antnum) throws FileNotFoundException, IOException{
        antcount=antnum;
        ants=new ant[antcount];
        //讀取資料
        int[] x;
        int[] y;
        String strbuff;
        BufferedReader tspdata = new BufferedReader(new InputStreamReader(new FileInputStream(filename)));
        strbuff = tspdata.readLine();
        citycount = Integer.valueOf(strbuff);
        distance = new int[citycount][citycount];
        x = new int[citycount];
        y = new int[citycount];
        for (int citys = 0; citys < citycount; citys++) {
            strbuff = tspdata.readLine();
            String[] strcol = strbuff.split(" ");
            x[citys] = Integer.valueOf(strcol[1]);
            y[citys] = Integer.valueOf(strcol[2]);
        }
        //計算距離矩陣
        for (int city1 = 0; city1 < citycount - 1; city1++) {
            distance[city1][city1] = 0;
            for (int city2 = city1 + 1; city2 < citycount; city2++) {
                distance[city1][city2] = (int) (Math.sqrt((x[city1] - x[city2]) * (x[city1] - x[city2])
                        + (y[city1] - y[city2]) * (y[city1] - y[city2])) + 0.5);
                distance[city2][city1] = distance[city1][city2];
            }
        }
        distance[citycount - 1][citycount - 1] = 0;
        
        //初始化資訊素矩陣
        tao=new double[citycount][citycount];
        for(int i=0;i<citycount;i++)
        {
            for(int j=0;j<citycount;j++){
                tao[i][j]=0.1;
            }
        }
        bestlength=Integer.MAX_VALUE;
        besttour=new int[citycount+1];
        //隨機放置螞蟻
        for(int i=0;i<antcount;i++){
            ants[i]=new ant();
            ants[i].RandomSelectCity(citycount);
        }
        tspdata.close();
    }
    /**
     * ACO的執行過程
     * @param maxgen ACO的最多迴圈次數
     * 
     */
    public void run(int maxgen){
        for(int runtimes=0;runtimes<maxgen;runtimes++){
            //每一隻螞蟻移動的過程
            for(int i=0;i<antcount;i++){
                for(int j=1;j<citycount;j++){
                    ants[i].SelectNextCity(j,tao,distance);
                }
                //計算螞蟻獲得的路徑長度
                ants[i].CalTourLength(distance);
                if(ants[i].tourlength<bestlength){
                    //保留最優路徑
                    bestlength=ants[i].tourlength;
                    System.out.println("第"+runtimes+"代,發現新的解"+bestlength);
                    for(int j=0;j<citycount+1;j++) {
                        besttour[j]=ants[i].tour[j];
                    	System.out.print(besttour[j]+ "->");
                    }
                    System.out.println();
                }
            }
            //更新資訊素矩陣
            UpdateTao();
            //重新隨機設定螞蟻
            for(int i=0;i<antcount;i++){
                ants[i].RandomSelectCity(citycount);
            }
        }
       }
    /**
     * 更新資訊素矩陣
     */
    private void UpdateTao(){
        double rou=0.5;
        //資訊素揮發
        for(int i=0;i<citycount;i++)
            for(int j=0;j<citycount;j++)
                tao[i][j]=tao[i][j]*(1-rou);
        //資訊素更新
        for(int i=0;i<antcount;i++){
            for(int j=0;j<citycount;j++){
                tao[ants[i].tour[j]][ants[i].tour[j+1]]+=2.0/ants[i].tourlength;
                tao[ants[i].tour[j+1]][ants[i].tour[j]]+=2.0/ants[i].tourlength;
            }
        }
    }
    /**
     * 輸出程式執行結果
     */
    public void ReportResult(){
        System.out.println("最優路徑長度是"+bestlength);
    }
}

./Main.java

package tspsolver;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
 *主程式 呼叫ACO求解問題
 * @author ZKY
 */
public class Main {
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        // TODO code application logic here
        ACO aco;
        aco=new ACO();
        try {
            aco.init("D://data.txt", 50);
            aco.run(4000);
            aco.ReportResult();
        } catch (FileNotFoundException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ex) {
            Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}