基於蟻群演算法求解求解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 Robber(JAVA)
題目描述: 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++:蟻群演算法解決TSP(C++多執行緒版)
TSP問題:旅行商問題,最短迴路。 這裡採用att48資料,鄰接矩陣全部取整數,原資料放在文後。 解決程式碼如下: //#define TEST_INPUT //#define TEST_T //#define TEST_ANT //#define TEST_VALUE #
旅行商問題TSP(蟻群演算法Java)
旅行商問題,即TSP問題(Traveling Salesman Problem)是數學領域中著名問題之一。假設有一個旅行商人要拜訪N個城市,他必須選擇所要走的路徑,路徑的限制是每個城市只能拜訪一次,而且最後要 回到原來出發的城市。路徑的選擇目標是要求得的路徑路程為所有路徑之中