C++ 求解TSP旅行商问题

一、
TSP(旅行商问题)简介

 TSP,即旅行商问题,又称TSP问题(Traveling Salesman
Problem),是数学领域中著名问题之一。

假设有一个旅行商人要拜访N个城市,他必须选择所要走的路径,路径的限制是每个城市只能拜访一次,而且最后要回到原来出发的城市。路径的选择目标是要求得的路径路程为所有路径之中的最小值

最近刚好在学习路径规划这一类的问题,简单了解了一下TSP问题,发现csdn上C++版本的并不是很多,主要尝试了两种代码

二、代码实现(C++)

第一种:

#include<iostream>
#define n 7
using namespace std;

int pd = 0;
int s[n] = {-1,-1,-1,-1,-1,-1,-1} ;              // 记录已经访问过的城市, 初始化为-1,表示未访问
int distance[n][n] = {{10000,48,38,48,40,50,34},    // 城市间距离数组,10000表示自己到自己的距离
                      {48,10000,22,26,56,46,50},
                      {38,22,10000,28,46,40,40},
                      {48,26,28,10000,44,34,50},
                      {40,56,46,44,10000,22,26},
                     {50,46,40,34,22,10000,28},
                     {34,50,40,50,26,28,10000}};     //设定邻接矩阵

bool visit(int k)                       //判断城市k是否被访问过
{
    for (int i = 0; i < n ; i++)
        if (s[i] == k) return 1 ;
            return 0;
}

void clostCityDistance(int currentCity) //查找距离当前城市最近的下一个城市
{
    int Dtemp = 10000;                  //Dtemp暂时存储当前最小路径的值
    pd++;
    for (int i = 0; i < n; i++)
    {
        if ( visit(i) == 0 && ::distance[i][s[currentCity]] <Dtemp )
        {
            Dtemp = ::distance[i][s[currentCity]] ;
            s[pd] = i ;      //若该城市没有被访问过,且距离当前城市最短,则将访问该城市,存入s[]数组中
        }
    }
    for(int i = 0 ; i < 7 ; i++)
        cout<<s[i];
    cout<<endl;
    for (int i = 0; i < n; i++)         //查找是否还有未访问的城市
    {
        if ( s[i] == -1 )
            clostCityDistance(s[pd]);
    }
}

void TSP()
{
    int sum = 0 ;// 最短路径之和
    s[0] = 0;//从第2个城市出发 ,初始化出发的城市,可在0,1,2,3中任意一个
    clostCityDistance(s[0]) ;//寻找距离2城市最近的城市
    for (int i=0; i < n ; i++)
    {
        if (i == n -1)
        {
            printf("%d",s[i] );
            printf("->%d 距离为:%d\n",s[0],::distance [ s[n-1] ] [s[0]]);
            printf("总距离是  %d\n",sum += ::distance [ s[n-1] ] [s[0]] );
            break ;
        }
            printf("%d->%d 距离为:%d \n",s[i],s[i+1], ::distance[ s[i] ][ s[i+1] ] ;
            sum += ::distance[ s[i] ][ s[i+1] ] ;
    }
}



int main() {
    TSP() ;
    return 0;
}

 这份代码我简单修改了一下,用pd变量解决了原来的数组溢出问题。

它的优点在于时间复杂度很低,在未被访问的节点中选择距离当前节点最近的节点作为下一个目标节点以此循环下去,这种算法在节点数很多的情况下能节省很多时间,可以很好地满足实时性的要求。缺点也很明显,它搜寻到的并不是全局最优路径,这个缺点在节点数不多的时候体现的不是很明显,但节点数和节点间距上来后会有一定的累积效应,可能会使结果与最优路径的差值很大。

第二种:

#include<iostream>
#include<cmath>
using namespace std;
#define N 9999

int distance[7][7] = {{10000,48,38,48,40,50,34},    // 城市间距离数组,10000表示自己到自己的距离
                      {48,10000,22,26,56,46,50},
                      {38,22,10000,28,46,40,40},
                      {48,26,28,10000,44,34,50},
                      {40,56,46,44,10000,22,26},
                      {50,46,40,34,22,10000,28},
                      {34,50,40,50,26,28,10000}};
//TSP问题求解函数
void TSP(int n,int** graph,int location){
    //构建二维数组[i,2^n],j模拟集合个数
    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++){
        D[i][0]=graph[i][location]; //D[i,{}]
        Rec[i][0]= -1;
        for(int j=1;j<=(int)pow(2,n)-1;j++){
            D[i][j]=N;
            Rec[i][j] = -1;
        }
    }
    //动态规划
    for(int j=1;j<=(int)pow(2,n)-1;j++){    //对每一列求解
        for(int i=1;i<=n;i++){  //每一行找最短路径
            int min = N;
            if(((int)pow(2,i-1) & j) == 0){   //顶点集不能包含i
                int length = N;
                for(int k=1;k<=n;k++){
                    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;//局部最优决策
                        }
                    }
                }
            }
        }
    }
    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;
    n=7;
    //建立二维数组模拟邻接矩阵
    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] = ::distance[i-1][j-1];
//            cout<<"输入邻接矩阵graph["<<i<<"]["<<j<<"]的权:"<<endl;
//            cin>>graph[i][j];
        }
    }
    for(int i = 1 ; i <= 7 ; i++)
    {
        for(int j = 1 ; j <= 7 ; j++)
        {
        cout<<graph[i][j]<<" ";
        }
        cout<<endl;
    }

    cout<<"旅行家现在在第几个城市?"<<endl;
    int location;
    cin>>location;
    TSP(n,graph,location);   //TSP求解
    return 0;
}

这个算法就比较经典,用了一个递推结构,不断将多节点的大距离拆散成小距离求解。

它的优点在于获取的会是全局最优路径,唯一欠佳的点就是他的时间复杂度达到了O(n*2^n),在多节点的情况下很难满足实时性要求,尤其是在运行过程中需要反复更新节点和路径的机器人。

网上也有一些其他的算法,在需要同时兼顾路径和实时的情况下,可以参考一下如模拟退火、粒子群等

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
以下是使用遗传算法解决旅行商问题的C语言代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <math.h> #include <time.h> #define POP_SIZE 100 // 种群大小 #define CROSS_RATE 0.8 // 交叉率 #define MUTATION_RATE 0.01 // 突变率 #define N_GENERATIONS 500 // 迭代次数 #define N_CITIES 20 // 城市数量 // 定义城市结构体 typedef struct { float x, y; } City; City cities[N_CITIES]; // 城市数组 // 生成城市坐标 void init_cities() { srand(time(NULL)); for (int i = 0; i < N_CITIES; i++) { cities[i].x = (float) rand() / RAND_MAX * 100; cities[i].y = (float) rand() / RAND_MAX * 100; } } // 计算两个城市之间的距离 float get_distance(City city1, City city2) { float x1 = city1.x, y1 = city1.y; float x2 = city2.x, y2 = city2.y; return sqrt(pow(x1 - x2, 2) + pow(y1 - y2, 2)); } // 计算路径长度 float get_fitness(int *individual) { float distance = 0; for (int i = 0; i < N_CITIES - 1; i++) { City city1 = cities[individual[i]]; City city2 = cities[individual[i+1]]; distance += get_distance(city1, city2); } City city1 = cities[individual[N_CITIES - 1]]; City city2 = cities[individual[0]]; distance += get_distance(city1, city2); return 1 / distance; } // 随机生成初始种群 void init_pop(int (*pop)[N_CITIES]) { for (int i = 0; i < POP_SIZE; i++) { for (int j = 0; j < N_CITIES; j++) { pop[i][j] = j; } for (int j = 0; j < N_CITIES; j++) { int index = rand() % N_CITIES; int temp = pop[i][j]; pop[i][j] = pop[i][index]; pop[i][index] = temp; } } } // 选择 void selection(int (*pop)[N_CITIES], float *fitness, int (*parents)[2]) { for (int i = 0; i < POP_SIZE; i++) { int index1 = rand() % POP_SIZE; int index2 = rand() % POP_SIZE; if (fitness[index1] > fitness[index2]) { parents[i][0] = index1; } else { parents[i][0] = index2; } index1 = rand() % POP_SIZE; index2 = rand() % POP_SIZE; if (fitness[index1] > fitness[index2]) { parents[i][1] = index1; } else { parents[i][1] = index2; } } } // 交叉 void crossover(int (*pop)[N_CITIES], int (*parents)[2], int (*offspring)[N_CITIES]) { for (int i = 0; i < POP_SIZE; i++) { if ((float) rand() / RAND_MAX < CROSS_RATE) { int index1 = rand() % N_CITIES; int index2 = rand() % N_CITIES; if (index1 > index2) { int temp = index1; index1 = index2; index2 = temp; } for (int j = index1; j <= index2; j++) { offspring[i][j] = pop[parents[i][0]][j]; } int k = 0; for (int j = 0; j < N_CITIES; j++) { if (k == index1) { k = index2 + 1; } if (k < N_CITIES && !contains(offspring[i], k, pop[parents[i][1]][j])) { offspring[i][k] = pop[parents[i][1]][j]; k++; } } } else { for (int j = 0; j < N_CITIES; j++) { offspring[i][j] = pop[parents[i][0]][j]; } } } } // 突变 void mutation(int (*offspring)[N_CITIES]) { for (int i = 0; i < POP_SIZE; i++) { if ((float) rand() / RAND_MAX < MUTATION_RATE) { int index1 = rand() % N_CITIES; int index2 = rand() % N_CITIES; int temp = offspring[i][index1]; offspring[i][index1] = offspring[i][index2]; offspring[i][index2] = temp; } } } // 替换 void replacement(int (*pop)[N_CITIES], float *fitness, int (*offspring)[N_CITIES], float *offspring_fitness) { for (int i = 0; i < POP_SIZE; i++) { if (offspring_fitness[i] > fitness[i]) { for (int j = 0; j < N_CITIES; j++) { pop[i][j] = offspring[i][j]; } fitness[i] = offspring_fitness[i]; } } } // 判断一个值是否在数组中 int contains(int *array, int length, int value) { for (int i = 0; i < length; i++) { if (array[i] == value) { return 1; } } return 0; } // 输出结果 void print_result(int *best_individual, float best_fitness) { printf("最短路径长度:%.2f\n", 1 / best_fitness); printf("最短路径:"); for (int i = 0; i < N_CITIES; i++) { printf("%d ", best_individual[i]); } printf("\n"); } int main() { init_cities(); // 生成城市坐标 int pop[POP_SIZE][N_CITIES]; // 种群数组 init_pop(pop); // 随机生成初始种群 float fitness[POP_SIZE]; // 适应度数组 for (int i = 0; i < POP_SIZE; i++) { fitness[i] = get_fitness(pop[i]); // 计算适应度 } int parents[POP_SIZE][2]; // 父代数组 int offspring[POP_SIZE][N_CITIES]; // 子代数组 float offspring_fitness[POP_SIZE]; // 子代适应度数组 for (int generation = 0; generation < N_GENERATIONS; generation++) { selection(pop, fitness, parents); // 选择 crossover(pop, parents, offspring); // 交叉 mutation(offspring); // 突变 for (int i = 0; i < POP_SIZE; i++) { offspring_fitness[i] = get_fitness(offspring[i]); // 计算子代适应度 } replacement(pop, fitness, offspring, offspring_fitness); // 替换 } int best_individual[N_CITIES]; // 最优个体 float best_fitness = 0; // 最优适应度 for (int i = 0; i < POP_SIZE; i++) { if (fitness[i] > best_fitness) { best_fitness = fitness[i]; for (int j = 0; j < N_CITIES; j++) { best_individual[j] = pop[i][j]; } } } print_result(best_individual, best_fitness); // 输出结果 return 0; } ``` 该代码使用遗传算法求解旅行商问题,使用欧氏距离作为城市之间的距离。通过遗传算法的迭代过程,不断优化种群中的个体,最终得到一条最短路径。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱兜圈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值