基于爬山算法求解TSP问题(JAVA)

 

基于爬山算法求解TSP问题(JAVA)

分类: 算法   1101人阅读  评论(2)  收藏  举报

一、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.其距离计算方法下图所示:

具体代码如下:

[java]  view plain copy
  1. package noah;  
  2.   
  3. import java.io.BufferedReader;  
  4. import java.io.FileInputStream;  
  5. import java.io.IOException;  
  6. import java.io.InputStreamReader;  
  7. import java.util.Random;  
  8.   
  9. public class HillClimbing {  
  10.   
  11.     private int MAX_GEN;// 迭代次数  
  12.     private int cityNum; // 城市数量,编码长度  
  13.     private int[][] distance; // 距离矩阵  
  14.     private int bestT;// 最佳出现代数  
  15.     private int[] bestGh;// 最好的路径编码  
  16.     private int bestEvaluation;  
  17.   
  18.     private Random random;  
  19.   
  20.     public HillClimbing() {  
  21.   
  22.     }  
  23.   
  24.     /** 
  25.      * constructor of GA 
  26.      *  
  27.      * @param n 
  28.      *            城市数量 
  29.      * @param g 
  30.      *            运行代数 
  31.      *  
  32.      **/  
  33.     public HillClimbing(int n, int g) {  
  34.         cityNum = n;  
  35.         MAX_GEN = g;  
  36.     }  
  37.   
  38.     // 给编译器一条指令,告诉它对被批注的代码元素内部的某些警告保持静默  
  39.     @SuppressWarnings("resource")  
  40.     /** 
  41.      * 初始化HillClimbing算法类 
  42.      * @param filename 数据文件名,该文件存储所有城市节点坐标数据 
  43.      * @throws IOException 
  44.      */  
  45.     private void init(String filename) throws IOException {  
  46.         // 读取数据  
  47.         int[] x;  
  48.         int[] y;  
  49.         String strbuff;  
  50.         BufferedReader data = new BufferedReader(new InputStreamReader(  
  51.                 new FileInputStream(filename)));  
  52.         distance = new int[cityNum][cityNum];  
  53.         x = new int[cityNum];  
  54.         y = new int[cityNum];  
  55.         for (int i = 0; i < cityNum; i++) {  
  56.             // 读取一行数据,数据格式1 6734 1453  
  57.             strbuff = data.readLine();  
  58.             // 字符分割  
  59.             String[] strcol = strbuff.split(" ");  
  60.             x[i] = Integer.valueOf(strcol[1]);// x坐标  
  61.             y[i] = Integer.valueOf(strcol[2]);// y坐标  
  62.         }  
  63.         // 计算距离矩阵  
  64.         // 针对具体问题,距离计算方法也不一样,  
  65.         // 此处用的是att48作为案例,它有48个城市,距离计算方法为伪欧氏距离,最优值为10628  
  66.         for (int i = 0; i < cityNum - 1; i++) {  
  67.             distance[i][i] = 0// 对角线为0  
  68.             for (int j = i + 1; j < cityNum; j++) {  
  69.                 double rij = Math  
  70.                         .sqrt(((x[i] - x[j]) * (x[i] - x[j]) + (y[i] - y[j])  
  71.                                 * (y[i] - y[j])) / 10.0);  
  72.                 // 四舍五入,取整  
  73.                 int tij = (int) Math.round(rij);  
  74.                 if (tij < rij) {  
  75.                     distance[i][j] = tij + 1;  
  76.                     distance[j][i] = distance[i][j];  
  77.                 } else {  
  78.                     distance[i][j] = tij;  
  79.                     distance[j][i] = distance[i][j];  
  80.                 }  
  81.             }  
  82.         }  
  83.         distance[cityNum - 1][cityNum - 1] = 0;  
  84.   
  85.         bestGh = new int[cityNum];  
  86.         bestEvaluation = Integer.MAX_VALUE;  
  87.         bestT = 0;  
  88.   
  89.         random = new Random(System.currentTimeMillis());  
  90.     }  
  91.   
  92.     // 初始化编码Ghh  
  93.     void initGroup() {  
  94.         int i, j;  
  95.         bestGh[0] = random.nextInt(65535) % cityNum;  
  96.         for (i = 1; i < cityNum;)// 编码长度  
  97.         {  
  98.             bestGh[i] = random.nextInt(65535) % cityNum;  
  99.             for (j = 0; j < i; j++) {  
  100.                 if (bestGh[i] == bestGh[j]) {  
  101.                     break;  
  102.                 }  
  103.             }  
  104.             if (j == i) {  
  105.                 i++;  
  106.             }  
  107.         }  
  108.     }  
  109.   
  110.     public int evaluate(int[] chr) {  
  111.         int len = 0;  
  112.         // 染色体,起始城市,城市1,城市2...城市n  
  113.         for (int i = 1; i < cityNum; i++) {  
  114.             len += distance[chr[i - 1]][chr[i]];  
  115.         }  
  116.         // 城市n,起始城市  
  117.         len += distance[chr[cityNum - 1]][chr[0]];  
  118.         return len;  
  119.     }  
  120.   
  121.     // 爬山算法  
  122.     public void pashan(int[] Gh, int T) {  
  123.         int i, temp, tt = 0;  
  124.         int ran1, ran2;  
  125.         int e;// 评价新值  
  126.         int[] tempGh = new int[cityNum];  
  127.         bestEvaluation = evaluate(Gh);  
  128.   
  129.         // 爬山代数T  
  130.         for (tt = 0; tt < T; tt++) {  
  131.             for (i = 0; i < cityNum; i++) {  
  132.                 tempGh[i] = Gh[i];  
  133.             }  
  134.             ran1 = random.nextInt(65535) % cityNum;  
  135.             ran2 = random.nextInt(65535) % cityNum;  
  136.             while (ran1 == ran2) {  
  137.                 ran2 = random.nextInt(65535) % cityNum;  
  138.             }  
  139.   
  140.             // 两交换法实施邻域操作  
  141.             temp = tempGh[ran1];  
  142.             tempGh[ran1] = tempGh[ran2];  
  143.             tempGh[ran2] = temp;  
  144.   
  145.             e = evaluate(tempGh);// 评价新值  
  146.   
  147.             if (e < bestEvaluation) {  
  148.                 bestT = tt;  
  149.                 bestEvaluation = e;  
  150.                 for (i = 0; i < cityNum; i++) {  
  151.                     Gh[i] = tempGh[i];  
  152.                 }  
  153.             }  
  154.         }  
  155.   
  156.     }  
  157.   
  158.     public void solve() {  
  159.         initGroup();// 初始化编码  
  160.         pashan(bestGh, MAX_GEN);  
  161.   
  162.         System.out.println("最佳长度出现代数:");  
  163.         System.out.println(bestT);  
  164.         System.out.println("最佳长度");  
  165.         System.out.println(bestEvaluation);  
  166.         System.out.println("最佳路径:");  
  167.         for (int i = 0; i < cityNum; i++) {  
  168.             System.out.print(bestGh[i] + ",");  
  169.             if (i % 10 == 0 && i != 0) {  
  170.                 System.out.println();  
  171.             }  
  172.         }  
  173.     }  
  174.   
  175.     /** 
  176.      * @param args 
  177.      * @throws IOException 
  178.      */  
  179.     public static void main(String[] args) throws IOException {  
  180.         System.out.println("Start....");  
  181.         HillClimbing hillClimbing = new HillClimbing(485000);  
  182.         hillClimbing.init("c://data.txt");  
  183.         hillClimbing.solve();  
  184.     }  
  185. }  

运行结果截图:

四、总结

爬山算法由于其简单的结构,在处理多约束大规模问题时比较力不从心,很难得到较好的解,但在小规模的NP问题求解中,解的质量还是比较好的;此外爬山算法结构简单,在某些情况下,整体效率比A星算法的效果还好。

注:本文部分内容来源于网络,但程序以及分析结果属于本人成果,转载请注明!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值