找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
热搜: 活动 交友 discuz
查看: 68|回复: 0

C++蚁群算法,带图,超详细

[复制链接]

0

主题

0

回帖

26

积分

管理员

积分
26
发表于 2024-7-16 23:12:53 | 显示全部楼层 |阅读模式
  1. //main.cpp
  2. #include"Head.h"
  3. int main() {
  4.         //更新随机种子
  5.         srand((unsigned)time(NULL));
  6.         Graph* graph = new Graph;
  7.         graph->readCity();//启用文件读取,注销掉则为随机构造城市
  8.         graph->innitial_Graph();
  9.         AntColony* ant_colony = new AntColony;
  10.         ant_colony->innitial_AntColony();
  11.         for (int NC_now = 0; NC_now < NC_MAX; ++NC_now) {
  12.                 for (int i = 1; i < N; ++i) {
  13.                         ant_colony->ChooseNextCity(graph, i);
  14.                 }
  15.                 ant_colony->Update(graph,NC_now);
  16.                 ant_colony->innitial_AntColony();
  17.         }
  18.        
  19.         cout << "The BestLength=" << BestLength << endl;
  20.         ShowByPython(graph);
  21.         int last, next;
  22.         double sum = 0;
  23.         for (int i = 1; i < N; ++i) {
  24.                 last = BestPath[i-1];
  25.                 next = BestPath[i];
  26.                 cout << "Travel " << i << " : " << last << "   \t-> " << next << " \tdistante: " << graph->Length[last][next] << endl;
  27.                 sum += graph->Length[last][next];
  28.         }
  29.         last = BestPath[N - 1];
  30.         next = BestPath[0];
  31.         cout << "Travel " << N << " : " << last << "   \t-> " << next << " \tdistante: " << graph->Length[last][next] << endl;
  32.         sum += graph->Length[last][next];
  33.         cout << "最优解路程和 = " << sum << endl;
  34.         cout << "TSPlib上的已知最优解 :" << graph->KnowBest << endl;
  35.         cout << "与已知最优解的偏差为 :" << ((sum - graph->KnowBest) / graph->KnowBest) * 100 << "%" << endl;//最后两句必须启用文件读取才有效
  36.         delete graph;
  37.         delete ant_colony;
  38.         return 0;
  39. }
