1 问题描述
何为旅行商问题?按照非专业的说法,这个问题要求找出一条n个给定的城市间的最短路径,使我们在回到触发的城市之前,对每个城市都只访问一次。这样该问题就可以表述为求一个图的最短哈密顿回路的问题。(哈密顿回路:定义为一个对图的每个顶点都只穿越一次的回路)

很容易看出来,哈密顿回路也可以定义为n+1个相邻顶点v1,v2,v3,…,vn,v1的一个序列。其中,序列的第一个顶点和最后一个顶点是相同的,而其它n-1个顶点都是互不相同的。并且,在不失一般性的前提下,可以假设,所有的回路都开始和结束于相同的特定顶点。因此,可以通过生成n-1个中间城市的组合来得到所有的旅行线路,计算这些线路的长度,然后求取最短的线路。下图是该问题的一个小规模实例,并用该方法得到了它的解,

2 解决方案
2.1 蛮力法

此处使用蛮力法解决旅行商问题,取的是4个城市规模,并已经定义好各个城市之间的距离(PS:该距离使用二维数组初始化定义,此处的距离是根据图1中所示距离定义)。此处主要是在体验使用蛮力法解决该问题的思想,如要丰富成普遍规模问题,还请大家自己稍微修改一下哒。对于代码中如碰到不能理解的地方,可以参考文章末尾给出的参考资料链接,以及相关代码注解~

具体代码如下:

package com.liuzhen.chapterThree;public class TravelingSalesman {public int count = 0;     //定义全局变量,用于计算当前已行走方案次数,初始化为0public int MinDistance = 100;    //定义完成一个行走方案的最短距离,初始化为100(PS:100此处表示比实际要大很多的距离)public int[][] distance = {{0,2,5,7},{2,0,8,3},{5,8,0,1},{7,3,1,0}};   //使用二维数组的那个音图的路径相关距离长度/** start为开始进行排序的位置* step为当前正在行走的位置* n为需要排序的总位置数* Max为n!值*/public void Arrange(int[] A,int start,int step,int n,int Max){if(step == n){   // 当正在行走的位置等于城市总个数时++count;           //每完成一次行走方案,count自增1printArray(A);     //输出行走路线方案及其总距离}if(count == Max)System.out.println("已完成全部行走方案!!!,最短路径距离为:"+MinDistance);  else{for(int i = start;i < n;i++){   /*第i个数分别与它后面的数字交换就能得到新的排列,从而能够得到n!次不同排序方案* (PS:此处代码中递归的执行顺序有点抽象,具体解释详见本人另一篇博客:)  *算法笔记_017:递归执行顺序的探讨(Java)
*/swapArray(A,start,i);Arrange(A,start+1,step+1,n,Max);swapArray(A,i,start);}}}//交换数组中两个位置上的数值public  void swapArray(int[] A,int p,int q){int temp = A[p];A[p] = A[q];A[q] = temp;}//输出数组A的序列,并输出当前行走序列所花距离,并得到已完成的行走方案中最短距离public void printArray(int[] A){for(int i = 0;i < A.length;i++)   //输出当前行走方案的序列System.out.print(A[i]+"  ");int tempDistance = distance[A[0]][A[3]];     //此处是因为,最终要返回出发地城市,所以总距离要加上最后到达的城市到出发点城市的距离for(int i = 0;i < (A.length-1);i++)   //输出当前行走方案所花距离tempDistance += distance[A[i]][A[i+1]];if(MinDistance > tempDistance)   //返回当前已完成方案的最短行走距离MinDistance = tempDistance;System.out.println("  行走路程总和:"+tempDistance);}public static void main(String[] args){int[] A = {0,1,2,3};TravelingSalesman test = new TravelingSalesman();test.Arrange(A,0,0,4,24);    //此处Max = 4!=24}
}

运行结果:

