一、前言

    【旅行商问题】旅行商问题(TravelingSalesmanProblem,TSP)是一个经典的组合优化问题。经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。从图论的角度来看,该问题实质是在一个带权完全无向图中,找一个权值最小的Hamilton回路。由于该问题的可行解是所有顶点的全排列,随着顶点数的增加,会产生组合爆炸,它是一个NP完全问题。由于其在交通运输、电路板线路设计以及物流配送等领域内有着广泛的应用,国内外学者对其进行了大量的研究。早期的研究者使用精确算法求解该问题,常用的方法包括:分枝定界法、线性规划法、动态规划法等。但是,随着问题规模的增大,精确算法将变得无能为力,因此,在后来的研究中,国内外学者重点使用近似算法或启发式算法,主要有遗传算法模拟退火法蚁群算法禁忌搜索算法、贪婪算法神经网络等。【参考百度百科】。

TSP_旅行商问题 - 蛮力法DFS(一)

旅行商问题:字母代表城市,数字代表城市间的路径或者费用

TSP_旅行商问题 - 蛮力法DFS(一)

蛮力法求解上图的所有路径并记录最佳路径的相关信息

    旅行商求解系列:

-------------------------------------------------------------------------------------------------

(1)TSP_旅行商问题- 蛮力法( 深度遍历优先算法DFS )
(2)TSP_旅行商问题- 动态规划
(3)TSP_旅行商问题- 模拟退火算法
(4)TSP_旅行商问题- 遗传算法
(5)TSP_旅行商问题- 粒子群算法
(6)TSP_旅行商问题- 神经网络

-------------------------------------------------------------------------------------------------

二、本文概要

    本文基于蛮力法(此处采用深度优先遍历,DFS)解决旅行商问题。通过遍历出所有满足条件的路径情况,并保持更新最优解,直到所有情况都遍历完,得到全局最优解。但是,使用蛮力法需要遍历的城市个数高达city_num的阶乘,当city_num=12的时候,需遍历479001600种情况,程序所需时间以小时为单位。

三、蛮力法 - 深度优先遍历,DFS

1. 创建图的邻接矩阵:参考文章“图的存储方式

[cpp] view plain copy
  1. void CreateGraph(Graph &G){  
  2.     ifstream read_in;  
  3.     read_in.open("L:\\Coding\\图的常见操作\\图的常见操作\\city_10.txt");  
  4.     if (!read_in.is_open())  
  5.     {  
  6.         cout<<"文件读取失败."<<endl;  
  7.         return;  
  8.     }  
  9.       
  10.     read_in >> G.vex_num;  
  11.   
  12.     G.arc_num = 0;  
  13.     for (int i = 0;i < G.vex_num; i++)  
  14.     {  
  15.         read_in >> G.vexs[i];  
  16.     }  
  17.     G.vexs[G.vex_num] = '\0';    // char的结束符.  
  18.   
  19.     for (int i = 0; i < G.vex_num;i++)  
  20.     {  
  21.         for (int j = 0; j < G.vex_num; j++)  
  22.         {  
  23.             read_in >> G.arcs[i][j];  
  24.   
  25.             // calculate the arc_num  
  26.             if (G.arcs[i][j] > 0)  
  27.             {  
  28.                 G.arc_num++;  
  29.             }  
  30.         }  
  31.     }  
  32.   
  33.     // display  
  34.     cout<<"无向图创建完毕,相关信息如下:"<<endl;  
  35.     cout<<"【顶点数】 G.vex_num = "<<G.vex_num<<endl;  
  36.     cout<<"【边数】 G.arc_num = "<<G.arc_num<<endl;  
  37.     cout<<"【顶点向量】 vexs[max_vexNum] = ";  
  38.     for (int i = 0; i < G.vex_num; i++)  
  39.     {  
  40.         cout<<G.vexs[i]<<" ";  
  41.     }  
  42.     cout<<endl<<"【邻接矩阵】 arcs[max_vexNum][max_vexNum] 如下:"<<endl;  
  43.     for (int i = 0; i < G.vex_num;i++)  
  44.     {  
  45.         for (int j = 0; j < G.vex_num; j++)  
  46.         {  
  47.             cout << std::right<<setw(10) << G.arcs[i][j]<<" ";  
  48.         }  
  49.         cout<<endl;  
  50.     }  
  51. }  

2. DFS - 递归