复制代码


  1. //Head.h
  2. #include<iostream>
  3. #include<cstdlib>
  4. #include<string>
  5. #include<cmath>
  6. #include<fstream>
  7. #include<Python.h>
  8. using namespace std;
  9. #define PATH "E:\\TSPlib\\ch150.txt"//TSPlib数据文件的路径
  10. #define N 150                //城市数量
  11. #define M 100                //蚂蚁数量
  12. #define NC_MAX 1200        //迭代次数
  13. #define α 1                //信息素浓度重要性
  14. #define β 1                //启发式重要性
  15. #define Q 100                //蚂蚁所携带信息素
  16. #define ρ 0.2                //蒸发系数
  17. //注意:如果从文件中读取City,目前需要手动调整城市数量N,要与文件中保持一致
  18. //并且修改 PATH 路径与启用main函数中的readCity函数
  19. //以下数据结构用于最后分析
  20. int BestPath_PerRound[NC_MAX][N] = { 0 };//记录每轮的最优路径
  21. double BestLength_PerRound[NC_MAX] = { 0 };//记录每轮最优路径的长度
  22. int BestPath[N] = { 0 };//全局最短路径(后一轮迭代未必一定比前一轮更优,只是总体是这样,不绝对),不保存每轮的信息
  23. double BestLength = 1000000;//全局最短路径长度
  24. double AverageLength_PerRound[NC_MAX] = { 0 };//记录每轮所有蚂蚁所走路径的平均长度
  25. class City {
  26. public:
  27.         //无参构造函数,创建城市时默认在[0,999]内随机获得x,y坐标
  28.         City() {
  29.                 x = rand() % 1000;
  30.                 y = rand() % 1000;
  31.         }
  32.         double x;
  33.         double y;
  34. };
  35. class Graph {
  36. public:
  37.         City citys[N];//无参构造函数创建N个城市
  38.         double Length[N][N] = { 0 };//路径长度表
  39.         double tao[N][N] = { 0 };//信息素浓度表
  40.         double Heuristic[N][N] = { 0 };//启发式表,将城市之间距离的倒数作为启发函数
  41. public:
  42.         //使用fstream读取文件中的城市坐标,并创建城市
  43.         int KnowBest;//读取文件时存放已知最优解
  44.         void readCity() {
  45.                 ifstream CityFile;
  46.                 CityFile.open(PATH, ios::in);
  47.                 if (!CityFile.is_open()) {
  48.                         cout << "Open file failure" << endl;
  49.                         exit(0);
  50.                 }
  51.                 CityFile >> KnowBest;
  52.                 cout << "The known best result is :" << KnowBest << endl;
  53.                 int a;double b[2];
  54.                 while (!CityFile.eof()) {
  55.                         CityFile >> a >> b[0] >> b[1];
  56.                         citys[a - 1].x = b[0];
  57.                         citys[a - 1].y = b[1];
  58.                 }
  59.                 CityFile.close();
  60.         }
  61.         //初始化城市图,并计算各城市之间的距离
  62.         void innitial_Graph() {
  63.                 double x = 0, y = 0;
  64.                 for (int i = 0; i < N; ++i) {
  65.                         for (int j = 0; j < N; ++j) {
  66.                                 x = citys[i].x - citys[j].x;
  67.                                 y = citys[i].y - citys[j].y;
  68.                                 Length[i][j] = sqrt(pow(x, 2) + pow(y, 2));//就是两点间距离公式
  69.                                 if (i == j) {
  70.                                         Length[i][j] = 10000;//默认城市到自己的距离为10000,防止启发式表出现无穷大,并不会计算城市自己到自己的概率
  71.                                 }
  72.                                 else {
  73.                                         if (x == 0 && y == 0) {
  74.                                                 //这种情况是城市坐标完全重合,如果遇到这种情况则不能继续计算了,否则i与j之间的距离为0,启发式就会为无穷大
  75.                                                 //随之p的分子和分母都会变成无穷大,无穷大除无穷大...
  76.                                                 cout << "innitial_Graph ERROR!!! City overlapping, please retry." << endl;
  77.                                                 cout << "City :" << i << " and City :" << j << endl;
  78.                                                 exit(0);
  79.                                         }
  80.                                 }
  81.                                 Heuristic[i][j] = 1 / Length[i][j];//将距离的倒数作为启发式
  82.                                 tao[i][j] = 1;//信息素tao不能初始化为0
  83.                         }
  84.                 }
  85.         }
  86. };
  87. class AntColony {
  88. public:
  89.         int tabu[M][N] = { 0 };//禁忌列表,同时记录了蚂蚁的路径,每个元素取值为[0,N-1]
  90.         int allow[M][N] = { 0 };//允许列表,完全依赖于禁忌列表,只是为了算法方便一些而设置,无法记录路径,每个元素取值为[0,1]
  91.         double probability[M][N] = { 0 };//概率表,存放各蚂蚁选择下一作城市的概率
  92.         double cumulative_probability[M][N] = { 0 };//累积概率表,用于轮盘赌算法随机选择城市
  93. public:
  94.         //每轮迭代前都应该被调用一次
  95.         void innitial_AntColony() {
  96.                 //初始化各列表
  97.                 for (int i = 0; i < M; ++i) {
  98.                         for (int j = 0; j < N; ++j) {
  99.                                 tabu[i][j] = -1;
  100.                                 allow[i][j] = 1;
  101.                                 probability[i][j] = 0;
  102.                                 cumulative_probability[i][j] = 0;
  103.                         }
  104.                 }
  105.                 //为每一只蚂蚁随机一个初始城市,并将其加入禁忌列表
  106.                 for (int i = 0; i < M; i++) {
  107.                         tabu[i][0] = rand() % N;
  108.                         allow[i][tabu[i][0]] = 0;
  109.                 }
  110.         }
  111.         //求取概率表和累积概率表,然后根据轮盘赌算法选择下一个城市,每轮迭代应该被调用N-1次(初始化时已经有一个城市了)
  112.         void ChooseNextCity(Graph* graph, int times) {//times表示此轮迭代已经是第几次调用这个函数了,times应该从1开始,到N-1次结束
  113.                 //求概率表
  114.                 double sum = 0;
  115.                 int city_now = -1;
  116.                 for (int i = 0; i < M; ++i) {
  117.                         sum = 0;
  118.                         city_now = tabu[i][times - 1];//蚂蚁i目前在城市city_now
  119.                         for (int j = 0; j < N; ++j) {
  120.                                 if (allow[i][j] == 1) {
  121.                                         probability[i][j] = pow(graph->tao[city_now][j], α) * pow(graph->Heuristic[city_now][j], β);
  122.                                         sum += probability[i][j];
  123.                                 }
  124.                                 else {
  125.                                         probability[i][j] = 0;
  126.                                         sum += 0;
  127.                                 }
  128.                         }
  129.                         //上面求出的probability表并不是真正的概率表,而只是概率的分子,下面求出真正的概率表
  130.                         for (int j = 0; j < N; ++j) {
  131.                                 probability[i][j] = probability[i][j] / sum;
  132.                         }
  133.                 }
  134.                 //用概率表求累积概率表
  135.                 for (int i = 0; i < M; ++i) {
  136.                         cumulative_probability[i][0] = probability[i][0];
  137.                         for (int j = 1; j < N; ++j) {
  138.                                 cumulative_probability[i][j] = cumulative_probability[i][j - 1] + probability[i][j];
  139.                         }
  140.                 }
  141.                 //依据累积概率表,用轮盘赌算法为每只蚂蚁选择下一个城市
  142.                 double p = 0;
  143.                 for (int i = 0; i < M; ++i) {
  144.                         p = rand() % 1000;
  145.                         p /= 1000;
  146.                         for (int j = 0; j < N; ++j) {
  147.                                 if (p <= cumulative_probability[i][j]) {
  148.                                         //如果满足此式,则让蚂蚁i前往城市j(下面更新tabu表的操作就是从逻辑上把蚂蚁移动到城市j)
  149.                                         tabu[i][times] = j;
  150.                                         allow[i][j] = 0;
  151.                                         break;
  152.                                 }
  153.                         }
  154.                 }
  155.         }
  156.         //更新函数每次迭代后都应该被调用一次,用于更新信息素表graph->tabu,并记录本轮迭代的相关信息(最优路径,最优路径的长度,所有蚂蚁所走路径的平均长度)
  157.         //NC_now表示目前的迭代次数,应该从0开始,到NC_MAX-1结束,依次调用此函数
  158.         void Update(Graph* graph, int NC_now) {
  159.                 double delta_ktao = 0;        //用于保存当前蚂蚁留在所经边的信息素
  160.                 double delta_tao[N][N] = { 0 };        //此轮迭代的信息素增量表,表示本轮信息素的增量,配合蒸发系数更新信息素表
  161.                 double sum_Length[M] = { 0 };        //保存此轮每只蚂蚁所经过的路径长度
  162.                 int last_city, next_city;
  163.                 //遍历tabu表,计算每只蚂蚁的路径长度,存放在sum_Length[]中
  164.                 for (int i = 0; i < M; ++i) {
  165.                         for (int j = 1; j < N; ++j) {
  166.                                 last_city = tabu[i][j - 1];
  167.                                 next_city = tabu[i][j];
  168.                                 sum_Length[i] += graph->Length[last_city][next_city];
  169.                         }
  170.                         //还要再加上终点到起点的距离
  171.                         last_city = tabu[i][N - 1];
  172.                         next_city = tabu[i][0];
  173.                         sum_Length[i] += graph->Length[last_city][next_city];
  174.                 }
  175.                 //遍历tabu表,计算信息素增量表delta_tao[][]
  176.                 for (int i = 0; i < M; ++i) {
  177.                         delta_ktao = Q / sum_Length[i];
  178.                         for (int j = 1; j < N; ++j) {
  179.                                 last_city = tabu[i][j - 1];
  180.                                 next_city = tabu[i][j];
  181.                                 delta_tao[last_city][next_city] += delta_ktao;
  182.                         }
  183.                 }
  184.                 //利用信息素增量表和蒸发系数更新信息素表tao[][]
  185.                 for (int i = 0; i < N; ++i) {
  186.                         for (int j = 0; j < N; ++j) {
  187.                                 graph->tao[i][j] = graph->tao[i][j] * (1 - ρ) + delta_tao[i][j];
  188.                         }
  189.                 }
  190.                 //计算本轮最优路径,最优路径的长度,所有蚂蚁所走路径的平均长度
  191.                 int flag = 0;
  192.                 double min = 1000000, sum = 0;        //min的初始值必须是一个足够大的值
  193.                 for (int i = 0; i < M; ++i) {
  194.                         sum += sum_Length[i];
  195.                         if (min > sum_Length[i]) {
  196.                                 min = sum_Length[i];
  197.                                 flag = i;//标记最好的蚂蚁
  198.                         }
  199.                 }
  200.                 //记录本轮信息至全局变量中
  201.                 for (int i = 0; i < N; ++i) {
  202.                         BestPath_PerRound[NC_now][i] = tabu[flag][i];
  203.                 }
  204.                 BestLength_PerRound[NC_now] = min;
  205.                 AverageLength_PerRound[NC_now] = sum / M;
  206.                 //更新全局最优路径和其长度
  207.                 if (BestLength > min) {
  208.                         for (int i = 0; i < N; ++i) {
  209.                                 BestPath[i] = BestPath_PerRound[NC_now][i];
  210.                         }
  211.                         BestLength = min;
  212.                 }
  213.                 //用2-opt局部搜索优化本轮最优路径并用信息素强化
  214.                 {
  215.                         int TempPath[N];
  216.                         int flag = false;
  217.                         int t;
  218.                         for (int i = 0; i < N; ++i) {
  219.                                 TempPath[i] = BestPath_PerRound[NC_now][i];
  220.                         }
  221.                         for (int a = 0; a < N-1; ++a) {
  222.                                 //如果路径被优化为新路径,则再对新路径从头来一次局部搜索
  223.                                 if (flag == true) {
  224.                                         a = 0;
  225.                                         flag = false;
  226.                                 }
  227.                                 for (int b = a + 1; b < N; ++b) {
  228.                                         //逆序a~b的路径
  229.                                         for (int i = a, j = b; i < j; ++i, --j) {
  230.                                                 t = TempPath[i];
  231.                                                 TempPath[i] = TempPath[j];
  232.                                                 TempPath[j] = t;
  233.                                         }
  234.                                         //重新求优化后的路径长度,加上终点到起点的距离
  235.                                         sum = 0;
  236.                                         for (int i = 1; i < N; ++i) {
  237.                                                 last_city = TempPath[i - 1];
  238.                                                 next_city = TempPath[i];
  239.                                                 sum += graph->Length[last_city][next_city];
  240.                                         }
  241.                                         last_city = TempPath[N - 1];
  242.                                         next_city = TempPath[0];
  243.                                         sum += graph->Length[last_city][next_city];
  244.                                         //如果此解更优则更新本轮最优解
  245.                                         if (sum < BestLength_PerRound[NC_now]) {
  246.                                                 flag = true;//如果路径被更新,则局部搜索从0开始
  247.                                                 //先更新平均路径再更新最优路径
  248.                                                 AverageLength_PerRound[NC_now] = (AverageLength_PerRound[NC_now] * M - BestLength_PerRound[NC_now] + sum) / M;
  249.                                                 BestLength_PerRound[NC_now] = sum;
  250.                                                 for (int i = 0; i < N; ++i) {
  251.                                                         BestPath_PerRound[NC_now][i] = TempPath[i];
  252.                                                 }
  253.                                                 //如果比全局最优解还优,则更新全局最优解
  254.                                                 if (sum < BestLength) {
  255.                                                         for (int i = 0; i < N; ++i) {
  256.                                                                 BestPath[i] = TempPath[i];
  257.                                                         }
  258.                                                         BestLength = sum;
  259.                                                 }
  260.                                         }
  261.                                 }
  262.                         }
  263.                         //使用2opt局部搜索获得更强的解之后,给最优路径追加信息素奖励
  264.                         double reward = Q / sum;
  265.                         for (int i = 1; i < N; ++i) {
  266.                                 last_city = BestPath[i-1];
  267.                                 next_city = BestPath[i];
  268.                                 graph->tao[last_city][next_city] += reward;
  269.                         }
  270.                 }
  271.         }
  272. };
  273. //此函数的目的是为了用图展示出蚁群算法的结果,用C++调用python的matplotlib库
  274. bool ShowByPython(Graph* graph) {
  275.         //C++中初始化python环境
  276.         Py_Initialize();
  277.         if (!Py_IsInitialized()) {
  278.                 std::cout << "Py_Initialize Error!" << std::endl;
  279.                 return false;
  280.         }
  281.         try {
  282.                 //执行python语句
  283.                 PyRun_SimpleString("import sys");
  284.                 PyRun_SimpleString("import matplotlib.pyplot as plt");
  285.                 string importPy = "sys.argv = ['suibiantian']";
  286.                 PyRun_SimpleString(importPy.c_str());
  287.                 double x, y;
  288.                 PyRun_SimpleString("plt.figure(num=1)");
  289.                 PyRun_SimpleString("plt.title('Ant Colony algorithm for solving TSP')");//蚁群算法解决TSP问题
  290.                 PyRun_SimpleString("plt.xlabel('x')");
  291.                 PyRun_SimpleString("plt.ylabel('y')");
  292.                 importPy= "plt.xlabel('Shortest=" + to_string(BestLength) + "')";
  293.                 PyRun_SimpleString(importPy.c_str());
  294.                 for (int i = 0; i < N; ++i) {
  295.                         x = graph->citys[i].x;//Graph[i].get_x();
  296.                         y = graph->citys[i].y;
  297.                         importPy = "plt.scatter(" + to_string(x) + "," + to_string(y) + ")";
  298.                         PyRun_SimpleString(importPy.c_str());
  299.                 }
  300.                 int last_city, next_city;
  301.                 double X1, X2, Y1, Y2;
  302.                 string str;
  303.                 //把全局最优路径按顺序画出来,画在第一副图上
  304.                 PyRun_SimpleString("plt.figure(num=1)");
  305.                 for (int i = 1; i < N; ++i) {
  306.                         last_city = BestPath[i - 1];
  307.                         next_city = BestPath[i];
  308.                         X1 = graph->citys[last_city].x;
  309.                         Y1 = graph->citys[last_city].y;
  310.                         X2 = graph->citys[next_city].x;
  311.                         Y2 = graph->citys[next_city].y;
  312.                         str = "plt.plot([" + to_string(X1) + "," + to_string(X2) + "],[" + to_string(Y1) + "," + to_string(Y2) + "])";
  313.                         PyRun_SimpleString(str.c_str());
  314.                 }
  315.                 //把终点到起点的线也画上
  316.                 last_city = BestPath[N - 1];
  317.                 next_city = BestPath[0];
  318.                 X1 = graph->citys[last_city].x;
  319.                 Y1 = graph->citys[last_city].y;
  320.                 X2 = graph->citys[next_city].x;
  321.                 Y2 = graph->citys[next_city].y;
  322.                 str = "plt.plot([" + to_string(X1) + "," + to_string(X2) + "],[" + to_string(Y1) + "," + to_string(Y2) + "])";
  323.                 PyRun_SimpleString(str.c_str());
  324.                 //下面展示随着迭代进行,每轮最优路径长度的变化,这是第二幅图
  325.                 double last = 0, next = 0;
  326.                 //string str;
  327.                 PyRun_SimpleString("plt.figure(num=2)");
  328.                 PyRun_SimpleString("plt.title('ShortestLength Convergence graph')");//最优路径收敛图
  329.                 PyRun_SimpleString("plt.xlabel('times of iterations')");
  330.                 PyRun_SimpleString("plt.ylabel('BestLength')");
  331.                 for (int i = 1; i < NC_MAX; ++i) {
  332.                         last = BestLength_PerRound[i - 1];
  333.                         next = BestLength_PerRound[i];
  334.                         str = "plt.plot([" + to_string(i - 1) + "," + to_string(i) + "],[" + to_string(last) + "," + to_string(next) + "])";
  335.                         PyRun_SimpleString(str.c_str());
  336.                 }
  337.                 //下面展示随着迭代进行,每轮平均路径长度的变化,这是第三幅图
  338.                 PyRun_SimpleString("plt.figure(num=3)");
  339.                 PyRun_SimpleString("plt.title('AverageLength Convergence graph')");//平均路径收敛图
  340.                 PyRun_SimpleString("plt.xlabel('times of iterations')");
  341.                 PyRun_SimpleString("plt.ylabel('AverageLength')");
  342.                 for (int i = 1; i < NC_MAX; ++i) {
  343.                         last = AverageLength_PerRound[i - 1];
  344.                         next = AverageLength_PerRound[i];
  345.                         str = "plt.plot([" + to_string(i - 1) + "," + to_string(i) + "],[" + to_string(last) + "," + to_string(next) + "])";
  346.                         PyRun_SimpleString(str.c_str());
  347.                 }
  348.                 //展示上面描绘的三幅图
  349.                 PyRun_SimpleString("plt.show()");
  350.         }
  351.         catch (...) {
  352.                 PyErr_Print();
  353.                 PyErr_Clear();
  354.                 Py_Finalize();
  355.                 return false;
  356.         }
  357.         Py_Finalize();
  358.         return true;
  359. }
复制代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Archiver|手机版|小黑屋|膜结构网

GMT+8, 2024-12-27 22:02 , Processed in 0.146078 second(s), 18 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表