一、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),所有的節點一次並且回到起始節點,使得串連這些節點的路徑成本最低。
二、爬山演算法
爬山演算法是一種局部擇優的方法,採用啟發學習法方法,是對深度優先搜尋的一種改進,它利用反饋資訊協助產生解的決策。 該演算法每次從當前解的臨近解空間中選擇一個最優解作為當前解,直到達到一個局部最優解。屬於人工智慧演算法的一種。
爬山演算法實現很簡單,其主要缺點是會陷入局部最優解,而不一定能搜尋到全域最優解。如所示:假設C點為當前解,爬山演算法搜尋到A點這個局部最優解就會停止搜尋,因為在A點無論向那個方向小幅度移動都不能得到更優的解。
爬山演算法實施步驟:
三、爬山演算法求解TSP問題
在該JAVA實現中我們選擇使用tsplib上的資料att48,這是一個對稱TSP問題,城市規模為48,其最優值為10628.其距離計算方法所示:
具體代碼如下:
package noah;import java.io.BufferedReader;import java.io.FileInputStream;import java.io.IOException;import java.io.InputStreamReader;import java.util.Random;public class HillClimbing {private int MAX_GEN;// 迭代次數private int cityNum; // 城市數量,編碼長度private int[][] distance; // 距離矩陣private int bestT;// 最佳出現代數private int[] bestGh;// 最好的路徑編碼private int bestEvaluation;private Random random;public HillClimbing() {}/** * constructor of GA * * @param n * 城市數量 * @param g * 運行代數 * **/public HillClimbing(int n, int g) {cityNum = n;MAX_GEN = g;}// 給編譯器一條指令,告訴它對被批註的代碼元素內部的某些警告保持靜默@SuppressWarnings("resource")/** * 初始化HillClimbing演算法類 * @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 1453strbuff = data.readLine();// 字元分割String[] strcol = strbuff.split(" ");x[i] = Integer.valueOf(strcol[1]);// x座標y[i] = Integer.valueOf(strcol[2]);// y座標}// 計算距離矩陣// 針對具體問題,距離計算方法也不一樣,// 此處用的是att48作為案例,它有48個城市,距離計算方法為偽歐氏距離,最優值為10628for (int i = 0; i < cityNum - 1; i++) {distance[i][i] = 0; // 對角線為0for (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;bestGh = new int[cityNum];bestEvaluation = Integer.MAX_VALUE;bestT = 0;random = new Random(System.currentTimeMillis());}// 初始化編碼Ghhvoid initGroup() {int i, j;bestGh[0] = random.nextInt(65535) % cityNum;for (i = 1; i < cityNum;)// 編碼長度{bestGh[i] = random.nextInt(65535) % cityNum;for (j = 0; j < i; j++) {if (bestGh[i] == bestGh[j]) {break;}}if (j == i) {i++;}}}public int evaluate(int[] chr) {int len = 0;// 染色體,起始城市,城市1,城市2...城市nfor (int i = 1; i < cityNum; i++) {len += distance[chr[i - 1]][chr[i]];}// 城市n,起始城市len += distance[chr[cityNum - 1]][chr[0]];return len;}// 爬山演算法public void pashan(int[] Gh, int T) {int i, temp, tt = 0;int ran1, ran2;int e;// 評價新值int[] tempGh = new int[cityNum];bestEvaluation = evaluate(Gh);// 爬山代數Tfor (tt = 0; tt < T; tt++) {for (i = 0; i < cityNum; i++) {tempGh[i] = Gh[i];}ran1 = random.nextInt(65535) % cityNum;ran2 = random.nextInt(65535) % cityNum;while (ran1 == ran2) {ran2 = random.nextInt(65535) % cityNum;}// 兩交換法實施鄰網域作業temp = tempGh[ran1];tempGh[ran1] = tempGh[ran2];tempGh[ran2] = temp;e = evaluate(tempGh);// 評價新值if (e < bestEvaluation) {bestT = tt;bestEvaluation = e;for (i = 0; i < cityNum; i++) {Gh[i] = tempGh[i];}}}}public void solve() {initGroup();// 初始化編碼pashan(bestGh, MAX_GEN);System.out.println("最佳長度出現代數:");System.out.println(bestT);System.out.println("最佳長度");System.out.println(bestEvaluation);System.out.println("最佳路徑:");for (int i = 0; i < cityNum; i++) {System.out.print(bestGh[i] + ",");if (i % 10 == 0 && i != 0) {System.out.println();}}}/** * @param args * @throws IOException */public static void main(String[] args) throws IOException {System.out.println("Start....");HillClimbing hillClimbing = new HillClimbing(48, 5000);hillClimbing.init("c://data.txt");hillClimbing.solve();}}
運行結果:
四、總結
爬山演算法由於其簡單的結構,在處理多約束大規模問題時比較力不從心,很難得到較好的解,但在小規模的NP問題求解中,解的品質還是比較好的;此外爬山演算法結構簡單,在某些情況下,整體效率比A星演算法的效果還好。
註:本文部分內容來源於網路,但程式以及分析結果屬於本人成果,轉載請註明!