[cpp] view plain copy
  1. void DFS(Graph G, char city_start){  
  2.     int v_index = _findCityIndex(G, city_start);    // 起始城市,每次调用(递归)都更新.  
  3.   
  4.     if (path_index == G.vex_num - 1 && G.arcs[v_index][int('A') - 65] > 0)  
  5.     {  
  6.         path_DFS[path_num][path_index] = city_start;  
  7.         path_DFS[path_num][path_index + 1] = 'A';   // A为起始城市  
  8.         lenth_DFS[path_num] = 0;    // 存储最短路径  
  9.   
  10.         // 计算最短路径  
  11.         for (int i = 0; i < G.vex_num; i++)  
  12.         {  
  13.             lenth_DFS[path_num] += G.arcs[(int)path_DFS[path_num][i] - 65][(int)path_DFS[path_num][i+1] - 65];  
  14.         }  
  15.   
  16.         if (bestLength > lenth_DFS[path_num])  
  17.         {  
  18.             // 更新最短路径  
  19.             bestLength = lenth_DFS[path_num];  
  20.         }  
  21.   
  22.         //cout << "第【" << (path_num + 1) << "】条路径!" << endl;  
  23.         DFS_fout << "第【" << (path_num + 1) << "】条路径!" << endl;  
  24.         path_num++;    // 下一条路径  
  25.         // 初始化下一次路径与上一次相同  
  26.         for (int i = 0; i < G.vex_num;i++)  
  27.         {  
  28.             path_DFS[path_num][i] = path_DFS[path_num-1][i];  
  29.         }  
  30.         return;  
  31.     }  
  32.     else  
  33.     {  
  34.         for (int i = 0; i < G.vex_num; i++)  
  35.         {  
  36.             if (G.arcs[v_index][i] > 0 && !is_visited[i])  
  37.             {  
  38.                 path_DFS[path_num][path_index] = city_start;  
  39.                 path_index++;  
  40.                 is_visited[v_index] = true;  
  41.                 DFS(G, (char)(i + 65));  
  42.                 // cout<<"--- 深度遍历回溯 ---"<<endl;  
  43.                 path_index--;  
  44.                 is_visited[v_index] = false;  
  45.             }  
  46.         }  
  47.     }  
  48. }  

三、算法分析(DFS)

1. DFS的时间复杂度:

  1)采用邻接表表示的图深度遍历    -----》 时间复杂度为O(n+e);

  2)采用邻接矩阵表示的图深度遍历 -----》 时间复杂度为O(n^2);

2. 空间复杂度:O(MAX_PATH_LENGTH * max_vexNum)

[cpp] view plain copy
  1. bool is_visited[max_vexNum];                 // 存储当前城市是否已被访问  
  2. char path_DFS[MAX_PATH_LENGTH][max_vexNum];   // 存储所有路径  
  3. double lenth_DFS[MAX_PATH_LENGTH];            // 存储所有路径对应的长度  

四、 测试数据及其结果

1. 程序数据:city_10.txt

[cpp] view plain copy
  1. 10  
  2. A B C D E F G H I J   
  3. 0 2538.94 2873.8 2575.27 2318.1 2158.71 2216.58 3174.04 3371.13 3540.24   
  4. 2538.94 0 1073.54 111.288 266.835 395.032 410.118 637.942 853.554 1055   
  5. 2873.8 1073.54 0 964.495 988.636 1094.32 1382.73 1240.15 1460.25 1687   
  6. 2575.27 111.288 964.495 0 262.053 416.707 503.563 624.725 854.916 1068.42   
  7. 2318.1 266.835 988.636 262.053 0 163.355 395.14 885 1110.86 1318.19   
  8. 2158.71 395.032 1094.32 416.707 163.355 0 338.634 1030.34 1248.58 1447.69   
  9. 2216.58 410.118 1382.73 503.563 395.14 338.634 0 984.068 1160.26 1323.7   
  10. 3174.04 637.942 1240.15 624.725 885 1030.34 984.068 0 243.417 473.768   
  11. 3371.13 853.554 1460.25 854.916 1110.86 1248.58 1160.26 243.417 0 232.112   
  12. 3540.24 1055 1687 1068.42 1318.19 1447.69 1323.7 473.768 232.112 0   

2. 程序运行结果

TSP_旅行商问题 - 蛮力法DFS(一)

五、总结

    使用蛮力法解决TSP问题的优缺点如下:
1. 优点:
  1)在时间允许的条件下,一定能够得到全局最优解。
  2)结构简单,易于实现。
  3)暂用内存较少。
2. 缺点:
  1)速度慢。
  2)组合爆炸问题:当城市个数达到12的时候,需遍历479001600种路径,程序耗时以“小时”为单位。
  3)不适用于大规模数据中,具体问题具体分析。

六、源程序

1. DFS.h:

[cpp] view plain copy
  1. #ifndef _DFS_H_    
  2. #define _DFS_H_    
  3.     /* 1. 图 - 邻接矩阵表示法 */  
  4.     /* ---------------------------------------------------------------- */  
  5.     /* 较完善的数据结构 
  6.     #define VRType int 
  7.     #define InfoType int 
  8.     #define VertexType char 
  9.     #define max_n 20 
  10.     typedef enum{DG, DN, AG, AN} GraphKind; 
  11.  
  12.     // 弧结点与矩阵的类型 
  13.     typedef struct {  
  14.         VRType    adj;          //VRType为弧的类型。图--0,1;网--权值 
  15.         InfoType  *Info;        //与弧相关的信息的指针,可省略 
  16.     }ArcCell, AdjMatrix[max_n][max_n]; 
  17.  
  18.     // 图的类型 
  19.     typedef struct{      
  20.         VertexType vexs[max_n];     // 顶点向量 
  21.         AdjMatrix  arcs;        // 邻接矩阵 
  22.         int        vexnum, arcnum;  // 顶点数,边数 
  23.         GraphKind  kind;        // 图类型 
  24.     }MGraph;     
  25.      */  
  26.     /* ---------------------------------------------------------------- */  
  27.   
  28.     /* 简化的数据结构 */  
  29.     #define max_vexNum 26   // 最大城市个数  
  30.     #define MAX_PATH_LENGTH 9999999  
  31.     typedef struct{  
  32.         int vex_num, arc_num;           // 顶点数 边数  
  33.         char vexs[max_vexNum];          // 顶点向量  
  34.         double arcs[max_vexNum][max_vexNum];    // 邻接矩阵  
  35.     }Graph;  
  36.   
  37.     void CreateGraph(Graph &G);  
  38.     void DFS_Traverse(Graph G);  
  39.     void DFS(Graph G, char city_start); <span style="white-space:pre;"> </span>// 深度优先遍历 - stack  
  40.     void BFS(Graph G);              // 广度优先遍历   - queue  
  41.   
  42.     bool is_visited[max_vexNum];            // 存储当前城市是否已被访问  
  43.     char path_DFS[MAX_PATH_LENGTH][max_vexNum]; // 存储所有路径  
  44.     double lenth_DFS[MAX_PATH_LENGTH];      // 存储所有路径对应的长度  
  45.   
  46.     long int path_num = 0, path_index = 0;  
  47.     long double bestLength = INT_MAX + 0.0; <span style="white-space:pre;"> </span>// 最短路径初始化为无穷大  
  48.   
  49.     // 功能函数  
  50.     void CreateGraph(Graph &G);  
  51.     int _findCityIndex(Graph G, char city_start);  
  52.     void DFS(Graph G, char city_start);  
  53.   
  54. #endif  //_BP_H_   

2. DFS.cpp:

[cpp] view plain copy
  1. #include <iostream>  
  2. #include <stdlib.h>   
  3. #include <queue>  
  4. #include <stack>  
  5. #include <fstream>  
  6. #include <iomanip>    // 本文用于输出对齐  
  7.   
  8. #include "DFS.h"  
  9.   
  10. using namespace std;  
  11.   
  12. ofstream DFS_fout("L:\\Coding\\图的常见操作\\图的常见操作\\city_10_out_2.txt");  
  13.   
  14. int main(){  
  15.     cout<<"程序开始..."<<endl;  
  16.     time_t T_begin = clock();  
  17.   
  18.     Graph G;  
  19.     CreateGraph(G);  
  20.   
  21.     for (int i = 0; i < G.vex_num; i++)  
  22.     {  
  23.         is_visited[i] = false;  
  24.     }  
  25.   
  26.     char city_start = 'A';  
  27.     DFS(G, city_start);  
  28.   
  29.   
  30.     for (int i = 0; i < path_num; i++)  
  31.     {  
  32.         for (int j = 0; j <= G.vex_num; j++)  
  33.         {  
  34.             // cout<<path_DFS[i][j]<<" ";  
  35.             // DFS_fout<<path_DFS[i][j]<<" ";  
  36.         }  
  37.         // cout<<"对应的路程lenth_DFS[] = "<<lenth_DFS[i]<<endl;  
  38.         // DFS_fout<<"对应的路程lenth_DFS[] = "<<lenth_DFS[i]<<endl;  
  39.     }  
  40.   
  41.     // DFS_fout<<"最短路程bestLength = "<<bestLength<<endl;  
  42.     cout<<"最短路程bestLength = "<<bestLength<<endl;  
  43.   
  44.     time_t T_end = clock();  
  45.     double RunningTime = double(T_end - T_begin)/CLOCKS_PER_SEC;  
  46.     // DFS_fout<<"程序运行时间 RunningTime = "<<RunningTime<<endl;  
  47.     cout<<"程序运行时间 RunningTime = "<<RunningTime<<endl;  
  48.     system("pause");  
  49.     return 0;  
  50. }  
  51.   
  52. void CreateGraph(Graph &G){  
  53.     ifstream read_in;  
  54.     read_in.open("L:\\Coding\\图的常见操作\\图的常见操作\\city_10.txt");  
  55.     if (!read_in.is_open())  
  56.     {  
  57.         cout<<"文件读取失败."<<endl;  
  58.         return;  
  59.     }  
  60.       
  61.     read_in >> G.vex_num;  
  62.   
  63.     G.arc_num = 0;  
  64.     for (int i = 0;i < G.vex_num; i++)  
  65.     {  
  66.         read_in >> G.vexs[i];  
  67.     }  
  68.     G.vexs[G.vex_num] = '\0';   // char的结束符.  
  69.   
  70.     for (int i = 0; i < G.vex_num;i++)  
  71.     {  
  72.         for (int j = 0; j < G.vex_num; j++)  
  73.         {  
  74.             read_in >> G.arcs[i][j];  
  75.   
  76.             // calculate the arc_num  
  77.             if (G.arcs[i][j] > 0)  
  78.             {  
  79.                 G.arc_num++;  
  80.             }  
  81.         }  
  82.     }  
  83.   
  84.     // display  
  85.     cout<<"无向图创建完毕,相关信息如下:"<<endl;  
  86.     cout<<"【顶点数】 G.vex_num = "<<G.vex_num<<endl;  
  87.     cout<<"【边数】 G.arc_num = "<<G.arc_num<<endl;  
  88.     cout<<"【顶点向量】 vexs[max_vexNum] = ";  
  89.     for (int i = 0; i < G.vex_num; i++)  
  90.     {  
  91.         cout<<G.vexs[i]<<" ";  
  92.     }  
  93.     cout<<endl<<"【邻接矩阵】 arcs[max_vexNum][max_vexNum] 如下:"<<endl;  
  94.     for (int i = 0; i < G.vex_num;i++)  
  95.     {  
  96.         for (int j = 0; j < G.vex_num; j++)  
  97.         {  
  98.             cout << std::right<<setw(10) << G.arcs[i][j]<<" ";  
  99.         }  
  100.         cout<<endl;  
  101.     }  
  102. }  
  103.   
  104. int _findCityIndex(Graph G, char city_start){  
  105.     for (int i = 0; i < G.vex_num;i++)  
  106.     {  
  107.         if (G.vexs[i] == city_start)  
  108.         {  
  109.             return i;  
  110.         }  
  111.     }  
  112.     cout<<"【error】当前城市未找到!"<<endl;  
  113.     return -1;  
  114. }  
  115.   
  116. void DFS(Graph G, char city_start){  
  117.     int v_index = _findCityIndex(G, city_start);    // 起始城市,每次调用(递归)都更新.  
  118.   
  119.     if (path_index == G.vex_num - 1 && G.arcs[v_index][int('A') - 65] > 0)  
  120.     {  
  121.         path_DFS[path_num][path_index] = city_start;  
  122.         path_DFS[path_num][path_index + 1] = 'A';   // A为起始城市  
  123.         lenth_DFS[path_num] = 0;    // 存储最短路径  
  124.   
  125.         // 计算最短路径  
  126.         for (int i = 0; i < G.vex_num; i++)  
  127.         {  
  128.             lenth_DFS[path_num] += G.arcs[(int)path_DFS[path_num][i] - 65][(int)path_DFS[path_num][i+1] - 65];  
  129.         }  
  130.   
  131.         if (bestLength > lenth_DFS[path_num])  
  132.         {  
  133.             // 更新最短路径  
  134.             bestLength = lenth_DFS[path_num];  
  135.         }  
  136.   
  137.         DFS_fout << "第【" << (path_num + 1) << "】条路径!" << endl;  
  138.         path_num++; // 下一条路径  
  139.         // 初始化下一次路径与上一次相同  
  140.         for (int i = 0; i < G.vex_num;i++)  
  141.         {  
  142.             path_DFS[path_num][i] = path_DFS[path_num-1][i];  
  143.         }  
  144.         return;  
  145.     }  
  146.     else  
  147.     {  
  148.         for (int i = 0; i < G.vex_num; i++)  
  149.         {  
  150.             if (G.arcs[v_index][i] > 0 && !is_visited[i])  
  151.             {  
  152.                 path_DFS[path_num][path_index] = city_start;  
  153.                 path_index++;  
  154.                 is_visited[v_index] = true;  
  155.                 DFS(G, (char)(i + 65));  
  156.                 path_index--;  
  157.                 is_visited[v_index] = false;  
  158.             }  
  159.         }  
  160.     }  
  161.   
  162. }  

七、资源下载

相关文章: