天天看點

基于蟻群算法求解求解TSP問題(JAVA)

原文位址:http://blog.csdn.net/wangqiuyun/article/details/8878298

一、TSP問題

TSP問題(Travelling Salesman Problem)即旅行商問題,又譯為旅行推銷員問題、貨郎擔問題,是數學領域中著名問題之一。假設有一個旅行商人要拜訪n個城市,他必須選擇所要走的路徑,路徑的限制是每個城市隻能拜訪一次,而且最後要回到原來出發的城市。路徑的選擇目标是要求得的路徑路程為所有路徑之中的最小值。

TSP問題是一個組合優化問題。該問題可以被證明具有NPC計算複雜性。TSP問題可以分為兩類,一類是對稱TSP問題(Symmetric TSP),另一類是非對稱問題(Asymmetric TSP)。所有的TSP問題都可以用一個圖(Graph)來描述:

V={c1, c2, …, ci, …, cn},i = 1,2, …, n,是所有城市的集合.ci表示第i個城市,n為城市的數目;
E={(r, s): r,s∈ V}是所有城市之間連接配接的集合;
C = {crs: r,s∈ V}是所有城市之間連接配接的成本度量(一般為城市之間的距離);

如果crs = csr, 那麼該TSP問題為對稱的,否則為非對稱的。

一個TSP問題可以表達為:

求解周遊圖G = (V, E, C),所有的節點一次并且回到起始節點,使得連接配接這些節點的路徑成本最低。

二、蟻群算法

蟻群算法(ant colony optimization, ACO),又稱螞蟻算法,是一種用來在圖中尋找優化路徑的機率型算法。它由Marco Dorigo于1992年在他的博士論文中提出,其靈感來源于螞蟻在尋找食物過程中發現路徑的行為。蟻群算法是一種模拟進化算法,初步的研究表明該算法具有許多優良的性質。針對PID控制器參數優化設計問題,将蟻群算法設計的結果與遺傳算法設計的結果進行了比較,數值仿真結果表明,蟻群算法具有一種新的模拟進化優化方法的有效性和應用價值。

蟻群算法原理:假如蟻群中所有螞蟻的數量為m,所有城市之間的資訊素用矩陣pheromone表示,最短路徑為bestLength,最佳路徑為bestTour。每隻螞蟻都有自己的記憶體,記憶體中用一個禁忌表(Tabu)來存儲該螞蟻已經通路過的城市,表示其在以後的搜尋中将不能通路這些城市;還有用另外一個允許通路的城市表(Allowed)來存儲它還可以通路的城市;另外還用一個矩陣(Delta)來存儲它在一個循環(或者疊代)中給所經過的路徑釋放的資訊素;還有另外一些資料,例如一些控制參數(α,β,ρ,Q),該螞蟻行走玩全程的總成本或距離(tourLength),等等。假定算法總共運作MAX_GEN次,運作時間為t。

蟻群算法計算過程如下:

(1)初始化

(2)為每隻螞蟻選擇下一個節點。

(3)更新資訊素矩陣

(4)檢查終止條件

(5)輸出最優值

三、蟻群算法求解TSP問題

在該JAVA實作中我們選擇使用tsplib上的資料att48,這是一個對稱TSP問題,城市規模為48,其最優值為10628.其距離計算方法下圖所示:

基于蟻群算法求解求解TSP問題(JAVA)

具體代碼如下:

[java] 

