TSP旅行商问题各种算法实现

本文深入探讨了遗传算法、模拟退火、蚁群算法等优化算法在旅行商问题(TSP)中的应用,通过C++实现并详细解释了每种算法的工作原理和性能表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 C++版本

遗传算法、模拟退火、蚁群算法、Hopfield神经网络、禁忌搜索,部分思路参考网络或者Paper。

//遗传算法解决TSP问题,35s
# include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int times = 3000;//遗传代数
const int chrom = 34; //染色体长度
const int num = 380; //染色体数量
int pc = 80, pm = 6;
int tmp[num+3];
double mp[chrom+3][chrom+3]={0}; //图
int vis[chrom+3], cur;
double fitness[num+3], INF = 1e13;
vector<int>g[num+3], neb[chrom+3], nb[chrom+3];
vector<int>debug[times+3];
double debug2[times+3];
struct node{
    double x, y;
}point[chrom+3];
double dis(int u, int v){
    double x = point[u].x-point[v].x;
    double y = point[u].y-point[v].y;
    return sqrt(x*x+y*y);
}
void init_map(){//初始化边的情况
    for(int i=0; i<chrom; ++i){
        scanf("%lf%lf",&point[i].x,&point[i].y);
    }
    for(int i=0; i<chrom; ++i){
        for(int j=0; j<chrom; ++j){
            mp[i][j] = dis(i,j);
        }
    }
}
void encoding(){//对染色体编码
    vector<int>id;
    for(int i=0; i<chrom; ++i) id.push_back(i);
    for(int i=0; i<num; ++i){
        random_shuffle(id.begin(), id.end());
        g[i] = id;
    }

}
double cal_distance(vector<int>x){//计算染色体的距离
    double res = mp[x[0]][x[chrom-1]];
    for(int i=0; i<chrom-1; ++i)
        res += mp[x[i]][x[i+1]];
    return res;
}
void get_fitness(){//计算染色体的适应度
    for(int i=0; i<num; ++i){
        fitness[i] = INF/cal_distance(g[i]);
    }
}
bool cmp(int x, int y){
    return fitness[x] > fitness[y];
}
void selection(){//选自算子
    for(int i=0; i<num; ++i) tmp[i] = i;
    sort(tmp, tmp+num, cmp);//按适应度排序
    int L = (int)(num*0.3);
    for(int i=L*9/10; i; --i){
        int x = rand()%L+L;
        int y = rand()%L;
        g[tmp[x]] = g[tmp[y]];
    }
}
vector<int> core(int flag, vector<int>neb[]){
    memset(vis, 0, sizeof(vis));
    for(int i=0; i<chrom; ++i) nb[i] = neb[i];
    vector<int>ans;
    vector<pair<double,int> >best;
    ans.push_back(g[flag][0]);
    for(int i=0; i<chrom-1; ++i){
        int x = ans[i];
        best.clear();
        vis[x] = 1;
        for(int j=0; j<nb[x].size(); ++j){
            int y = nb[x][j];
            best.push_back({mp[x][y], y});
            auto it = find(nb[y].begin(), nb[y].end(), x);
            if(it != nb[y].end()){
                nb[y].erase(it);
            }
        }
        if(!best.empty()){
            sort(best.begin(), best.end());
            ans.push_back(best[0].second);
        }
        else{
            double min_dis = 1e9;
            int min_id = 0;
            for(int j=0; j<chrom; ++j){
                if(!vis[j] && mp[x][j] < min_dis){
                    min_dis = mp[x][j];
                    min_id = j;
                }
            }
            ans.push_back(min_id);
        }
    }
    return ans;
}
pair<double, int> get_best(){
    double min_dis = 1e9;
    int min_id = 0;
    for(int i=0; i<num; ++i){
        double x = cal_distance(g[i]);
        if(min_dis > x){
            min_dis = x;
            min_id = i;
        }
    }
    return {min_dis,min_id};
}
void crosscover(){
    for(int i=0; i<num/2; ++i){
        if((rand()%100+1) <= pc){
            for(int j=0; j<chrom; ++j) neb[j].clear();
            for(int j=0; j<chrom; ++j){
                neb[g[i][j]].push_back(g[i][(j+1)%chrom]);
                neb[g[i][j]].push_back(g[i][(j-1+chrom)%chrom]);
                neb[g[num-i-1][j]].push_back(g[num-i-1][(j+1)%chrom]);
                neb[g[num-i-1][j]].push_back(g[num-i-1][(j-1+chrom)%chrom]);
            }
            for(int j=0; j<chrom; ++j){
                sort(neb[j].begin(), neb[j].end());
                neb[j].erase(unique(neb[j].begin(), neb[j].end()), neb[j].end());
            }
            vector<int>tmp1 = core(i, neb);
            vector<int>tmp2 = core(num-i-1, neb);
            vector<pair<double,vector<int> > >best;
            best.push_back({cal_distance(g[i]), g[i]});
            best.push_back({cal_distance(g[num-i-1]), g[num-i-1]});
            best.push_back({cal_distance(tmp1), tmp1});
            best.push_back({cal_distance(tmp2), tmp2});
            sort(best.begin(), best.end());
            g[i] = best[0].second;
            g[num-i-1] = best[1].second;
        }
    }
}
void mutation(){
    int gai = pm;
    if(cur >= times*3/4) gai = pm*5;
    for(int i=0; i<num; ++i){
        if((rand()%100+1) <= gai){
            int x = rand()%chrom, y = rand()%chrom;
            if(x > y) swap(x, y);
            for(int j=0; x+j<=(x+y>>1); ++j){
                swap(g[i][x+j], g[i][y-j]);
            }
        }
    }
}
int main(){
    //freopen("a.txt", "r", stdin);
    //freopen("2.txt", "w", stdout);
    srand(time(0));
    init_map();
    encoding();
    for(int i=0; i<times; ++i){
        cur = i;
        get_fitness();
        selection();
        pair<double,int>it = get_best();
        debug[i] = g[it.second];
        debug2[i] = it.first;
        crosscover();
        mutation();
    }
    printf("%f\n",debug2[times-1]);
    return 0;
}
//模拟退火算法,2.6s
# include <bits/stdc++.h>
using namespace std;
const int num = 34;
double T0 = 18000;
double T1 = 1e-9;
double r = 0.98;
int len = 1000;
double mp[num+3][num+3];
struct node{
    double x, y;
}point[num+3];
double dis(int u, int v){
    double x = point[u].x-point[v].x;
    double y = point[u].y-point[v].y;
    return sqrt(x*x+y*y);
}
void init_map(){//初始化边的情况
    for(int i=0; i<num; ++i){
        scanf("%lf%lf",&point[i].x,&point[i].y);
    }
    for(int i=0; i<num; ++i){
        for(int j=0; j<num; ++j){
            mp[i][j] = dis(i,j);
        }
    }
}
double cal_distance(vector<int>x){//计算染色体的距离
    double res = mp[x[0]][x[num-1]];
    for(int i=0; i<num-1; ++i)
        res += mp[x[i]][x[i+1]];
    return res;
}
void change(vector<int>&v){
    int x = rand()%num;
    int y = rand()%num;
    while(y == x) y = rand()%num;
    for(int i=0; i+x<=(x+y>>1); ++i)
        swap(v[i+x], v[y-i]);
}
int main(){
    //freopen("a.txt", "r", stdin);
    //reopen("3.txt", "w", stdout);
    srand(time(0));
    vector<int>ans;
    init_map();
    int icount = 0, cnt = 0;
    for(int i=0; i<num; ++i) ans.push_back(i);
    random_shuffle(ans.begin(), ans.end());
    while(T0 > T1){
        for(int i=0; i<len; ++i){
            vector<int>tmp = ans;
            change(tmp);
            double pre = cal_distance(ans);//前
            double cur = cal_distance(tmp);//后
            if(cur < pre || exp(-(cur-pre)/T0) > ((double)rand())/RAND_MAX){
                    ans = tmp;

            }
        }
        ++icount;
        T0 *= r;
    }
    printf("run %d %d times\n",icount,cnt);
    printf("%f\n",cal_distance(ans));
    for(int i:ans) printf("%d ",i);
    return 0;
}
//蚂蚁群算法TSP,33s
# include <bits/stdc++.h>
using namespace std;
const int N = 34;
const int M = 700;
double mp[N+3][N+3];
double phe[N+3][N+3];
double phe2[N+3][N+3];
double rate = 0.5;
double Q = 1000;
int path[M+3][N+3]={0};
int alpha = 1;
int beta = 4;
int times = 700;
bool vis[N+3];
struct node{
    double x,y;
}point[N+3];
double dis(int u, int v){
    double x = point[u].x-point[v].x;
    double y = point[u].y-point[v].y;
    return sqrt(x*x+y*y);
}
void init_map(){//初始化边的情况
    for(int i=0; i<N; ++i){
        scanf("%lf%lf",&point[i].x,&point[i].y);
    }
    for(int i=0; i<N; ++i){
        for(int j=0; j<N; ++j){
            mp[i][j] = dis(i,j);
        }
    }
}
double cal_distance(int a[]){
    double tot = 0;
    for(int i=0; i<N-1; ++i){
        tot += mp[a[i]][a[i+1]];
    }
    tot += mp[a[0]][a[N-1]];
    return tot;
}
int main(){
    //freopen("a.txt", "r", stdin);
    //freopen("4.txt", "w", stdout);
    srand(time(0));
    init_map();
    for(int i=0; i<N; ++i)
        for(int j=0; j<N; ++j)
            phe[i][j] = 1.0;
    for(int T=0; T<times; ++T){
        for(int i=0; i<M; ++i){//遍历每个蚂蚁
            memset(vis, false, sizeof(vis));
            int cur = rand()%N;
            vis[cur] = true;
            path[i][0] = cur;
            for(int j=1; j<N; ++j){
                double pob[N+3] = {0}, POB=0;
                for(int k=0; k<N; ++k){
                    if(!vis[k]){
                        pob[k] = pow(phe[cur][k],alpha)*pow(1.0/mp[cur][k], beta);
                        POB += pob[k];
                    }
                }
                if(POB > 0){
                    double zhuan = rand()*1.0/RAND_MAX * POB;
                    for(int k=0; k<N; ++k){
                        if(!vis[k]){
                            zhuan -= pob[k];
                            if(zhuan <= 0 || k==N-1){
                                cur = k;
                                break;
                            }
                        }
                    }
                }
                vis[cur] = true;
                path[i][j] = cur;
            }


        }
        memset(phe2, 0, sizeof(phe2));//更新信息素
        for(int i=0; i<M; ++i){
            double tot = 0;
            tot += cal_distance(path[i]);
            for(int j=0; j<N-1; ++j){
                phe2[path[i][j]][path[i][j+1]] += Q/tot;
                phe2[path[i][j+1]][path[i][j]] = phe2[path[i][j]][path[i][j+1]];
            }
            phe2[path[i][0]][path[i][N-1]] += Q/tot;
            phe2[path[i][N-1]][path[i][0]] = phe2[path[i][0]][path[i][N-1]];
        }
        for(int i=0; i<N; ++i){
            for(int j=0; j<N; ++j){
                phe[i][j] = phe[i][j]*rate + phe2[i][j];
            }
        }
    }
    double imin = 1e18;
    int id = 0;
    for(int i=0; i<M; ++i){
        double tmp = cal_distance(path[i]);
        if(tmp < imin) imin = tmp, id = i;
    }
    printf("%f\n",imin);
    for(int i=0; i<N; ++i) printf("%d ",path[id][i]);
    return 0;
}
//Hopfield神经网络求解TSP,20s
# include <bits/stdc++.h>
using namespace std;
const int N = 34;
double A = 10000,D = 75, U0 = 0.001, C = N*N;
double mp[N+3][N+3];
double U[(N+3)*(N+3)];
double V[(N+3)*(N+3)];
int id[N+3]={0};
double rnd(){//返回[-1,1]的随机浮点数
    return 1.0*rand()/RAND_MAX*2-1;
}
struct node{
    double x,y;
}point[N+3];
double dis(int u, int v){
    double x = point[u].x-point[v].x;
    double y = point[u].y-point[v].y;
    return sqrt(x*x+y*y);
}
void init(){//初始化边的情况
    for(int i=0; i<N; ++i){
       // scanf("%d",&id);
        scanf("%lf%lf",&point[i].x,&point[i].y);
    }
    for(int i=0; i<N; ++i){
        for(int j=0; j<N; ++j){
            mp[i][j] = dis(i,j);
        }
    }
}
void update_U(){
    double X[N+3] = {0};
    double Y[N+3] = {0};
    double tot = 0;
    for(int i=0; i<N*N; ++i) tot += V[i];
    for(int i=0; i<N; ++i){
        double sum = 0;
        for(int j=0; j<N; ++j)
            sum += V[i*N+j];
        X[i] = sum;
    }
    for(int j=0; j<N; ++j){
        double sum = 0;
        for(int i=0; i<N; ++i)
            sum += V[i*N+j];
        Y[j] = sum;
    }
    for(int i=0; i<N; ++i){
        for(int j=0; j<N; ++j){
            double sum = 0;
            for(int k=0; k<N; ++k){
                sum += mp[i][k]*V[k*N+(j+1)%N];
            }
            U[i*N+j] += (-A*(X[i]-1) - A*(Y[j]-1) - D*sum)*0.0001;
        }
    }
}
void update_V(){
    for(int i=0; i<N*N; ++i){
        V[i] = 0.5*(1+tanh(U[i]/U0));
    }
}
pair<double, vector<int> > check_V(){
    set<int>s;
    vector<int>g;
    double imax[N+3]={0};
    for(int i=0; i<N; ++i) imax[i] = -1e18;
    for(int j=0; j<N; ++j){
        for(int i=0; i<N; ++i){
            imax[j] = max(imax[j], V[i*N+j]);
        }
    }
    for(int j=0; j<N; ++j){
        for(int i=0; i<N; ++i){
            if(V[i*N+j] == imax[j]){
                s.insert(i);
                g.push_back(i);
                break;
            }
        }
    }
    double res = 0;
    if(s.size() != N) return make_pair(1e18, g);
    for(int i=0; i<N; ++i) res += mp[g[i]][g[(i+1)%N]];
    return make_pair(res, g);
}
int main(){
    //freopen("a.txt", "r", stdin);
    //freopen("5.txt", "w", stdout);
    srand(time(0));
    init();
    for(int i=0; i<N*N; ++i) U[i] = 0.5*U0*log(N-1)+rnd();
    update_V();
    vector<int>bb;
    double tmp = 0, ans = 1e18;
    int times = 80000;
    while(times--){
        update_U();
        update_V();
        auto it = check_V();
        if(it.first < ans){
            ans = it.first;
            bb = it.second;
        }
    }
    printf("%f\n",ans);
    for(int i:bb) printf("%d ",i);
    return 0;
}
//禁忌搜索,18s
# include <bits/stdc++.h>
using namespace std;
const int N = 34;
const double inf = 1e18;
double mp[N+3][N+3];
int times = 8000;
int len = 12;
vector<vector<int> >jinji;
map<vector<int>,bool>Hash;
struct node{
    double x, y;
}point[N+3];
double dis(int u, int v){
    double x = point[u].x-point[v].x;
    double y = point[u].y-point[v].y;
    return sqrt(x*x+y*y);
}
void init(){
    for(int i=0; i<N; ++i){
        scanf("%lf%lf",&point[i].x,&point[i].y);
    }
    for(int i=0; i<N; ++i){
        for(int j=0; j<N; ++j){
            mp[i][j] = dis(i,j);
        }
    }
}
double cal(vector<int>g){
    double res = 0;
    for(int i=0; i<N; ++i) res += mp[g[i]][g[(i+1)%N]];
    return res;
}
void update(vector<int>g){
    if(Hash[g]) return;
    jinji.push_back(g);
    if(jinji.size() > len){
        vector<int>tmp = *jinji.begin();
        Hash[tmp]= false;
        jinji.erase(jinji.begin());
    }
}
int main(){
    //freopen("a.txt", "r", stdin);
    //freopen("6.txt", "w", stdout);
    srand(time(0));
    init();
    vector<int>ans;
    for(int i=0; i<N; ++i) ans.push_back(i);
    random_shuffle(ans.begin(), ans.end());
    double ans_val = cal(ans);
    for(int T=0; T<times; ++T){
        double t_val = inf, t_val2 = inf;
        vector<int>t_ans, t_ans2;
        for(int i=0; i<400; ++i){
            vector<int>base = ans;
            int x = rand()%N, y = rand()%N;
            if(x > y) swap(x, y);
            for(int j=0; j<=(y-x>>1); ++j) swap(base[j], base[y-j]);
            double tmp = cal(base);
            if(tmp < t_val){
                t_val = tmp;
                t_ans = base;
            }
            else if(!Hash[base] && tmp < t_val2){
                t_val2 = tmp;
                t_ans = base;
            }
        }
        if(t_val != inf){
            ans = t_ans;
            ans_val = t_val;
        }
        else if(t_val2 != inf){
            ans = t_ans2;
            ans_val = t_val2;
        }
        update(ans);
    }
    printf("%f\n",ans_val);
    for(int i:ans) printf("%d ",i);
    return 0;
}