求问TSP问题的多种算法解决

赫爾修羅-韋 2015-06-04 06:47:18
现在需要做一个TSP问题的解法,表示遇到了无奈的情况

问题描述:旅行商问题,已知N个城市的坐标或者距离邻接矩阵(int类型),从一个城市出发固定0城市或者固定1城市出发,经过且仅经过其余N-1个城市一次,回到出发城市。求最短距离,并且需要知道最短距离的途径路径。

测试样例:已知坐标的,最大有52个城市
已知距离邻接矩阵的,最大有58个城市

0、暴力法:城市顺序全排列,找到最短距离(纯属开玩笑,跑两天不知道行不行···
1、回溯法:运行了1个小时才能得到结果而且只是,表示未免也太慢了点吧。
2、分支定界法:空间消耗太大了,运行时间也难以保证。
3、动态规划法:空间复杂度要求太高了,城市稍微比较多的样例程序就会崩···,数量比较少的样例倒是没问题,自己写出来了
4、模拟退火算法,遗传算法,蚁群算法,看了半天的算法介绍,表示自己学不会怎么破···
5、其他算法:禁忌搜索?(其实这个觉得能写,但算法有点不理解····

想问问大神们,有什么好的方法可以过了那17个样例。真心感谢啊······


希望能用C/C++语言实现,不是也没关系。
...全文
4531 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
释沐佛 2018-12-03
  • 打赏
  • 举报
回复
释沐佛 2018-12-03
  • 打赏
  • 举报
回复
百度:TSP问题算法小软件 4.0
或者:http://www.onlinedown.net/soft/578727.htm
冷夜1987 2018-01-15
  • 打赏
  • 举报
回复
最加回复,最后少了一个}结尾
冷夜1987 2018-01-15
  • 打赏
  • 举报
回复
import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.List; public class TabuSearchAlgorithm { /** 迭代次数 */ private int MAX_GEN; /** 每次搜索邻居个数 */ private int neighbourhoodNum; /** 禁忌长度 */ private int tabuTableLength; /** 节点数量,编码长度 */ private int nodeNum; /** 节点间距离矩阵 */ private int[][] nodeDistance; /** 当前路线 */ private int[] route; /** 最好的路径 */ public int[] bestRoute; /** 最佳路径总长度 */ private int bestEvaluation; /** 禁忌表 */ private int[][] tabuTable; /** 禁忌表中的评估值 */ private int[] tabuTableEvaluate; private long tp; public TabuSearchAlgorithm() { } /** * constructor of GA * * @param n * 城市数量 * @param g * 运行代数 * @param c * 每次搜索邻居个数 * @param m * 禁忌长度 * **/ public TabuSearchAlgorithm(int n, int g, int c, int m) { nodeNum = n; MAX_GEN = g; neighbourhoodNum = c; tabuTableLength = m; } /** * 初始化Tabu算法类 * * @param filename * 数据文件名,该文件存储所有城市节点坐标数据 * @throws IOException */ public void init(List<String> list) throws IOException { // 读取数据 int[] x; int[] y; String strbuff; nodeDistance = new int[nodeNum][nodeNum]; x = new int[nodeNum]; y = new int[nodeNum]; String[] strcol; for (int i = 0; i < nodeNum; i++) { // 读取一行数据,数据格式1 6734 1453 strbuff = list.get(i); // 字符分割 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 < nodeNum - 1; i++) { nodeDistance[i][i] = 0; // 对角线为0 for (int j = i + 1; j < nodeNum; 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) { nodeDistance[i][j] = tij + 1; nodeDistance[j][i] = nodeDistance[i][j]; } else { nodeDistance[i][j] = tij; nodeDistance[j][i] = nodeDistance[i][j]; } } } nodeDistance[nodeNum - 1][nodeNum - 1] = 0; route = new int[nodeNum]; bestRoute = new int[nodeNum]; bestEvaluation = Integer.MAX_VALUE; tabuTable = new int[tabuTableLength][nodeNum]; tabuTableEvaluate = new int[tabuTableLength]; for (int i = 0; i < tabuTableEvaluate.length; i++) { tabuTableEvaluate[i] = Integer.MAX_VALUE; } } /** 生成初始群体 */ void generateInitGroup() { System.out.println("1.生成初始群体"); boolean iscontinue = false; for (int i = 0; i < route.length; i++) { do { iscontinue = false; route[i] = (int) (Math.random() * nodeNum); for (int j = i - 1; j >= 0; j--) { if (route[i] == route[j]) { iscontinue = true; break; } } } while (iscontinue); // System.out.println("i="+i+", route[i]="+route[i]); } } /** 复制编码体,复制Gha到Ghb */ public void copyGh(int[] Gha, int[] Ghb) { for (int i = 0; i < nodeNum; i++) { Ghb[i] = Gha[i]; } } /** 计算路线的总距离 */ public int evaluate(int[] chr) { // 0123 int len = 0; // 编码,起始城市,城市1,城市2...城市n for (int i = 1; i < nodeNum; i++) { len += nodeDistance[chr[i - 1]][chr[i]]; } // 城市n,起始城市 len += nodeDistance[chr[nodeNum - 1]][chr[0]]; return len; } /** * 随机获取邻域路径 * * @param route * 当前路径 */ public int[] getNeighbourhood(int[] route) { int temp; int ran1, ran2; int[] tempRoute = new int[route.length]; copyGh(route, tempRoute); ran1 = (int) (Math.random() * nodeNum); do { ran2 = (int) (Math.random() * nodeNum); } while (ran1 == ran2); temp = tempRoute[ran1]; tempRoute[ran1] = tempRoute[ran2]; tempRoute[ran2] = temp; return tempRoute; } /** * 随机获取一定数量的领域路径 */ public int[][] getNeighbourhood(int[] route, int tempNeighbourhoodNum) { int[][] NeighbourhoodRoutes = new int[tempNeighbourhoodNum][nodeNum]; List<int[]> tempExchangeNodeList = new ArrayList<>(); int temp; int ran0, ran1; int[] tempRoute = null; boolean iscontinue; for (int i = 0; i < tempNeighbourhoodNum; i++) { tempRoute = new int[route.length]; copyGh(route, tempRoute); do { iscontinue = false; // 随机生成一个邻域; ran0 = (int) (Math.random() * nodeNum); do { ran1 = (int) (Math.random() * nodeNum); } while (ran0 == ran1); // 判断是否重复 for (int j = 0; j < tempExchangeNodeList.size(); j++) { if (tempExchangeNodeList.get(j)[0] < tempExchangeNodeList.get(j)[1]) { if ((ran0 < ran1 && (tempExchangeNodeList.get(j)[0] == ran0 && tempExchangeNodeList.get(j)[1] == ran1)) || (ran0 > ran1 && (tempExchangeNodeList.get(j)[0] == ran1 && tempExchangeNodeList.get(j)[1] == ran0))) { iscontinue = true; } } else { if ((ran0 < ran1 && (tempExchangeNodeList.get(j)[0] == ran1 && tempExchangeNodeList.get(j)[1] == ran0)) || (ran0 > ran1 && (tempExchangeNodeList.get(j)[0] == ran0 && tempExchangeNodeList.get(j)[1] == ran1))) { iscontinue = true; } } } if (iscontinue == false) { temp = tempRoute[ran0]; tempRoute[ran0] = tempRoute[ran1]; tempRoute[ran1] = temp; // 判断是否与route相同 for (int j = 0; j < tempRoute.length; j++) { if (tempRoute[j] != route[j]) { iscontinue = false; } } if (iscontinue == false && !isInTabuTable(tempRoute)) { NeighbourhoodRoutes[i] = tempRoute; } else { iscontinue = true; } } } while (iscontinue); } return NeighbourhoodRoutes; } /** 判断路径是否在禁忌表中 */ public boolean isInTabuTable(int[] tempRoute) { int i, j; int flag = 0; for (i = 0; i < tabuTableLength; i++) { flag = 0; for (j = 0; j < nodeNum; j++) { if (tempRoute[j] != tabuTable[i][j]) { flag = 1;// 不相同 break; } } if (flag == 0) {// 相同,返回存在相同 break; } } if (i == tabuTableLength) {// 不等 return false;// 不存在 } else { return true;// 存在 } } /** 解禁忌与加入禁忌,注意禁忌策略的选择 */ public void flushTabuTable(int[] tempGh) { int tempValue = evaluate(tempGh); // 找到禁忌表中路径的最大值; int tempMax = tabuTableEvaluate[0]; int maxValueIndex = 0; for (int i = 0; i < tabuTableLength; i++) { if (tabuTableEvaluate[i] > tempMax) { tempMax = tabuTableEvaluate[i]; maxValueIndex = i; } } // 新的路径加入禁忌表 if (tempValue < tabuTableEvaluate[maxValueIndex]) { if (tabuTableEvaluate[maxValueIndex] < Integer.MAX_VALUE) { copyGh(tabuTable[maxValueIndex], route); } System.out.println("测试点:更新禁忌表,maxValueIndex= " + maxValueIndex); for (int k = 0; k < nodeNum; k++) { tabuTable[maxValueIndex][k] = tempGh[k]; } tabuTableEvaluate[maxValueIndex] = tempValue; } } /** 启动禁忌搜索 */ public void startSearch() { int nn; int neighbourhoodEvaluation; int currentBestRouteEvaluation; /** 存放邻域路径 */ int[] neighbourhoodOfRoute = new int[nodeNum]; /** 当代最好路径 */ int[] currentBestRoute = new int[nodeNum]; /** 当前代数 */ int currentIterateNum = 0; /** 最佳出现代数 */ int bestIterateNum = 0; int[][] neighbourhoodOfRoutes = null; // 用于控制迭代次数 int[] priviousRoute = new int[nodeNum]; // 初始化编码Ghh generateInitGroup(); // 将当前路径作为最好路径 copyGh(route, bestRoute); currentBestRouteEvaluation = evaluate(route); bestEvaluation = currentBestRouteEvaluation; System.out.println("2.迭代搜索...."); while (currentIterateNum < MAX_GEN) { for (int i = 0; i < route.length; i++) { priviousRoute[i] = route[i]; } neighbourhoodOfRoutes = getNeighbourhood(route, neighbourhoodNum); System.out.println("测试点:currentIterateNum= " + currentIterateNum); for (nn = 0; nn < neighbourhoodNum; nn++) { // 得到当前路径route的一个邻域路径neighbourhoodOfRoute // neighbourhoodOfRoute=getNeighbourhood(route); neighbourhoodOfRoute = neighbourhoodOfRoutes[nn]; neighbourhoodEvaluation = evaluate(neighbourhoodOfRoute); // System.out.println("测试:neighbourhoodOfRoute="+neighbourhoodEvaluation); if (neighbourhoodEvaluation < currentBestRouteEvaluation) { copyGh(neighbourhoodOfRoute, currentBestRoute); currentBestRouteEvaluation = neighbourhoodEvaluation; // System.out.println("测试:neighbourhoodOfRoute="+neighbourhoodEvaluation); } } if (currentBestRouteEvaluation < bestEvaluation) { bestIterateNum = currentIterateNum; copyGh(currentBestRoute, bestRoute); bestEvaluation = currentBestRouteEvaluation; System.out.println("测试:currentBestRouteEvaluation=" + currentBestRouteEvaluation); } copyGh(currentBestRoute, route); // 解禁忌表,currentBestRoute加入禁忌表 // System.out.println("测试点:currentBestRoute= "+currentBestRoute); flushTabuTable(currentBestRoute); currentIterateNum++; for (int i = 0; i < priviousRoute.length; i++) { if (priviousRoute[i] != route[i]) { currentIterateNum = 0; break; } } printRunStatus(); } // 结果显示: System.out.println("最佳长度出现代数:"); System.out.println(bestIterateNum); System.out.println("最佳长度:"); System.out.println(bestEvaluation); System.out.println("最佳路径:"); for (int i = 0; i < nodeNum; i++) { System.out.print(bestRoute[i] + ","); } } /** * @Description: 输出结运行状态 */ private void printRunStatus() { System.out.println("最优路径长度:" + bestEvaluation); } public static void main(String[] args) throws IOException { System.out.println("Start...."); List<String> listSearch = new ArrayList<String>(); listSearch.add("1,10017,3884"); listSearch.add("2,10016,3883"); listSearch.add("3,10007,3887"); TabuSearchAlgorithm tabu = new TabuSearchAlgorithm(listSearch.size(), 120, 500, 5); tabu.init(listSearch); tabu.startSearch(); } 这个是禁忌算法的代码,参数第一个是编号,第二个是坐标点距离子午0度线的x坐标,第三是坐标的距离赤道的y坐标
qq_29066673 2017-09-20
  • 打赏
  • 举报
回复
楼主大大,可以分享一下禁忌搜索算法求本题的方法吗
flysnowtiger 2016-09-27
  • 打赏
  • 举报
回复
楼主好,能给我把禁忌搜索的代码发我么?跪求,楼主好人【可怜样】
mxway 2015-06-05
  • 打赏
  • 举报
回复
找个蚁群算法的源码,一行行的看,蚁群算法求tsp源码也就200行以内。最复杂也就是双重循环。用心看应该没问题。
赫爾修羅-韋 2015-06-05
  • 打赏
  • 举报
回复
谢谢了,我写好了另一种叫作,禁忌搜索的算法,弄好了。thx
赫爾修羅-韋 2015-06-04
  • 打赏
  • 举报
回复
然而我这种学渣表示学不会这么高级的算法TAT
mxway 2015-06-04
  • 打赏
  • 举报
回复
模拟退火算法,遗传算法,蚁群算法这几个算法求出来的不是最优解。而是相对较优解。不论最大城市是52还是58,0,1,2,3想要求出最优解都不特现实。对于城市数较大的tsp,基本上就是使用模拟退火算法,遗传算法,蚁群算法求出相对较优解。
<项目介绍> 基于C++实现的改进版遗传算法解决TSP问题源码+项目说明.zip 该资源内项目源码是个人的毕设,代码都测试ok,都是运行成功后才上传资源,答辩评审平均分达到94.5分,放心下载使用! 该资源适合计算机相关专业(如人工智能、通信工程、自动化、软件工程等)的在校学生、老师或者企业员工下载,适合小白学习或者实际项目借鉴参考! 当然也可作为毕业设计、课程设计、课程作业、项目初期立项演示等。如果基础还行,可以在此代码基础之上做改动以实现更多功能。 > 旅行商问题(Traveling Salesman Problem,TSP)是一个经典的组合优化问题。经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。 在本文中使用的数据为来自TSPLIB的`ATT48`,即美国本土48个州首府坐标,TSPLIB的已知最优解为10628。 从图论的角度来看,该问题实质是在一个带权完全无向图中,找一个权值最小的Hamilton回路。由于该问题的可行解是所有顶点的全排列,随着顶点数的增加,会产生组合爆炸,它是一个NP完全问题。 # 解决思路 由于NPC问题至今没有得到解决TSP问题往往是通过启发式搜索(我觉得也可以叫暴力)算法来“猜”出最优解。 使用遗传算法解决TSP问题,主要思路如下: - 使用一个不重复的,首尾相同的字符串来表示一个解,该字符串即TSP的顺序 - 使用交叉,变异两种方式产生新的解,并根据数据来计算解的适应度 - 种群定义为当前所有解的一个集合,当种群中的每个个体完成一次“进化”(由概率决定)之后,称之为进化一代 - 对每一代种群进行筛选(根据适应度进行排序),择优劣汰,具有更优秀适应度的个体有更高的概率被其他个体选中进行交叉 - 不断重复上述过程,直到出现可以完全适应的个体(最优解) 遗传算法TSP问题上可以融合多种算法,从而达到不同的效果,比如交叉应该如何交叉,变异应该如何变异等等。同时遗传算法的参数难以调整到最优——包括交叉率,变异率,种群容量等可以对搜索过程产生较大影响的参数都难以调整。限于个人水平,我无法从数学上给出最优参数,只能以经验论,加以多次实验选取表现优秀的样本。 # 解决方案 - 语言:C++ - 个体(解)的表示:用`vector`储存,代表节点遍历顺序 - 距离的计算:取伪欧式(pseudo Euclidean)距离,计算方法如下(向上取整): ![](assets/2.png) 使用`unordered_map`,内部实现为散列表,使得计算个体适应度可以达到*O*(N)​级别的复杂度 - 随机数的生成:设置时间种子,并由此生成随机数 - 交叉对象的选择:轮盘赌 - 交叉算法的选择:顺序移位 - 变异算法的选择:顺序移位 / 贪婪倒位 - 参数的选择:由多次实验得出 # Feature - 种群自动扩增 - 贪心初始化 - 自动调整变异算子 - 自动调整变异率 实验记录 - 实验样本:ATT48@TSPLIB(`att48.tsp`) - 理论最优解:10628 - 实验平台:CLion 2019.3(G++ 8.2.1 x64) ## 1000代 | 序号 | 最优解 | 耗时 / s | | ---- | ------- | -------- | | 0 | 12530 | 0.871 | | 1 | 11965 | 1.061 | | 2 | 12571 | 0.904 | | 3 | 11974 | 0.84 | | 4 | 12295 | 1.108 | | 5 | 12528 | 0.793 | | 6 | 12601 | 1.285 | | 7 | 12505 | 0.82 | | 8 | 11605 | 1.096 | | 9 | 12410 | 0.901 | | AVG | 12298.4 | 0.9679 | ## 5000代 | 序号 | 最优解 | 耗时 / s | | ---- | ------- | -------- | | 0 | 10906 | 3.349 | | 1 | 11122 | 3.899 | | 2 | 11265 | 4.49 | | 3 | 11591 | 5.184 | | 4 | 11407 | 6.562 |

64,654

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