[TSP Problem] Algorithm Design and Analysis Experiment 3

 Experiment Report of School of Computer Science and Engineering

Course Title

Design and Analysis of Algorithms

class

Experimental content

Experiment 3 : TSP problem

mentor

Name

Epee DS

student ID

Experiment date

2022.06.06

1. Problem description, including input and output data content and format

Foreword:

The traveling salesman problem (TSP) is a problem: given a list of cities and the distance between each pair of cities, find the shortest circuit that visits each city once and returns to the starting city. It is an NP- hard problem in combinatorial optimization and is of great importance in operations research and theoretical computer science.

Many methods were originally developed from TSP. It was later extended to other NP-complete problems. [1]

Due to its wide application in the fields of transportation, circuit board circuit design and logistics distribution, scholars at home and abroad have done a lot of research on it. Early researchers used precise algorithms to solve this problem, commonly used methods include: branch and bound method, linear programming method, dynamic programming method and so on. However, as the scale of the problem increases, the exact algorithm will become powerless. Therefore, in subsequent studies, domestic and foreign scholars focus on using approximate algorithms or heuristic algorithms, mainly including genetic algorithm, simulated annealing method, ant colony algorithm, Tabu search algorithm, greedy algorithm and neural network, etc. [2]

From the perspective of graph theory, the essence of this problem is to find a Hamilton circuit with the smallest weight in a fully undirected graph with weights. Since the feasible solution of this problem is the full arrangement of all vertices, as the number of vertices increases, combinatorial explosions will occur, and it is an NP-complete problem .

Problem Description:

Given an undirected graph, try to find a path with the smallest weight that can start from the specified point and pass through all nodes at one time and return to the starting point. If there is such a path, output the path and the total weight. If there is no such path path output "No such path exists".

    Input data format:

    The first line enters the total number of nodes n, and the vertex numbers are 1, 2, ..., n.

The second line enters the total number of edges m.

Next, according to the input format: "vertex 1 vertex 2 edge weight", enter the information of one edge per line.

The last line enters the node number of the starting point.

【Input example】

5

9

1 2 10

1 5 12

1 4 4

2 5 5

5 4 6

2 4 8

5 3 30

2 3 15

4 3 7

1

Output data format:

    When the program finishes running, the calculated results are output. If there is a path, then output specific path information and path weight, and if there is no path, then output "there is no such path".

【Example of output】

Minimum length: 43

The shortest path is: 1->4->3->2->5->1

2. Introduction to debugging environment

IDE:Dev-C++ 5.4.0

Programming language: C++

3. Debugging data and results (including specified debugging data and results), with screenshots of the debugging interface

Input data for debugging:

3

3

1 2 1

3 1 3

2 3 2

1

After the graph visualization of the debugging input data is shown in Figure 1 below:

figure 1

Screenshot of the input effect interface on the machine:

operation result:

Screenshot of the final running interface:

Screenshot of debug debugging in the middle of running process:

 DP table printing, rows from 1 to 3, columns from 0111 (table collection)

As can be seen from the above figure, D[1][0] is 0, because the cost from starting from 1 to location(1) is 0 (equivalent to self-to-self)

D[2][0] is the cost of returning to location starting from 2 without passing through any city, so D[2][0] = graph[3][location]

It can be seen from the above table that D[2][100(2) is equivalent to {3}]=5,

State transition: D[1][ 110(2) is equivalent to {2,3}] = min{ D[2][{3}] + graph[1][2] = 5 + 1 , D[3][ {2}] + graph[1][3] = 3 + 3 } = 6

  1. Algorithm flowchart, algorithm time complexity and space complexity

The algorithm flow chart is shown in the figure below:

The time complexity of the algorithm is as follows:

In this code, the main time is spent on the triple loop as shown in the figure below.

Each for loop is equivalent to a multiplication. The first traversal of all set states has about 2n, the second loop enumerates the starting node i has about n, and the innermost loop enumerates the node k in the vertex set j about If there is n, the total time complexity is about O(n2*2n), which is an exponential time complexity. As the number of nodes n increases, the time spent will explode exponentially. The relationship between problem size and time spent is roughly shown in the following figure:

The algorithm space complexity analysis is as follows:

    The space complexity of the algorithm mainly comes from the D two-dimensional array used to store the state and the Rec two-dimensional array used to store the result path . The adjacency matrix graph (two-dimensional array, about n rows and n columns) used to save the graph will appear very small in front of them, so it can be approximately ignored when the problem scale n is large enough. The number of columns of D and Rec is the number of simulation sets, about 2n, and the number of rows is about the number of nodes n given by the input, so the space complexity reflected by the storage space they need to use is O(n2*2n). The relationship between the size of the problem data and the size of the storage space used is roughly as shown in the figure below:

5. Problems encountered in the experiment and solutions (10-50 words)

During the experiment, it seems that there is a problem caused by the different initial values ​​​​used when initializing the array, and I am a little confused when debugging. Later, it will be uniformly initialized to inf , and it will be clearer when debugging.

6. References (2-5)

[1] Guo Jingyang. Overview of the Traveling Salesman Problem [J]. Popular Science and Technology, 2006(8):229-230.

[2] Chen Wenlan, Dai Shugui. A Review of Algorithms for the Traveling Salesman Problem [J]. Journal of Chuzhou University, 2006, 8(3):1-6.

Attachment: detailed code

#include<iostream>

#include<cmath>

using namespace std;

const int inf = 0x3f3f3f3f;

//TSP问题求解函数

void TSP(int n, int** graph, int location){

    //构建二维数组[i,2^n],j模拟集合个数

    //D[i][j]=x表示从 i出发,经过j集合(二进制下110表示经过2、3结点)中所有结点一次,然后回到 location 的最小花费x

    //那么j集合必然不包含起始点location,因为最后也要返回到location

    //j集合也不包含i,因为就是从i结点出发的

    int **D = new int*[n+1];  //建行0~n,为方便理解和表示,第0行未使用

    int **Rec = new int*[n+1];  //建立Rec 存放决策,记录前驱节点

    for(int i = 1; i <= n; i++) {

    //建列表示集合

        D[i] = new int [(int)pow(2,n)];

        Rec[i] = new int [(int)pow(2,n)];

    }

    //初始化D、Rec

    for(int i = 1; i <= n; i++){

      //dp[3][{}]就是从3出发,不经过任何城市,回到location的花费,所以dp[3][{}] = graph[3][location]

        D[i][0] = graph[i][location]; //D[i,{}]

        cout << D[i][0] << endl;

        Rec[i][0] = -1;

        for(int j = 1; j <= (int)pow(2, n)-1; j++){

            D[i][j] = inf;

            Rec[i][j] = -1;

        }

    }

    cout << endl;

    //动态规划

    for(int j = 1; j <= (int)pow(2, n)-1; j++){    //对每一列求解

        for(int i = 1; i <= n; i++){  //每一行找最短路径

            int min = inf;

            //顶点集j不能包含i(编号为2^(i-1))

            if(((int)pow(2, i-1) & j) == 0){  

                int length = inf;

                for(int k = 1; k <= n; k++) {

                  //若顶点k(编号为2^(k-1))在顶点集j中

                  if((int)pow(2, k-1) & j ) {

                        length = graph[i][k] + D[k][j-(int)pow(2,k-1)];

                        if(length < min) {

                            min = length;

                            D[i][j] = min;

                            Rec[i][j] = k; //局部最优决策

                        }

                    }

                }

            }

        }

    }

   

    if (D[location][(int)pow(2,n)-1 - (int)pow(2,location-1)] >= inf) {

      cout << "不存在这样一条路径\n";

      return;

    }

   

    //调试 打印dp表用

    cout << "DP表:" << endl;

    for (int i = 1; i <= n; i++) {

      for (int j = 0; j <= pow(2,n)-1; j++) {

          cout << D[i][j] << " ";

      }

      cout << endl;

    }

   

    cout<<"最短长度:"<<D[location][(int)pow(2,n)-1-(int)pow(2, location-1)]<<endl;//最短路径长度

  cout<<"最短路径为:"<<location;

    int row = location;

    int column = (int)pow(2,n)-1-(int)pow(2,row-1);

    while(column > 0) {

        cout << "->" << Rec[row][column];

        row = Rec[row][column];

        column -= (int)pow(2,row-1);

    }

  cout << "->" << location << endl;

}



int main(){

    cout << "旅行家需要游历的城市数目?:" << endl;

    int n;

    cin >> n;

    //建立二维数组模拟邻接矩阵

    int **graph = new int* [n+1];

    for(int i = 1; i <= n; i++)

        graph[i] = new int[n+1];

   

    //初始化边权值

    for (int i = 1; i <= n; i++)

      for (int j = 1; j <= n; j++)

          graph[i][j] = inf;

   

    //自己到自己的边权为0

    for (int i = 1; i <= n; i++) graph[i][i] = 0; 

   

    //邻接矩阵输入方法

    //for(int i=1;i<=n;i++){

    //    for(int j=1;j<=n;j++){

    //        cout<<"输入邻接矩阵graph["<<i<<"]["<<j<<"]的权:"<<endl;

    //       cin>>graph[i][j];

    //        graph[t2][t1] = graph[i][j];

    //    }

    //}

   

    cout << "请输入总边数:" << endl;

    int m; //m为边数

    cin >> m;

   

    cout<<"输入格式: 顶点1 顶点2 边权:"<<endl;

    //输入格式: 顶点1 顶点2 边权

    for (int i = 0; i < m; i++) {

      int t1, t2, cost;

      cin>>t1>>t2>>cost;

      graph[t1][t2] = graph[t2][t1] = cost;

    }

   

    cout << "旅行家出发的城市编号?" << endl;

    int location;

    cin >> location;

    TSP(n, graph, location);   //TSP求解

    return 0;

}



/*

3

3

1 2 1

3 1 3

2 3 2

1

*/

Guess you like

Origin blog.csdn.net/qq_45732909/article/details/125761673