1. 程式人生 > >TSP問題及蟻群演算法理解與實現

TSP問題及蟻群演算法理解與實現

/**
 * Created by coco on 17-10-20.
 */
import java.io.*;
import java.util.logging.Logger;

import static java.util.logging.Logger.getLogger;

public class ACO {

    private Ant[] ants; // 螞蟻
    private int antNum; // 螞蟻數量
    private int cityNum; // 城市數量
    private int MAX_GEN; // 執行代數
    private float[][] pheromone; // 資訊素矩陣
    private int[][] distance; // 距離矩陣
    private int bestLength; // 最佳長度
    private int[] bestTour; // 最佳路徑

    // 三個引數
    private float alpha;
    private float beta;
    private float rho;

    public ACO() {

    }

    /**
     * constructor of ACO
     *
     * @param n
     *            城市數量
     * @param m
     *            螞蟻數量
     * @param g
     *            執行代數
     * @param a
     *            alpha
     * @param b
     *            beta
     * @param r
     *            rho
     *
     **/
    public ACO(int n, int m, int g, float a, float b, float r) {
        cityNum = n;
        antNum = m;
        ants = new Ant[antNum];
        MAX_GEN = g;
        alpha = a;
        beta = b;
        rho = r;
    }

    /**
     * 初始化ACO演算法類
     * @param filename 資料檔名,該檔案儲存所有城市節點座標資料
     * @throws IOException
     */
    private void init(String filename,int firstCity,int lastCity) throws IOException {
        // 讀取資料
        int[] x;
        int[] y;
        String strbuff;
        BufferedReader data = new BufferedReader(new InputStreamReader(
                new FileInputStream(filename)));
        distance = new int[cityNum][cityNum];
        x = new int[cityNum];
        y = new int[cityNum];
        for (int i = 0; i < cityNum; i++) {
            // 讀取一行資料,資料格式1 6734 1453
            strbuff = data.readLine();
            // 字元分割
            String[] strcol = strbuff.split(" ");
            x[i] = Integer.valueOf(strcol[0]);// x座標
            y[i] = Integer.valueOf(strcol[1]);// y座標
        }
        // 計算距離矩陣
        // 針對具體問題,距離計算方法也不一樣,此處用的是att48作為案例,它有48個城市,距離計算方法為偽歐氏距離,最優值為10628
        for (int i = 0; i < cityNum - 1; i++) {
            distance[i][i] = 0; // 對角線為0
            for (int j = i + 1; j < cityNum; j++) {
                double rij = Math
                        .sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])
                                * (y[i] - y[j])) / 10.0);
                // 四捨五入,取整
                int tij = (int) Math.round(rij);
                if (tij < rij) {
                    distance[i][j] = tij + 1;
                    distance[j][i] = distance[i][j];
                } else {
                    distance[i][j] = tij;
                    distance[j][i] = distance[i][j];
                }
            }
        }
        distance[cityNum - 1][cityNum - 1] = 0;
        // 初始化資訊素矩陣
        pheromone = new float[cityNum][cityNum];
        for (int i = 0; i < cityNum; i++) {
            for (int j = 0; j < cityNum; j++) {
                pheromone[i][j] = 0.1f; // 初始化為0.1
            }
        }
        bestLength = Integer.MAX_VALUE;
        bestTour = new int[cityNum + 1];

        // 隨機放置螞蟻
        for (int i = 0; i < antNum; i++) {
            ants[i] = new Ant(cityNum);
            ants[i].init(distance, alpha, beta,firstCity,lastCity);
        }
    }

    public void solve() {
        // 迭代MAX_GEN次
        for (int g = 0; g < MAX_GEN; g++) {
//            if(g%100 == 0)
//                System.out.println("正在進行第 "+g+" 次迭代……");
            // antNum只螞蟻
            for (int i = 0; i < antNum; i++) {
                // i這隻螞蟻走cityNum步,完整一個TSP
                for (int j = 1; j < cityNum; j++) {
                    ants[i].selectNextCity(pheromone);
                }

                // 把這隻螞蟻起始城市加入其禁忌表中
                // 禁忌表最終形式:起始城市,城市1,城市2...城市n,終點城市
      
                // 檢視這隻螞蟻行走路徑距離是否比當前距離優秀
                if (ants[i].getTourLength() < bestLength) {
                    // 比當前優秀則拷貝優秀TSP路徑
                    bestLength = ants[i].getTourLength();

                 for (int k = 0; k < ants[i].getTabu().size() ; k++) {
                        bestTour[k] = ants[i].getTabu().get(k).intValue();
                    }
              }
                // 更新這隻螞蟻的資訊數變化矩陣,對稱矩陣
                for (int j = 0; j < ants[i].getTabu().size() - 1; j++) {
                    ants[i].getDelta()[ants[i].getTabu().get(j).intValue()][ants[i]
                            .getTabu().get(j + 1).intValue()] = (float) (1. / ants[i]
                            .getTourLength());
                    ants[i].getDelta()[ants[i].getTabu().get(j + 1).intValue()][ants[i]
                            .getTabu().get(j).intValue()] = (float) (1. / ants[i]
                            .getTourLength());
                }
            }
            // 更新資訊素
            updatePheromone();

            for (int i = 0; i < antNum; i++) {
                ants[i].init(distance, alpha, beta,ants[i].getFirstCity(),ants[i].getLastCity());
            }
        }

        // 列印最佳結果
         writeToFile();
         System.out.flush();
    }

    // 更新資訊素
    private void updatePheromone() {
        // 資訊素揮發
        for (int i = 0; i < cityNum; i++)
            for (int j = 0; j < cityNum; j++)
//                pheromone[i][j] = pheromone[i][j] * (1 - rho);
                pheromone[i][j] = pheromone[i][j] * rho ;
        // 資訊素更新
        for (int i = 0; i < cityNum; i++) {
            for (int j = 0; j < cityNum; j++) {
                for (int k = 0; k < antNum; k++) {
                    pheromone[i][j] += ants[k].getDelta()[i][j];
                }
            }
        }
    }

    //輸出到檔案
    private void writeToFile(){
        File of = new File("/home/coco/Downloads/result.txt");
        try {
            FileWriter fw = new FileWriter(of,true);
            fw.append("The optimal length is: " + bestLength + "\r\n");
            fw.append("The optimal tour is:");
            for (int i = 0; i < cityNum; i++) {
                fw.append(bestTour[i] + " ");
            }
            fw.append("\r\n");
            fw.close();
        }catch(Exception e){e.printStackTrace();}
    }


    /**
     * @param args
     * @throws IOException
     */
    public static void main(String[] args) throws Exception {
        System.out.println("Start....");
        ACO aco = new ACO(48, 100, 300, 1.f, 5.f, 0.5f);

        for (int i = 0; i < 40; i++) {
            int firstCity = (int)(Math.random()*48);
            int lastCity = (int)(Math.random()*48);
                        System.out.println("第"+ i+ "對起點終點\tfirstCity:"+firstCity + "\tlastCity:"+lastCity);
            System.err.flush();
            aco.init("/home/coco/Downloads/data.txt",firstCity,lastCity);
            aco.solve();
        }

    }

}