C++:蚁群算法解决TSP(C++多线程版)

本文介绍了使用C++编程实现蚁群算法解决旅行商问题(TSP),并应用在att48数据集上,采用多线程优化计算效率。内容包括对TSP问题的定义、蚁群算法的原理以及具体的C++代码实现。

TSP问题:旅行商问题,最短回路。

这里采用att48数据,邻接矩阵全部取整数,原数据放在文后。

解决代码如下:

//#define TEST_INPUT
//#define TEST_T
//#define TEST_ANT
//#define TEST_VALUE

#include<cstdio>
#include<cstring>
#include<thread>
#include<vector>
#include<cstdlib>
#include<algorithm>
#include<ctime>
#include<cmath>

using namespace std;

#define eps 0.000001     //浮点数精度
#define maxn 50         //最大支持50座城市


mutex lock;             //锁


int city = 48;          //城市数量
int num = 1000;       //蚂蚁数量
int iteration = 100;          //迭代次数
double alpha = 1;           //信息素比重
double beta = 2;            //开销比重
double evaporation = 0.997;   //蒸发速率
double Q = 100;             //信息素增加量


int G[maxn+2][maxn+2];      //开销,城市编号从1开始
double D[maxn+2][maxn+2];   //开销倒数
double T[maxn+2][maxn+2];   //信息素


int ans = 0x7fffffff;   //最优解
vector<int> res;        //最优路径


//print the result
void output()
{
    printf("the shortest circle: ");
    for(vector<int>::iterator p=res.begin();p!=res.end();p++)
    {
        printf("%d%s",*p,p==res.end()-1?"\n":" ");
    }
    printf("the shortest circle cost: %d\n",ans);
}

//output to the file
void output_file()
{
    FILE* f = fopen("output.txt","w");
    fprintf(f,"%d\n",ans);
    for(vector<int>::iterator p=res.begin();p!=res.end();p++)
    {
        fprintf(f,"%d%s",*p,p==res.end()-1?"\n":" ");
    }
    fclose(f);
}

//print the and T
void print_T()
{
    printf("\nT:\n");
    for(int i=1;i<city+1;i++)
    {
        for(int j=1;j<city+1;j++)
        {
            printf("%f%s",T[i][j],j==city?"\n":" ");
        }
    }
}

//初始化
void init()
{
    memset(G,0,sizeof(G));
    memset(D,0,sizeof(D));
    memset(T,0,sizeof(T));
    res.clear();
    FILE* f = NULL;
    f = fopen("att48_d.txt","r");


#ifdef TEST_INPUT
    fclose(f);
    f = fopen("input.txt","r");
    city = 9;
    num = 100;
    iteration = 100;
#endif

    for(int i=1;i<city+1;i++)
    {
        for(int j=1;j<city+1;j++)
        {
            fscanf(f,"%d",&G[i][j]);
        }
    }
    fclose(f);

    for(int i=1;i<city+1;i++)
    {
        for(int j=1;j<city+1;j++)
        {
            if(i==j) continue;
            D[i][j] = 1.0/G[i][j];       //初始D为开销的倒数
            T[i][j] = 1.0;              //初始信息素T为1
        }
    }
}


//[x,y]随机整数
int get_random_int(int x,int y)
{
    return (int)rand()%(y-x+1)+x;
}


//[x,y]随机小数
double get_random_float(int x,int y)
{
    return (double)rand()/(RAND_MAX)*(y-x) + x;
}


//蚂蚁
void ant(int id)
{
    vector<int> A;                                      //已经访问过
    vector<int> B;                                      //需要访问
    int s = get_random_int(1,city);
    A.push_back(s);                                     //随机选择起始城市
    for(int i=1;i<city+1;i++) if(i!=s) B.push_back(i);  //待访问城市列表

    while(!B.empty())
    {
        double sum = 0;
        double best_v = 0;
        vector<int>::iterator best_city;

        lock.lock();

        //获取所有去向的评估值之和
        for(vector<int>::iterator p=B.begin();p!=B.end();p++) sum+=(pow(T[s][*p],alpha)*pow(D[s][*p],beta));

        //评估每一个需要被访问的城市
        for(vector<int>::iterator p=B.begin();p!=B.end();p++) {
            //当前城市
            int c = *p;

            //当前城市评估值
            double v = pow(T[s][c], alpha) * pow(D[s][c], beta) / sum;

            if (v > best_v) {
                best_city = p;
                best_v = v;

            }

            //评估值越大的城市越有几率成为下一个访问城市
            double r = get_random_float(0, 1);

#ifdef TEST_VALUE
            printf("ant:%d    last:%d    now:%d   r:%f      v:%f\n", id, *(A.end() - 1), c, r, v);
#endif
            if (r <= v + eps) {                         //这个比较值需要修改
                A.push_back(c);
                B.erase(p);
                break;
            }
        }

        lock.unlock();
    }

    //蚂蚁走完了所有城市
    int sum = 0;

    //获取路径总花费
    for(vector<int>::iterator p=A.begin()+1;p!=A.end();p++)
    {
        sum+=G[*(p-1)][*p];

#ifdef TEST_ANT
        printf("ant:%d   sum:%d   s:%d t:%d\n",id,sum,now,*p);
#endif

    }
    sum+=G[*(A.end()-1)][*(A.begin())];

#ifdef TEST_ANT
    printf("ant:%d   sum:%d   s:%d t:%d\n",id,sum,*(A.end()-1),*(A.begin()));
#endif

    //更新信息素
    lock.lock();
    for(vector<int>::iterator p=A.begin()+1;p!=A.end();p++)
    {
        T[*(p-1)][*p]+=(Q/sum);
    }
    T[*(A.end()-1)][*(A.begin())]+=(Q/sum);

    //如果当前最佳路径为空,则用当前路径初始化最佳路径
    if(res.empty())
    {
        res = A;
        ans = sum;
    }
    else if(sum < ans)
    {
        res = A;
        ans = sum;
    }

#ifdef TEST_T
    printf("\nant:%d\n",id);
    print_T();
#endif

    lock.unlock();
}


int main()
{
    srand((unsigned)time(0));
    init();
    for(int k=0;k<iteration;k++)
    {
#ifdef TEST_T
        printf("iteration:%d\n",k);
#endif

        vector<thread> t;
        for(int i=0;i<num;i++) t.push_back(thread(ant,i));
        for(int i=0;i<num;i++) t[i].join();

        for(int i=1;i<city+1;i++)
        {
            for(int j=1;j<city+1;j++)
            {
                T[i][j]*=evaporation;
            }
        }
    }
    output();
    output_file();
    return 0;
}

att48_d.txt数据:

         0      4727      1205      6363      3657      3130      2414       563       463      5654      1713      1604      2368      2201      1290      1004      3833      2258      3419      2267      2957       720      1700      5279      2578      6076      3465      2654      3625      3115      1574      3951      1748      2142      6755      2383      3306      102
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值