1  2  3    行走路程总和:181  3  2    行走路程总和:112  1  3    行走路程总和:232  3  1    行走路程总和:113  2  1    行走路程总和:183  1  2    行走路程总和:230  2  3    行走路程总和:110  3  2    行走路程总和:182  0  3    行走路程总和:232  3  0    行走路程总和:183  2  0    行走路程总和:113  0  2    行走路程总和:231  0  3    行走路程总和:181  3  0    行走路程总和:230  1  3    行走路程总和:110  3  1    行走路程总和:233  0  1    行走路程总和:183  1  0    行走路程总和:111  2  0    行走路程总和:231  0  2    行走路程总和:112  1  0    行走路程总和:182  0  1    行走路程总和:110  2  1    行走路程总和:230  1  2    行走路程总和:18
已完成全部行走方案!!!,最短路径距离为:11

2.2 减治法
旅行商问题的核心,就是求n个不同城市的全排列,通俗一点的话,就是求1n的全排列。下面两种方法都是基于减治思想进行的,此处只实现求取1n的全排列。对于每一种排列,在旅行商问题中还得求取其相应路径长度,最后,在进行比较从而得到最短路径,对于求取最短路径的思想在2.1蛮力法中已经体现,此处不在重复,感兴趣的同学可以自己再动手实现一下~

2.2.1 Johson-Trotter算法
此处算法思想借用《算法设计与分析基础》第三版上讲解,具体如下:

package com.liuzhen.chapter4;import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;public class Arrange {//使用JohnsonTrotter算法获取1~n的全排列public HashMap<Integer , String> getJohnsonTrotter(int n){HashMap<Integer , String> hashMap = new HashMap<Integer , String>();int count = 0;                //用于计算生成排列的总个数,初始化为0int[] arrayN = new int[n];int[] directionN = new int[n+1];      //directionN[i]用于标记1~n中数字i上的箭头方向,初始化值为0,表示箭头方向向左;值为1 表示箭头方向向右for(int i = 0;i < n;i++)arrayN[i] = i+1;String result = getArrayString(arrayN);hashMap.put(count, result);        //将原始排列添加到哈希表中while(judgeMove(arrayN,directionN)){      //存在一个移动元素int maxI = getMaxMove(arrayN,directionN);if(directionN[arrayN[maxI]] == 0)      //箭头指向左方swap(arrayN,maxI,--maxI);if(directionN[arrayN[maxI]] == 1)       //箭头指向右方swap(arrayN,maxI,++maxI);for(int i = 0;i < n;i++){               //调转所有大于arrayN[maxI]的数的箭头方向if(arrayN[i] > arrayN[maxI]){if(directionN[arrayN[i]] == 0)directionN[arrayN[i]] = 1;elsedirectionN[arrayN[i]] = 0;}}count++;result = getArrayString(arrayN);hashMap.put(count, result);        //将得到的新排列添加到哈希表中}return hashMap;}//判断数组arrayN中是否存在可移动元素public boolean judgeMove(int[] arrayN,int[] directionN){boolean judge = false;for(int i = arrayN.length - 1;i >= 0;i--){if(directionN[arrayN[i]] == 0 && i != 0){     //当arrayN[i]数字上的箭头方向指向左边时if(arrayN[i] > arrayN[i-1])return true;}if(directionN[arrayN[i]] == 1 && i != (arrayN.length-1)){    //当arrayN[i]数字上的箭头方向指向右边时if(arrayN[i] > arrayN[i+1])return true;}}return judge;}//获取数组arrayN中最大的可移动元素的数组下标public int getMaxMove(int[] arrayN,int[] directionN){int result = 0;int temp = 0;for(int i = 0;i < arrayN.length;i++){if(directionN[arrayN[i]] == 0 && i != 0){     //当arrayN[i]数字上的箭头方向指向左边时if(arrayN[i] > arrayN[i-1]){int max = arrayN[i];if(max > temp)temp = max;}}if(directionN[arrayN[i]] == 1 && i != (arrayN.length-1)){    //当arrayN[i]数字上的箭头方向指向右边时if(arrayN[i] > arrayN[i+1]){int max = arrayN[i];if(max > temp)temp = max;}    }}for(int i = 0;i < arrayN.length;i++){if(arrayN[i] == temp)return i;}return result;}//交换数组中两个位置上的数值public void swap(int[] array,int m,int n){int temp = array[m];array[m] = array[n];array[n] = temp;}//把数组array中所有元素按照顺序以字符串结果返回public String getArrayString(int[] array){String result = "";for(int i = 0;i < array.length;i++)result = result + array[i];return result;}public static void main(String[] args){Arrange test = new Arrange();HashMap<Integer , String> hashMap = test.getJohnsonTrotter(3);Collection<String> c1 = hashMap.values();Iterator<String> ite = c1.iterator();while(ite.hasNext())System.out.println(ite.next());System.out.println(hashMap);}
}

运行结果:

132
321
213
{0=123, 1=132, 2=312, 3=321, 4=231, 5=213}

2.2.2 基于字典序的算法
此处算法思想也借用《算法设计与分析基础》第三版上讲解,具体如下:

package com.liuzhen.chapter4;import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;public class Arrange1 {public HashMap<Integer,String> getLexicographicPermute(int n){HashMap<Integer,String> hashMap = new HashMap<Integer,String>();int count = 0;         //用于计算生成排列的总个数,初始化为0int[] arrayN = new int[n];for(int i = 0;i < n;i++)arrayN[i] = i+1;String result = getArrayString(arrayN);hashMap.put(count, result);        //将原始排列添加到哈希表中while(riseTogetherArray(arrayN)){     //数组中存在两个连续的升序元素int i = getMaxI(arrayN);     //找出使得ai<ai+1的最大i: ai+1>ai+2>...>anint j = getMaxJ(arrayN);     //找到使得ai<aj的最大索引j: j>=i,因为ai<ai+1swap(arrayN,i,j);reverseArray(arrayN,i+1,arrayN.length-1);result = getArrayString(arrayN);count++;hashMap.put(count, result);        //将新得到的排列添加到哈希表中}System.out.println("排列总个数count = "+(count+1));return hashMap;}//判断数组中是否 包含两个连续的升序元素public boolean riseTogetherArray(int[] arrayN){boolean result = false;for(int i = 1;i < arrayN.length;i++){if(arrayN[i-1] < arrayN[i])return true;}return result;}//返回i:满足ai<ai+1,ai+1>ai+2>...>an(PS:an为数组中最后一个元素)public int getMaxI(int[] arrayN){int result = 0;for(int i = arrayN.length-1;i > 0;){if(arrayN[i-1] > arrayN[i])i--;elsereturn i-1;}return result;}//返回j:ai<aj的最大索引,j>=i+1,因为ai<ai+1(此处i值为上面函数getMaxI得到值)public int getMaxJ(int[] arrayN){int result = 0;int tempI = getMaxI(arrayN);for(int j = tempI+1;j < arrayN.length;){if(arrayN[tempI] < arrayN[j]){if(j == arrayN.length-1)return j;j++;}elsereturn j-1;}return result;}//交换数组中两个位置上的数值public void swap(int[] array,int m,int n){int temp = array[m];array[m] = array[n];array[n] = temp;}//将数组中a[m]到a[n]一段元素反序排列public void reverseArray(int[] arrayN,int m,int n){while(m < n){int temp = arrayN[m];arrayN[m++] = arrayN[n];arrayN[n--] = temp;}}//把数组array中所有元素按照顺序以字符串结果返回public String getArrayString(int[] array){String result = "";for(int i = 0;i < array.length;i++)result = result + array[i];return result;}public static void main(String[] args){Arrange1 test = new  Arrange1();HashMap<Integer,String> hashMap = test.getLexicographicPermute(3);Collection<String> c1 = hashMap.values();Iterator<String> ite = c1.iterator();while(ite.hasNext())System.out.println(ite.next());System.out.println(hashMap);}
}

运行结果:

排列总个数count = 6
132
231
321
{0=123, 1=132, 2=213, 3=231, 4=312, 5=321}

Java实现旅行商问题相关推荐

  1. Java:实现旅行商问题算法(附完整源码)

    Java:实现旅行商问题算 package com.williamfiset.algorithms.graphtheory;import java.util.ArrayList; import jav ...

  2. 遗传算法顺序交叉java,求解旅行商问题的顺序交叉多子代遗传算法

    求解旅行商问题的顺序交叉多子代遗传算法 [专利摘要]本发明公开了一种求解旅行商问题的顺序交叉多子代遗传算法.首先依据生物进化理论和数学生态学理论,提出了一种求解旅行商问题的顺序交叉多子代遗传算法,并给 ...

  3. 旅行商问题(动态规划_爬山算法_遗传算法)

    问题描述 ​ 旅行商问题(Travelling Salesman Problem, 简记TSP,亦称货郎担问题):设有n个城市和距离矩阵D=[dij],其中dij表示城市i到城市j的距离,i,j=1, ...

  4. java寻优算法_模拟退火算法SA原理及python、java、php、c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径...

    模拟退火算法SA原理及python.java.php.c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径 模拟退火算法(Simulated Annealing,SA)最早的思 ...

  5. 粒子群算法求解旅行商问题TSP (JAVA实现)

    粒子群算法求解旅行商问题TSP 写在开头: 最近师妹的结课作业问我,关于使用粒子群求解TSP问题的思路.我想了想,自己去年的作业用的是遗传算法,貌似有些关联,索性给看了看代码.重新学习了一遍粒子群算法 ...

  6. java 双调旅行商 hamiltonian,双调欧几里得旅行商问题(TSP)

    最小环+欧拉回路=最短哈密顿图 介绍 TSP(Traveling Salesman Problem)即旅行商问题,是数学领域中著名问题之一.这个问题是这样的:假设有一个旅行商人要拜访n个城市,他必须选 ...

  7. 回溯法解旅行商问题java,回溯法解旅行商问题 | 学步园

    第一次编写,格式写的比较混乱.先把源码上来吧.这个源码是模仿教材上的(本人学生一枚 ).分析过程在下面. #include using namespace std; class Traveling{ ...

  8. java 蚁群算法_Java蚁群算法(Ant Colony)求解旅行商问题(TSP)(二)

    算法准备 旅行商问题(TSP)是一个经典的图论问题.在给定一系列城市和他们之间的距离以后,一个旅行商人希望能够找到一条能够走遍所有城市,并返回起点城市的最短路径.既然路径能串起来所有的城市,那么问题中 ...

  9. 回溯法解旅行商问题java,回溯法-旅行商问题

    一.问题描述 旅行推销员问题(英语:Travelling salesman problem, TSP)是这样一个问题:给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路 ...

最新文章

  1. 《HBase企业应用开发实战》—— 第3章 数 据 模 型
  2. Android移动开发之【Android实战项目】后台服务Service
  3. 23 duplicate symbols for architecture i386
  4. 专访赵加雨:WebRTC在网易云信的落地
  5. 球迷福利!Next VR本周将直播三场ICC比赛
  6. 【福利派送】浪漫七夕,不可辜负!这 8 份礼物请收下!
  7. 2 找图像连通域_MATLAB--数字图像处理 简单人脸识别
  8. Smartfox Server 2x 在 CentOS6.3 上的搭建
  9. 华为路由器防火墙配置命令总结(上)
  10. 2021母婴行业洞察报告.pdf(附下载链接)
  11. “图片”--上传(文件上传一致,单文件)
  12. mingw w64 v8.0.0_R 4.0发布,配套编译工具Rtools 40发布
  13. 学习OpenGL ES之法线贴图
  14. Samba 共享服务
  15. 西门子cpu指示灯含义_CPU中的指示灯亮的意思
  16. 安卓手机有坏点测试软件,手机屏幕坏点怎么检测
  17. K8S 探针 readinessProbe、livenessProbe和startupProbe
  18. 德国:认真是一种可怕的力量
  19. 手机微博保存的图片无法在Win10电脑端查看 - 解决方案
  20. 关于win10重新安装应用商店(Microsoft Store)的解决方案

热门文章

  1. Linux Shell中的变量-环境变量
  2. 30000台苹果电脑遭恶意软件入侵,包括最新的M1系列
  3. 怎么准备全国计算机二级(C语言)考试?
  4. 淘宝链接转换成淘宝客链接--PHP
  5. Endnote使用笔记01:如何在Endnote 20中导入GB/T7714-2015引文格式
  6. STM32 CAN总线故障检测功能的使用
  7. 接口技术七段数码管c语言,031 实例7-七段数码管绘制
  8. C select 函数
  9. IOS图像2之jpg、png图片保存、互转、设置有损压缩因子
  10. 时间序列基础操作:使用python与eviews对AR与ARMA模型进行定阶与预报