1. 程式人生 > >基於蟻群演算法求解求解TSP問題(JAVA)

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

一、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.其距離計算方法下圖所示:

具體程式碼如下:

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;
		tourLength = 0;
	}

	/**
	 * 初始化螞蟻,隨機選擇起始位置
	 * 
	 * @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;
		// 計算分母部分
		for (Integer i : allowedCities) {
			sum += Math.pow(pheromone[currentCity][i.intValue()], alpha)
					* Math.pow(1.0 / distance[currentCity][i.intValue()], beta);
		}
		// 計算概率矩陣
		for (int i = 0; i < cityNum; i++) {
			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;
			}
		}
		// 輪盤賭選擇下一個城市
		Random random = new Random(System.currentTimeMillis());
		float sleectP = random.nextFloat();
		int selectCity = 0;
		float sum1 = 0.f;
		for (int i = 0; i < cityNum; i++) {
			sum1 += p[i];
			if (sum1 >= sleectP) {
				selectCity = i;
				break;
			}
		}
		// 從允許選擇的城市中去除select city
		for (Integer i : allowedCities) {
			if (i.intValue() == selectCity) {
				allowedCities.remove(i);
				break;
			}
		}
		// 在禁忌表中新增select city
		tabu.add(Integer.valueOf(selectCity));
		// 將當前城市改為選擇的城市
		currentCity = selectCity;
	}

	/**
	 * 計算路徑長度
	 * 
	 * @return 路徑長度
	 */
	private int calculateTourLength() {
		int len = 0;
		//禁忌表tabu最終形式:起始城市,城市1,城市2...城市n,起始城市
		for (int i = 0; i < cityNum; i++) {
			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;
	}

}
package noah;

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 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;
	}

	// 給編譯器一條指令,告訴它對被批註的程式碼元素內部的某些警告保持靜默
	@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];
		for (int i = 0; i < cityNum; i++) {
			// 讀取一行資料,資料格式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[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);
		}
	}

	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]
							.getTourLength());
				}
			}
			// 更新資訊素
			updatePheromone();
			// 重新初始化螞蟻
			for (int i = 0; i < antNum; i++) {
				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 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 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
	 * @throws IOException
	 */
	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();
	}

}

執行結果截圖:

四、總結

蟻群演算法是一種本質上並行的演算法。每隻螞蟻搜尋的過程彼此獨立,僅通過資訊激素進行通訊。所以蟻群演算法則可以看作是一個分散式的多agent系統,它在問題空間的多點同時開始進行獨立的解搜尋,不僅增加了演算法的可靠性,也使得演算法具有較強的全域性搜尋能力,但是也正是由於其並行性的本質,蟻群演算法的搜尋時間較長,在求解小規模的NP問題時耗費的計算資源相比其他啟發式演算法要多,因而顯得效率很低下,而當問題趨向於大規模時,蟻群演算法還是存在難收斂的問題,個人感覺除非你真想耗費大量計算資源來幹一件事情,否則還是慎用蟻群演算法。

注:本文部分內容來源於網路,但程式以及分析結果屬於本人成果,轉載請註明!


相關推薦

基於演算法求解求解TSP問題JAVA

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

粒子演算法的matlab實現

clc;clear;close all; %% 初始化種群 f= @(x)x .* sin(x) .* cos(2 * x) - 2 * x .* sin(3 * x); % 函式表示式 figure(1);ezplot(f,[0,0.01,20]); N = 50;

粒子演算法的matlab實現

      上一次的部落格中我將粒子群的搜尋過程可視化了,並將其轉存為了gif格式檔案,這個過程我先在這裡給大家講一下: 1.首先pause(),是在每次繪圖之後暫停一段時間,單位是秒,再進行下一次繪圖; 2.而當要轉存為gif檔案時,這其實就是一種無聲的視訊檔案,因此我們

Fibonacci序列遞迴演算法與遞推Java

