原文位址: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.其距離計算方法下圖所示:
具體代碼如下:
[java]
view plain
copy
- package noah;
- import java.util.Random;
- import java.util.Vector;
- public class Ant implements Cloneable {
- private Vector<Integer> tabu; // 禁忌表
- private Vector<Integer> allowedCities; // 允許搜尋的城市
- private float[][] delta; // 資訊數變化矩陣
- private int[][] distance; // 距離矩陣
- private float alpha;
- private float beta;
- private int tourLength; // 路徑長度
- private int cityNum; // 城市數量
- private int firstCity; // 起始城市
- private int currentCity; // 目前城市
- public Ant() {
- cityNum = 30;
- tourLength = 0;
- }
- /**
- * Constructor of Ant
- *
- * @param num
- * 螞蟻數量
- */
- public Ant(int num) {
- cityNum = num;
- * 初始化螞蟻,随機選擇起始位置
- * @param distance
- * 距離矩陣
- * @param a
- * alpha
- * @param b
- * beta
- public void init(int[][] distance, float a, float b) {
- alpha = a;
- beta = b;
- // 初始允許搜尋的城市集合
- allowedCities = new Vector<Integer>();
- // 初始禁忌表
- tabu = new Vector<Integer>();
- // 初始距離矩陣
- this.distance = distance;
- // 初始資訊數變化矩陣為0
- delta = new float[cityNum][cityNum];
- for (int i = 0; i < cityNum; i++) {
- Integer integer = new Integer(i);
- allowedCities.add(integer);
- for (int j = 0; j < cityNum; j++) {
- delta[i][j] = 0.f;
- }
- }
- // 随機挑選一個城市作為起始城市
- Random random = new Random(System.currentTimeMillis());
- firstCity = random.nextInt(cityNum);
- // 允許搜尋的城市集合中移除起始城市
- for (Integer i : allowedCities) {
- if (i.intValue() == firstCity) {
- allowedCities.remove(i);
- break;
- // 将起始城市添加至禁忌表
- tabu.add(Integer.valueOf(firstCity));
- // 目前城市為起始城市
- currentCity = firstCity;
- * 選擇下一個城市
- * @param pheromone
- * 資訊素矩陣
- public void selectNextCity(float[][] pheromone) {
- float[] p = new float[cityNum];
- float sum = 0.0f;
- // 計算分母部分
- sum += Math.pow(pheromone[currentCity][i.intValue()], alpha)
- * Math.pow(1.0 / distance[currentCity][i.intValue()], beta);
- // 計算機率矩陣
- boolean flag = false;
- for (Integer j : allowedCities) {
- if (i == j.intValue()) {
- p[i] = (float) (Math.pow(pheromone[currentCity][i], alpha) * Math
- .pow(1.0 / distance[currentCity][i], beta)) / sum;
- flag = true;
- break;
- }
- if (flag == false) {
- p[i] = 0.f;
- // 輪盤賭選擇下一個城市
- float sleectP = random.nextFloat();
- int selectCity = 0;
- float sum1 = 0.f;
- sum1 += p[i];
- if (sum1 >= sleectP) {
- selectCity = i;
- // 從允許選擇的城市中去除select city
- if (i.intValue() == selectCity) {
- // 在禁忌表中添加select city
- tabu.add(Integer.valueOf(selectCity));
- // 将目前城市改為選擇的城市
- currentCity = selectCity;
- * 計算路徑長度
- * @return 路徑長度
- private int calculateTourLength() {
- int len = 0;
- //禁忌表tabu最終形式:起始城市,城市1,城市2...城市n,起始城市
- len += distance[this.tabu.get(i).intValue()][this.tabu.get(i + 1)
- .intValue()];
- return len;
- public Vector<Integer> getAllowedCities() {
- return allowedCities;
- public void setAllowedCities(Vector<Integer> allowedCities) {
- this.allowedCities = allowedCities;
- public int getTourLength() {
- tourLength = calculateTourLength();
- return tourLength;
- public void setTourLength(int tourLength) {
- this.tourLength = tourLength;
- public int getCityNum() {
- return cityNum;
- public void setCityNum(int cityNum) {
- this.cityNum = cityNum;
- public Vector<Integer> getTabu() {
- return tabu;
- public void setTabu(Vector<Integer> tabu) {
- this.tabu = tabu;
- public float[][] getDelta() {
- return delta;
- public void setDelta(float[][] delta) {
- this.delta = delta;
- public int getFirstCity() {
- return firstCity;
- public void setFirstCity(int firstCity) {
- this.firstCity = firstCity;
- }
[java]
- import java.io.BufferedReader;
- import java.io.FileInputStream;
- import java.io.IOException;
- import java.io.InputStreamReader;
- public class ACO {
- private Ant[] ants; // 螞蟻
- private int antNum; // 螞蟻數量
- private int MAX_GEN; // 運作代數
- private float[][] pheromone; // 資訊素矩陣
- private int bestLength; // 最佳長度
- private int[] bestTour; // 最佳路徑
- // 三個參數
- private float rho;
- public ACO() {
- * constructor of ACO
- * @param n
- * 城市數量
- * @param m
- * @param g
- * 運作代數
- * @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;
- rho = r;
- // 給編譯器一條指令,告訴它對被批注的代碼元素内部的某些警告保持靜默
- @SuppressWarnings("resource")
- * 初始化ACO算法類
- * @param filename 資料檔案名,該檔案存儲所有城市節點坐标資料
- * @throws IOException
- private void init(String filename) 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];
- // 讀取一行資料,資料格式1 6734 1453
- strbuff = data.readLine();
- // 字元分割
- String[] strcol = strbuff.split(" ");
- x[i] = Integer.valueOf(strcol[1]);// x坐标
- y[i] = Integer.valueOf(strcol[2]);// 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[cityNum - 1][cityNum - 1] = 0;
- // 初始化資訊素矩陣
- pheromone = new float[cityNum][cityNum];
- 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);
- public void solve() {
- // 疊代MAX_GEN次
- for (int g = 0; g < MAX_GEN; 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,起始城市
- ants[i].getTabu().add(ants[i].getFirstCity());
- // 檢視這隻螞蟻行走路徑距離是否比目前距離優秀
- if (ants[i].getTourLength() < bestLength) {
- // 比目前優秀則拷貝優秀TSP路徑
- bestLength = ants[i].getTourLength();
- for (int k = 0; k < cityNum + 1; k++) {
- bestTour[k] = ants[i].getTabu().get(k).intValue();
- }
- // 更新這隻螞蟻的資訊數變化矩陣,對稱矩陣
- for (int j = 0; j < cityNum; 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]
- // 更新資訊素
- updatePheromone();
- // 重新初始化螞蟻
- ants[i].init(distance, alpha, beta);
- // 列印最佳結果
- printOptimal();
- // 更新資訊素
- 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);
- // 資訊素更新
- for (int k = 0; k < antNum; k++) {
- pheromone[i][j] += ants[k].getDelta()[i][j];
- private void printOptimal() {
- System.out.println("The optimal length is: " + bestLength);
- System.out.println("The optimal tour is: ");
- for (int i = 0; i < cityNum + 1; i++) {
- System.out.println(bestTour[i]);
- * @param args
- public static void main(String[] args) throws IOException {
- System.out.println("Start....");
- ACO aco = new ACO(48, 10, 100, 1.f, 5.f, 0.5f);
- aco.init("c://data.txt");
- aco.solve();
運作結果截圖: