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

weixin_34293059發表於2013-05-03

這兩天一直在查詢演算法問題之類的問題,現在正好有機會和大家分享一下.

    一、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問題時消耗的盤算資源比相其他啟發式演算法要多,因而顯得效率很低下,而當問題趨向於大規模時,蟻群演算法還是存在難收斂的問題,個人感覺除非你真想消耗量大盤算資源來幹一件事件,否則還是慎用蟻群演算法。

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

    

文章結束給大家分享下程式設計師的一些笑話語錄: 關於程式語言
如果 C++是一把錘子的話,那麼程式設計就會變成大手指頭。
如果你找了一百萬只猴子來敲打一百萬個鍵盤,那麼會有一隻猴子會敲出一 段 Java 程式,而其餘的只會敲出 Perl 程式。
一陣急促的敲門聲,“誰啊!”,過了 5 分鐘,門外傳來“Java”。
如果說 Java 很不錯是因為它可以執行在所有的作業系統上,那麼就可以說 肛交很不錯,因為其可以使用於所有的性別上。

相關文章