Fibonacci遞推公式: f(1) = f(2) = 1;f(n) = f(n-1)+f(n-2)(n>2).在這裡取他除以10007的餘數 遞迴 public class Fibonacci { static int digui(int n) {

藍橋杯 ADV-188 演算法提高 排列數java 深度優先搜尋 DFS

 演算法提高 排列數   時間限制:1.0s   記憶體限制:256.0MB 問題描述   0、1、2三個數字的全排列有六種,按照字母序排列如下:   012、021、102、120、201、210   輸入一個數n   求0~9十個數的全排列中的第n個(

分治法在排序演算法中的應用JAVA--快速排序Lomuto劃分、Hoare劃分、隨機化快排

分治法在排序演算法中的應用 快速排序:時間複雜度O(nlogn) 如果說歸併排序是按照元素在陣列中的位置劃分的話,那麼快速排序就是按照元素的值進行劃分。劃分方法由兩種,本節將主要介紹Huare劃分,在減治法在查詢演算法中的應用(JAVA)--快速查詢這篇文章中講述了Lomu

演算法-藍橋杯-演算法提高 矩陣翻轉 JAVA

1 引言    這是藍橋杯演算法提高部分的第一篇文章。通過做演算法提高部分的習題,我深刻地認識到自己在演算法方面的不足,在資料結構方面還有待提高。客觀上來講,自己並沒有一直在做演算法方面的訓練,所以許多題目沒有思路或者說有思路不知道如何實現。另一方面,就是自己在演算法方面投入

藍橋杯 ADV-127 演算法提高 日期計算java

 演算法提高 日期計算   時間限制:1.0s   記憶體限制:256.0MB 問題描述   已知2011年11月11日是星期五,問YYYY年MM月DD日是星期幾?注意考慮閏年的情況。尤其是逢百年不閏,逢400年閏的情況。 輸入格式   輸入只有一行

減治法在查詢演算法中的應用JAVA--快速查詢

減治法在查詢演算法中的應用 快速查詢:選擇問題是求一個n個數列表的第k個最小元素的問題,這個數k被稱為順序統計量。對於k=1或k=n來說,這並沒有什麼意義,我們通常會要找出這樣的元素:該元素比列表中一

漢諾塔演算法問題的解法Java、思路以及舉一反三

首先,先放程式碼,講解以及註釋將會在後文裡單獨寫出來public class hnt { public static void main(String[] args) { hnts("a","b","c",3); } public static void hnt

藍橋杯-演算法訓練-sign函式java

問題描述   給定實數x,輸出sign(x)的值。   sign(x)是符號函式,如果x>0,則返回1;如果x=0,則返回0;如果x<0,則返回-1。 輸入格式   一行一個實數x。 輸出

演算法-藍橋杯-演算法訓練 表示式計算 JAVA

1 引言什麼題。。。2 題目問題描述  輸入一個只包含加減乖除和括號的合法表示式,求表示式的值。其中除表示整除。輸入格式  輸入一行,包含一個表示式。輸出格式  輸出這個表示式的值。樣例輸入1-2+3*(4-5)樣例輸出-4資料規模和約定  表示式長度不超過100,表示式運算

劍指offer演算法8 跳臺階JAVA

這個問題歸根結底還是一個費布拉奇數列,仔細找一下規律即可,剛開始做的時候我是直接寫出前六個數的結果來找規律的。 一級臺階:1種 fib(1)=1  二級臺階:2種 fib(2)=2  三級臺階:3種 fib(3)=fib(1)+fib(2)=3  四級臺階:5種 fib(4

演算法題——House RobberJAVA

題目描述: You are a professional robber planning to rob houses along a street. Each house has a certain amount of money stashed, the

【機器學習】利用演算法求解旅行商TSP問題

如果喜歡這裡的內容,你能夠給我最大的幫助就是轉發,告訴你的朋友,鼓勵他們一起來學習。 If you like the content here, you can give me the greatest help is forwarding, tell you

粒子演算法求解旅行商問題TSP JAVA實現

粒子群演算法求解旅行商問題TSP 寫在開頭: 最近師妹的結課作業問我,關於使用粒子群求解TSP問題的思路。我想了想,自己去年的作業用的是遺傳演算法,貌似有些關聯,索性給看了看程式碼。重新學習了一遍粒子群演算法,在這裡記錄一下,算是對知識的總結,鞏固一下。

基於貪心演算法求解TSP問題JAVA

前段時間在搞貪心演算法,為了舉例,故拿TSP來開刀,寫了段求解演算法程式碼以便有需之人,注意程式碼考慮可讀性從最容易理解角度寫,沒有優化,有需要可以自行優化!一、TSP問題TSP問題(Travelling Salesman Problem)即旅行商問題,又譯為旅行推銷員問題、

基於爬山演算法求解TSP問題JAVA

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

C++:演算法解決TSPC++多執行緒版

TSP問題:旅行商問題,最短迴路。 這裡採用att48資料,鄰接矩陣全部取整數,原資料放在文後。 解決程式碼如下: //#define TEST_INPUT //#define TEST_T //#define TEST_ANT //#define TEST_VALUE #

旅行商問題TSP演算法Java

旅行商問題,即TSP問題(Traveling Salesman Problem)是數學領域中著名問題之一。假設有一個旅行商人要拜訪N個城市,他必須選擇所要走的路徑,路徑的限制是每個城市只能拜訪一次,而且最後要 回到原來出發的城市。路徑的選擇目標是要求得的路徑路程為所有路徑之中