view plain

 copy

  1. package noah;  
  2. import java.util.Random;  
  3. import java.util.Vector;  
  4. public class Ant implements Cloneable {  
  5.     private Vector<Integer> tabu; // 禁忌表  
  6.     private Vector<Integer> allowedCities; // 允許搜尋的城市  
  7.     private float[][] delta; // 資訊數變化矩陣  
  8.     private int[][] distance; // 距離矩陣  
  9.     private float alpha;  
  10.     private float beta;  
  11.     private int tourLength; // 路徑長度  
  12.     private int cityNum; // 城市數量  
  13.     private int firstCity; // 起始城市  
  14.     private int currentCity; // 目前城市  
  15.     public Ant() {  
  16.         cityNum = 30;  
  17.         tourLength = 0;  
  18.     }  
  19.     /** 
  20.      * Constructor of Ant 
  21.      *  
  22.      * @param num 
  23.      *            螞蟻數量 
  24.      */  
  25.     public Ant(int num) {  
  26.         cityNum = num;  
  27.      * 初始化螞蟻,随機選擇起始位置 
  28.      * @param distance 
  29.      *            距離矩陣 
  30.      * @param a 
  31.      *            alpha 
  32.      * @param b 
  33.      *            beta 
  34.     public void init(int[][] distance, float a, float b) {  
  35.         alpha = a;  
  36.         beta = b;  
  37.         // 初始允許搜尋的城市集合  
  38.         allowedCities = new Vector<Integer>();  
  39.         // 初始禁忌表  
  40.         tabu = new Vector<Integer>();  
  41.         // 初始距離矩陣  
  42.         this.distance = distance;  
  43.         // 初始資訊數變化矩陣為0  
  44.         delta = new float[cityNum][cityNum];  
  45.         for (int i = 0; i < cityNum; i++) {  
  46.             Integer integer = new Integer(i);  
  47.             allowedCities.add(integer);  
  48.             for (int j = 0; j < cityNum; j++) {  
  49.                 delta[i][j] = 0.f;  
  50.             }  
  51.         }  
  52.         // 随機挑選一個城市作為起始城市  
  53.         Random random = new Random(System.currentTimeMillis());  
  54.         firstCity = random.nextInt(cityNum);  
  55.         // 允許搜尋的城市集合中移除起始城市  
  56.         for (Integer i : allowedCities) {  
  57.             if (i.intValue() == firstCity) {  
  58.                 allowedCities.remove(i);  
  59.                 break;  
  60.         // 将起始城市添加至禁忌表  
  61.         tabu.add(Integer.valueOf(firstCity));  
  62.         // 目前城市為起始城市  
  63.         currentCity = firstCity;  
  64.      * 選擇下一個城市 
  65.      * @param pheromone 
  66.      *            資訊素矩陣 
  67.     public void selectNextCity(float[][] pheromone) {  
  68.         float[] p = new float[cityNum];  
  69.         float sum = 0.0f;  
  70.         // 計算分母部分  
  71.             sum += Math.pow(pheromone[currentCity][i.intValue()], alpha)  
  72.                     * Math.pow(1.0 / distance[currentCity][i.intValue()], beta);  
  73.         // 計算機率矩陣  
  74.             boolean flag = false;  
  75.             for (Integer j : allowedCities) {  
  76.                 if (i == j.intValue()) {  
  77.                     p[i] = (float) (Math.pow(pheromone[currentCity][i], alpha) * Math  
  78.                             .pow(1.0 / distance[currentCity][i], beta)) / sum;  
  79.                     flag = true;  
  80.                     break;  
  81.                 }  
  82.             if (flag == false) {  
  83.                 p[i] = 0.f;  
  84.         // 輪盤賭選擇下一個城市  
  85.         float sleectP = random.nextFloat();  
  86.         int selectCity = 0;  
  87.         float sum1 = 0.f;  
  88.             sum1 += p[i];  
  89.             if (sum1 >= sleectP) {  
  90.                 selectCity = i;  
  91.         // 從允許選擇的城市中去除select city  
  92.             if (i.intValue() == selectCity) {  
  93.         // 在禁忌表中添加select city  
  94.         tabu.add(Integer.valueOf(selectCity));  
  95.         // 将目前城市改為選擇的城市  
  96.         currentCity = selectCity;  
  97.      * 計算路徑長度 
  98.      * @return 路徑長度 
  99.     private int calculateTourLength() {  
  100.         int len = 0;  
  101.         //禁忌表tabu最終形式:起始城市,城市1,城市2...城市n,起始城市  
  102.             len += distance[this.tabu.get(i).intValue()][this.tabu.get(i + 1)  
  103.                     .intValue()];  
  104.         return len;  
  105.     public Vector<Integer> getAllowedCities() {  
  106.         return allowedCities;  
  107.     public void setAllowedCities(Vector<Integer> allowedCities) {  
  108.         this.allowedCities = allowedCities;  
  109.     public int getTourLength() {  
  110.         tourLength = calculateTourLength();  
  111.         return tourLength;  
  112.     public void setTourLength(int tourLength) {  
  113.         this.tourLength = tourLength;  
  114.     public int getCityNum() {  
  115.         return cityNum;  
  116.     public void setCityNum(int cityNum) {  
  117.         this.cityNum = cityNum;  
  118.     public Vector<Integer> getTabu() {  
  119.         return tabu;  
  120.     public void setTabu(Vector<Integer> tabu) {  
  121.         this.tabu = tabu;  
  122.     public float[][] getDelta() {  
  123.         return delta;  
  124.     public void setDelta(float[][] delta) {  
  125.         this.delta = delta;  
  126.     public int getFirstCity() {  
  127.         return firstCity;  
  128.     public void setFirstCity(int firstCity) {  
  129.         this.firstCity = firstCity;  
  130. }  

[java] 

  1. import java.io.BufferedReader;  
  2. import java.io.FileInputStream;  
  3. import java.io.IOException;  
  4. import java.io.InputStreamReader;  
  5. public class ACO {  
  6.     private Ant[] ants; // 螞蟻  
  7.     private int antNum; // 螞蟻數量  
  8.     private int MAX_GEN; // 運作代數  
  9.     private float[][] pheromone; // 資訊素矩陣  
  10.     private int bestLength; // 最佳長度  
  11.     private int[] bestTour; // 最佳路徑  
  12.     // 三個參數  
  13.     private float rho;  
  14.     public ACO() {  
  15.      * constructor of ACO 
  16.      * @param n 
  17.      *            城市數量 
  18.      * @param m 
  19.      * @param g 
  20.      *            運作代數 
  21.      * @param r 
  22.      *            rho 
  23.      **/  
  24.     public ACO(int n, int m, int g, float a, float b, float r) {  
  25.         cityNum = n;  
  26.         antNum = m;  
  27.         ants = new Ant[antNum];  
  28.         MAX_GEN = g;  
  29.         rho = r;  
  30.     // 給編譯器一條指令,告訴它對被批注的代碼元素内部的某些警告保持靜默  
  31.     @SuppressWarnings("resource")  
  32.      * 初始化ACO算法類 
  33.      * @param filename 資料檔案名,該檔案存儲所有城市節點坐标資料 
  34.      * @throws IOException 
  35.     private void init(String filename) throws IOException {  
  36.         // 讀取資料  
  37.         int[] x;  
  38.         int[] y;  
  39.         String strbuff;  
  40.         BufferedReader data = new BufferedReader(new InputStreamReader(  
  41.                 new FileInputStream(filename)));  
  42.         distance = new int[cityNum][cityNum];  
  43.         x = new int[cityNum];  
  44.         y = new int[cityNum];  
  45.             // 讀取一行資料,資料格式1 6734 1453  
  46.             strbuff = data.readLine();  
  47.             // 字元分割  
  48.             String[] strcol = strbuff.split(" ");  
  49.             x[i] = Integer.valueOf(strcol[1]);// x坐标  
  50.             y[i] = Integer.valueOf(strcol[2]);// y坐标  
  51.         // 計算距離矩陣  
  52.         // 針對具體問題,距離計算方法也不一樣,此處用的是att48作為案例,它有48個城市,距離計算方法為僞歐氏距離,最優值為10628  
  53.         for (int i = 0; i < cityNum - 1; i++) {  
  54.             distance[i][i] = 0; // 對角線為0  
  55.             for (int j = i + 1; j < cityNum; j++) {  
  56.                 double rij = Math  
  57.                         .sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])  
  58.                                 * (y[i] - y[j])) / 10.0);  
  59.                 // 四舍五入,取整  
  60.                 int tij = (int) Math.round(rij);  
  61.                 if (tij < rij) {  
  62.                     distance[i][j] = tij + 1;  
  63.                     distance[j][i] = distance[i][j];  
  64.                 } else {  
  65.                     distance[i][j] = tij;  
  66.         distance[cityNum - 1][cityNum - 1] = 0;  
  67.         // 初始化資訊素矩陣  
  68.         pheromone = new float[cityNum][cityNum];  
  69.                 pheromone[i][j] = 0.1f; // 初始化為0.1  
  70.         bestLength = Integer.MAX_VALUE;  
  71.         bestTour = new int[cityNum + 1];  
  72.         // 随機放置螞蟻  
  73.         for (int i = 0; i < antNum; i++) {  
  74.             ants[i] = new Ant(cityNum);  
  75.             ants[i].init(distance, alpha, beta);  
  76.     public void solve() {  
  77.         // 疊代MAX_GEN次  
  78.         for (int g = 0; g < MAX_GEN; g++) {  
  79.             // antNum隻螞蟻  
  80.             for (int i = 0; i < antNum; i++) {  
  81.                 // i這隻螞蟻走cityNum步,完整一個TSP  
  82.                 for (int j = 1; j < cityNum; j++) {  
  83.                     ants[i].selectNextCity(pheromone);  
  84.                 // 把這隻螞蟻起始城市加入其禁忌表中  
  85.                 // 禁忌表最終形式:起始城市,城市1,城市2...城市n,起始城市  
  86.                 ants[i].getTabu().add(ants[i].getFirstCity());  
  87.                 // 檢視這隻螞蟻行走路徑距離是否比目前距離優秀  
  88.                 if (ants[i].getTourLength() < bestLength) {  
  89.                     // 比目前優秀則拷貝優秀TSP路徑  
  90.                     bestLength = ants[i].getTourLength();  
  91.                     for (int k = 0; k < cityNum + 1; k++) {  
  92.                         bestTour[k] = ants[i].getTabu().get(k).intValue();  
  93.                     }  
  94.                 // 更新這隻螞蟻的資訊數變化矩陣,對稱矩陣  
  95.                 for (int j = 0; j < cityNum; j++) {  
  96.                     ants[i].getDelta()[ants[i].getTabu().get(j).intValue()][ants[i]  
  97.                             .getTabu().get(j + 1).intValue()] = (float) (1. / ants[i]  
  98.                             .getTourLength());  
  99.                     ants[i].getDelta()[ants[i].getTabu().get(j + 1).intValue()][ants[i]  
  100.                             .getTabu().get(j).intValue()] = (float) (1. / ants[i]  
  101.             // 更新資訊素  
  102.             updatePheromone();  
  103.             // 重新初始化螞蟻  
  104.                 ants[i].init(distance, alpha, beta);  
  105.         // 列印最佳結果  
  106.         printOptimal();  
  107.     // 更新資訊素  
  108.     private void updatePheromone() {  
  109.         // 資訊素揮發  
  110.         for (int i = 0; i < cityNum; i++)  
  111.             for (int j = 0; j < cityNum; j++)  
  112.                 pheromone[i][j] = pheromone[i][j] * (1 - rho);  
  113.         // 資訊素更新  
  114.                 for (int k = 0; k < antNum; k++) {  
  115.                     pheromone[i][j] += ants[k].getDelta()[i][j];  
  116.     private void printOptimal() {  
  117.         System.out.println("The optimal length is: " + bestLength);  
  118.         System.out.println("The optimal tour is: ");  
  119.         for (int i = 0; i < cityNum + 1; i++) {  
  120.             System.out.println(bestTour[i]);  
  121.      * @param args 
  122.     public static void main(String[] args) throws IOException {  
  123.         System.out.println("Start....");  
  124.         ACO aco = new ACO(48, 10, 100, 1.f, 5.f, 0.5f);  
  125.         aco.init("c://data.txt");  
  126.         aco.solve();  

運作結果截圖:

基于蟻群算法求解求解TSP問題(JAVA)