TSP_旅行商问题 - 蛮力法DFS(一)
2017-01-16 21:04
344 查看
一、前言
【旅行商问题】旅行商问题(TravelingSalesmanProblem,TSP)是一个经典的组合优化问题。经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。从图论的角度来看,该问题实质是在一个带权完全无向图中,找一个权值最小的Hamilton回路。由于该问题的可行解是所有顶点的全排列,随着顶点数的增加,会产生组合爆炸,它是一个NP完全问题。由于其在交通运输、电路板线路设计以及物流配送等领域内有着广泛的应用,国内外学者对其进行了大量的研究。早期的研究者使用精确算法求解该问题,常用的方法包括:分枝定界法、线性规划法、动态规划法等。但是,随着问题规模的增大,精确算法将变得无能为力,因此,在后来的研究中,国内外学者重点使用近似算法或启发式算法,主要有遗传算法、模拟退火法、蚁群算法、禁忌搜索算法、贪婪算法和神经网络等。【参考百度百科】。旅行商问题:字母代表城市,数字代表城市间的路径或者费用
蛮力法求解上图的所有路径并记录最佳路径的相关信息
旅行商求解系列:
-------------------------------------------------------------------------------------------------
(1)TSP_旅行商问题- 蛮力法( 深度遍历优先算法DFS )
(2)TSP_旅行商问题- 动态规划
(3)TSP_旅行商问题- 模拟退火算法
(4)TSP_旅行商问题- 遗传算法
(5)TSP_旅行商问题- 粒子群算法
(6)TSP_旅行商问题- 神经网络
-------------------------------------------------------------------------------------------------
二、本文概要
本文基于蛮力法(此处采用深度优先遍历,DFS)解决旅行商问题。通过遍历出所有满足条件的路径情况,并保持更新最优解,直到所有情况都遍历完,得到全局最优解。但是,使用蛮力法需要遍历的城市个数高达city_num的阶乘,当city_num=12的时候,需遍历479001600种情况,程序所需时间以小时为单位。三、蛮力法 - 深度优先遍历,DFS
1. 创建图的邻接矩阵:参考文章“图的存储方式”
void CreateGraph(Graph &G){ ifstream read_in; read_in.open("L:\\Coding\\图的常见操作\\图的常见操作\\city_10.txt"); if (!read_in.is_open()) { cout<<"文件读取失败."<<endl; return; } read_in >> G.vex_num; G.arc_num = 0; for (int i = 0;i < G.vex_num; i++) { read_in >> G.vexs[i]; } G.vexs[G.vex_num] = '\0'; // char的结束符. for (int i = 0; i < G.vex_num;i++) { for (int j = 0; j < G.vex_num; j++) { read_in >> G.arcs[i][j]; // calculate the arc_num if (G.arcs[i][j] > 0) { G.arc_num++; } } } // display cout<<"无向图创建完毕,相关信息如下:"<<endl; ef9f cout<<"【顶点数】 G.vex_num = "<<G.vex_num<<endl; cout<<"【边数】 G.arc_num = "<<G.arc_num<<endl; cout<<"【顶点向量】 vexs[max_vexNum] = "; for (int i = 0; i < G.vex_num; i++) { cout<<G.vexs[i]<<" "; } cout<<endl<<"【邻接矩阵】 arcs[max_vexNum][max_vexNum] 如下:"<<endl; for (int i = 0; i < G.vex_num;i++) { for (int j = 0; j < G.vex_num; j++) { cout << std::right<<setw(10) << G.arcs[i][j]<<" "; } cout<<endl; } }
2. DFS - 递归
void DFS(Graph G, char city_start){ int v_index = _findCityIndex(G, city_start); // 起始城市,每次调用(递归)都更新. if (path_index == G.vex_num - 1 && G.arcs[v_index][int('A') - 65] > 0) { path_DFS[path_num][path_index] = city_start; path_DFS[path_num][path_index + 1] = 'A'; // A为起始城市 lenth_DFS[path_num] = 0; // 存储最短路径 // 计算最短路径 for (int i = 0; i < G.vex_num; i++) { lenth_DFS[path_num] += G.arcs[(int)path_DFS[path_num][i] - 65][(int)path_DFS[path_num][i+1] - 65]; } if (bestLength > lenth_DFS[path_num]) { // 更新最短路径 bestLength = lenth_DFS[path_num]; } //cout << "第【" << (path_num + 1) << "】条路径!" << endl; DFS_fout << "第【" << (path_num + 1) << "】条路径!" << endl; path_num++; // 下一条路径 // 初始化下一次路径与上一次相同 for (int i = 0; i < G.vex_num;i++) { path_DFS[path_num][i] = path_DFS[path_num-1][i]; } return; } else { for (int i = 0; i < G.vex_num; i++) { if (G.arcs[v_index][i] > 0 && !is_visited[i]) { path_DFS[path_num][path_index] = city_start; path_index++; is_visited[v_index] = true; DFS(G, (char)(i + 65)); // cout<<"--- 深度遍历回溯 ---"<<endl; path_index--; is_visited[v_index] = false; } } } }
三、算法分析(DFS)
1. DFS的时间复杂度:
1)采用邻接表表示的图深度遍历 -----》 时间复杂度为O(n+e);2)采用邻接矩阵表示的图深度遍历 -----》 时间复杂度为O(n^2);
2. 空间复杂度:O(MAX_PATH_LENGTH * max_vexNum)
bool is_visited[max_vexNum]; // 存储当前城市是否已被访问 char path_DFS[MAX_PATH_LENGTH][max_vexNum]; // 存储所有路径 double lenth_DFS[MAX_PATH_LENGTH]; // 存储所有路径对应的长度
四、 测试数据及其结果
1. 程序数据:city_10.txt
10 A B C D E F G H I J 0 2538.94 2873.8 2575.27 2318.1 2158.71 2216.58 3174.04 3371.13 3540.24 2538.94 0 1073.54 111.288 266.835 395.032 410.118 637.942 853.554 1055 2873.8 1073.54 0 964.495 988.636 1094.32 1382.73 1240.15 1460.25 1687 2575.27 111.288 964.495 0 262.053 416.707 503.563 624.725 854.916 1068.42 2318.1 266.835 988.636 262.053 0 163.355 395.14 885 1110.86 1318.19 2158.71 395.032 1094.32 416.707 163.355 0 338.634 1030.34 1248.58 1447.69 2216.58 410.118 1382.73 503.563 395.14 338.634 0 984.068 1160.26 1323.7 3174.04 637.942 1240.15 624.725 885 1030.34 984.068 0 243.417 473.768 3371.13 853.554 1460.25 854.916 1110.86 1248.58 1160.26 243.417 0 232.112 3540.24 1055 1687 1068.42 1318.19 1447.69 1323.7 473.768 232.112 0
2. 程序运行结果
五、总结
使用蛮力法解决TSP问题的优缺点如下:1. 优点:
1)在时间允许的条件下,一定能够得到全局最优解。
2)结构简单,易于实现。
3)暂用内存较少。
2. 缺点:
1)速度慢。
2)组合爆炸问题:当城市个数达到12的时候,需遍历479001600种路径,程序耗时以“小时”为单位。
3)不适用于大规模数据中,具体问题具体分析。
六、源程序
1. DFS.h:
#ifndef _DFS_H_ #define _DFS_H_ /* 1. 图 - 邻接矩阵表示法 */ /* ---------------------------------------------------------------- */ /* 较完善的数据结构 #define VRType int #define InfoType int #define VertexType char #define max_n 20 typedef enum{DG, DN, AG, AN} GraphKind; // 弧结点与矩阵的类型 typedef struct { VRType adj; //VRType为弧的类型。图--0,1;网--权值 InfoType *Info; //与弧相关的信息的指针,可省略 }ArcCell, AdjMatrix[max_n][max_n]; // 图的类型 typedef struct{ VertexType vexs[max_n]; // 顶点向量 AdjMatrix arcs; // 邻接矩阵 int vexnum, arcnum; // 顶点数,边数 GraphKind kind; // 图类型 }MGraph; */ /* ---------------------------------------------------------------- */ /* 简化的数据结构 */ #define max_vexNum 26 // 最大城市个数 #define MAX_PATH_LENGTH 9999999 typedef struct{ int vex_num, arc_num; // 顶点数 边数 char vexs[max_vexNum]; // 顶点向量 double arcs[max_vexNum][max_vexNum]; // 邻接矩阵 }Graph; void CreateGraph(Graph &G); void DFS_Traverse(Graph G); void DFS(Graph G, char city_start); // 深度优先遍历 - stack void BFS(Graph G); // 广度优先遍历 - queue bool is_visited[max_vexNum]; // 存储当前城市是否已被访问 char path_DFS[MAX_PATH_LENGTH][max_vexNum]; // 存储所有路径 double lenth_DFS[MAX_PATH_LENGTH]; // 存储所有路径对应的长度 long int path_num = 0, path_index = 0; long double bestLength = INT_MAX + 0.0; // 最短路径初始化为无穷大 // 功能函数 void CreateGraph(Graph &G); int _findCityIndex(Graph G, char city_start); void DFS(Graph G, char city_start); #endif //_BP_H_
2. DFS.cpp:
#include <iostream> #include <stdlib.h> #include <queue> #include <stack> #include <fstream> #include <iomanip> // 本文用于输出对齐 #include "DFS.h" using namespace std; ofstream DFS_fout("L:\\Coding\\图的常见操作\\图的常见操作\\city_10_out_2.txt"); int main(){ cout<<"程序开始..."<<endl; time_t T_begin = clock(); Graph G; CreateGraph(G); for (int i = 0; i < G.vex_num; i++) { is_visited[i] = false; } char city_start = 'A'; DFS(G, city_start); for (int i = 0; i < path_num; i++) { for (int j = 0; j <= G.vex_num; j++) { // cout<<path_DFS[i][j]<<" "; // DFS_fout<<path_DFS[i][j]<<" "; } // cout<<"对应的路程lenth_DFS[] = "<<lenth_DFS[i]<<endl; // DFS_fout<<"对应的路程lenth_DFS[] = "<<lenth_DFS[i]<<endl; } // DFS_fout<<"最短路程bestLength = "<<bestLength<<endl; cout<<"最短路程bestLength = "<<bestLength<<endl; time_t T_end = clock(); double RunningTime = double(T_end - T_begin)/CLOCKS_PER_SEC; // DFS_fout<<"程序运行时间 RunningTime = "<<RunningTime<<endl; cout<<"程序运行时间 RunningTime = "<<RunningTime<<endl; system("pause"); return 0; } void CreateGraph(Graph &G){ ifstream read_in; read_in.open("L:\\Coding\\图的常见操作\\图的常见操作\\city_10.txt"); if (!read_in.is_open()) { cout<<"文件读取失败."<<endl; return; } read_in >> G.vex_num; G.arc_num = 0; for (int i = 0;i < G.vex_num; i++) { read_in >> G.vexs[i]; } G.vexs[G.vex_num] = '\0'; // char的结束符. for (int i = 0; i < G.vex_num;i++) { for (int j = 0; j < G.vex_num; j++) { read_in >> G.arcs[i][j]; // calculate the arc_num if (G.arcs[i][j] > 0) { G.arc_num++; } } } // display cout<<"无向图创建完毕,相关信息如下:"<<endl; cout<<"【顶点数】 G.vex_num = "<<G.vex_num<<endl; cout<<"【边数】 G.arc_num = "<<G.arc_num<<endl; cout<<"【顶点向量】 vexs[max_vexNum] = "; for (int i = 0; i < G.vex_num; i++) { cout<<G.vexs[i]<<" "; } cout<<endl<<"【邻接矩阵】 arcs[max_vexNum][max_vexNum] 如下:"<<endl; for (int i = 0; i < G.vex_num;i++) { for (int j = 0; j < G.vex_num; j++) { cout << std::right<<setw(10) << G.arcs[i][j]<<" "; } cout<<endl; } } int _findCityIndex(Graph G, char city_start){ for (int i = 0; i < G.vex_num;i++) { if (G.vexs[i] == city_start) { return i; } } cout<<"【error】当前城市未找到!"<<endl; return -1; } void DFS(Graph G, char city_start){ int v_index = _findCityIndex(G, city_start); // 起始城市,每次调用(递归)都更新. if (path_index == G.vex_num - 1 && G.arcs[v_index][int('A') - 65] > 0) { path_DFS[path_num][path_index] = city_start; path_DFS[path_num][path_index + 1] = 'A'; // A为起始城市 lenth_DFS[path_num] = 0; // 存储最短路径 // 计算最短路径 for (int i = 0; i < G.vex_num; i++) { lenth_DFS[path_num] += G.arcs[(int)path_DFS[path_num][i] - 65][(int)path_DFS[path_num][i+1] - 65]; } if (bestLength > lenth_DFS[path_num]) { // 更新最短路径 bestLength = lenth_DFS[path_num]; } DFS_fout << "第【" << (path_num + 1) << "】条路径!" << endl; path_num++; // 下一条路径 // 初始化下一次路径与上一次相同 for (int i = 0; i < G.vex_num;i++) { path_DFS[path_num][i] = path_DFS[path_num-1][i]; } return; } else { for (int i = 0; i < G.vex_num; i++) { if (G.arcs[v_index][i] > 0 && !is_visited[i]) { path_DFS[path_num][path_index] = city_start; path_index++; is_visited[v_index] = true; DFS(G, (char)(i + 65)); path_index--; is_visited[v_index] = false; } } } }
七、资源下载
本文相关代码以及数据:http://download.csdn.net/detail/houchaoqun_xmu/9740077相关文章推荐
- TSP_旅行商问题 - 蛮力法DFS(一)
- TSP_旅行商问题 - 蛮力法DFS(一)
- TSP_旅行商问题 - 蛮力法DFS(一)
- TSP_旅行商问题 - 蛮力法DFS(一)
- TSP_旅行商问题 - 蛮力法DFS(一)
- TSP_旅行商问题 - 蛮力法DFS(一)
- TSP_旅行商问题 - 蛮力法DFS(一)
- TSP_旅行商问题 - 蛮力法DFS(一)
- TSP_旅行商问题 - 蛮力法DFS(一)
- TSP_旅行商问题 - 蛮力法DFS(一)
- 模拟退火算法 解决旅行商(TSP)问题
- POJ 3311状压dp+floyd--TSP问题(货郎担问题||旅行商问题)
- 三进制状态压缩DP(旅行商问题TSP)HDU3001
- 三进制状态压缩DP(旅行商问题TSP)HDU3001
- 用遗传算法解决tsp旅行商问题
- 模拟退火算法解决旅行商问题_SA_TSP
- 利用Matlab以蚁群算法(Ant Colony Algorithm)求解不闭合的旅行商问题(Travelling Salesman Problem, TSP)并进行可视化
- HDU 2224 The shortest path_TSP旅行商问题_DP
- hdu 2224 双调欧几里得旅行商问题tsp
- 动态规划求解TSP(旅行商)问题