找回密码
 立即注册

QQ登录

只需一步,快速开始

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

C++蚁群算法

[复制链接]

0

主题

0

回帖

26

积分

管理员

积分
26
发表于 2024-7-16 23:11:41 | 显示全部楼层 |阅读模式
  1. #include<iostream>
  2. #include<string>
  3. #include<math.h>
  4. #include<fstream>
  5. #include<vector>
  6. #include<map>
  7. #include<unordered_map>
  8. #include<algorithm>
  9. #include<conio.h>
  10. using namespace std;
  11. #pragma region 变量与常量的定义
  12. //数据文件存放的路径
  13. constexpr auto FILE_PATH = "D:\\data.txt";
  14. //城市数量
  15. const int CITY_NUMBER = 19;
  16. //蚂蚁数量(一般为城市数量的1.5倍)
  17. const int ANT_NUMBER = 50;
  18. //最大迭代次数
  19. const int  MAX_ITERATIONS_NUMBER = 150;
  20. //路径数量
  21. const int  PATH_NUMBER = 74;
  22. //启发因子,信息素浓度重要性
  23. const double ALPHA = 1.0;
  24. //启发式重要性
  25. const double BETA = 2.0;
  26. //蚂蚁所携带总的信息素
  27. const double Q = 100.0;
  28. //信息素蒸发系数
  29. const double ROU = 0.5;
  30. //两两城市之间的信息素矩阵
  31. vector<vector<double>> pheromoneMatrix;
  32. //两两城市之间的距离
  33. vector<vector<double>> cityDistance;
  34. //下一代的信息素矩阵
  35. vector<vector<double>> nextPheromoneMatrix;
  36. //启发信息矩阵=1/cityDistance[i][j]
  37. vector<vector<double>> inspMartix;
  38. //关键字:string类型,值:int类型。分别对应保存每次迭代的最优路径和对应的路径距离。
  39. map<string, int>routResult;
  40. //含有充电设施的站点编号
  41. vector<int>rechargeNumber = { 3,4,7,8,12,14,16 };
  42. #pragma endregion
  43. #pragma region 蚂蚁
  44. class Ant {
  45. public:
  46.         //蚂蚁的当前位置
  47.         int antLoc;
  48.         //蚂蚁的禁忌表,存放已访问过的点
  49.         int taBu[CITY_NUMBER];
  50.         //蚂蚁走过路径的点的集合
  51.         int antPath[PATH_NUMBER];
  52.         //判断蚂蚁是否已经到达终点。True:表示到达,FALSE:表示未到达。
  53.         bool flag;
  54. };
  55. //初始化蚁群,大小为ANT_NUMBER
  56. Ant antColony[ANT_NUMBER];
  57. #pragma endregion
  58. #pragma region 城市
  59. class City {
  60. public:
  61.         //城市编号
  62.         int cityNum;
  63.         //选择每个点的概率
  64.         double cityProb;
  65. };
  66. //初始化城市
  67. City cityArray[CITY_NUMBER];
  68. //线性化选择概率,用于轮盘赌算法
  69. double lineCityProb[CITY_NUMBER];
  70. #pragma endregion
  71. #pragma region 矩阵初始化功能函数
  72. //res:表示待初始化的矩阵
  73. //temp:表示矩阵初始化的值
  74. void initMatrix(vector<vector<double>> &res, int temp) {
  75.         //定义中间变量v
  76.         vector<double>v;
  77.         for (int i = 0; i < CITY_NUMBER; ++i) {
  78.                 //清空v
  79.                 vector<double>().swap(v);
  80.                 for (int j = 0; j < CITY_NUMBER; ++j) {
  81.                         v.push_back(temp);
  82.                 }
  83.                 res.push_back(v);
  84.         }
  85. }
  86. #pragma endregion
  87. #pragma region 返回指定范围内的随机数
  88. ///                返回指定范围内的随机浮点数
  89. ///     @param dbLow                范围起始数   
  90. ///     @param dbUpper  范围终止数
  91. ///     @return                                        返回的此范围内的随机数
  92. double randomNumber(double dbLow, double dbUpper)
  93. {
  94.         double dbTemp = rand() / ((double)RAND_MAX + 1.0);
  95.         return dbLow + dbTemp * (dbUpper - dbLow);
  96. }
  97. #pragma endregion
  98. #pragma region 蚁群初始化
  99. void initAntsColony() {
  100.         //初始化禁忌表和行走路线
  101.         for (int i = 0; i < ANT_NUMBER; ++i) {
  102.                 for (int j = 0; j < CITY_NUMBER; ++j) {
  103.                         //初始化蚁群的禁忌表,设置每只蚂蚁的禁忌表初始化为-1,代表从未访问任何点
  104.                         antColony[i].taBu[j] = -1;
  105.                 }
  106.                 for (int j = 0; j < PATH_NUMBER; ++j) {
  107.                         //初始化蚁群的行走路径,设置每只蚂蚁的路径初始化为-1,代表还未经过任何点
  108.                         antColony[i].antPath[j] = -1;
  109.                 }
  110.         }
  111.         //将蚂蚁放入初始位置,此处设置每只蚂蚁的出发点为0,代表从源点出发
  112.         for (int i = 0; i < ANT_NUMBER; ++i) {
  113.                 //设置蚂蚁的当前位置为出发位置0
  114.                 antColony[i].antLoc = 0;
  115.                 //将当前位置更新到每只蚂蚁的禁忌表中,代表已经访问过此点
  116.                 antColony[i].taBu[0] = 0;
  117.                 //将当前位置更新到每只蚂蚁的行走路径中,代表路径中已有此点
  118.                 antColony[i].antPath[0] = 0;
  119.                 //将每只蚂蚁的flag设置为0,代表每只蚂蚁都还未到达终点
  120.                 antColony[i].flag = 0;
  121.         }
  122. }
  123. #pragma endregion
  124. #pragma region 初始化城市
  125. void initCity() {
  126.         for (int i = 0; i < CITY_NUMBER; ++i) {
  127.                 //初始化城市结构体
  128.                 //初始化每个城市的编号为-1
  129.                 cityArray[i].cityNum = -1;
  130.                 //初始化每个城市的选择概率为0
  131.                 cityArray[i].cityProb = 0;
  132.                 //初始化线性选择概率为0
  133.                 lineCityProb[i] = 0;
  134.         }
  135. }
  136. #pragma endregion
  137. #pragma region 初始化所需的各种矩阵,并读取文件距离数据
  138. void initVariousMatrix() {
  139.         //初始化城市距离矩阵为-1
  140.         initMatrix(cityDistance, -1);
  141.         /*             数据文件的读取                */
  142.         //创建文件流对象
  143.         ifstream CityFile;
  144.         //打开文件
  145.         CityFile.open(FILE_PATH, ios::in);
  146.         //如果文件打开失败,则退出
  147.         if (!CityFile.is_open()) {
  148.                 cout << "Open file failure" << endl;
  149.                 exit(0);
  150.         }
  151.         //从文件中读取数据到城市距离矩阵之中
  152.         for (int i = 0; i < CITY_NUMBER; ++i) {
  153.                 for (int j = 0; j < CITY_NUMBER; ++j) {
  154.                         CityFile >> cityDistance[i][j];
  155.                 }
  156.         }
  157.         //关闭文件
  158.         CityFile.close();
  159.         //初始化信息素矩阵为0
  160.          initMatrix(pheromoneMatrix, 0);
  161.         //初始化下一代信息素矩阵为0
  162.         initMatrix(nextPheromoneMatrix, 0);
  163.         //初始化启发信息矩阵为0
  164.          initMatrix(inspMartix, 0);
  165.         for (int i = 0; i < CITY_NUMBER; ++i) {
  166.                 for (int j = 0; j < CITY_NUMBER; ++j) {
  167.                         if (cityDistance[i][j] != -1) {                 //如果两个城市之间有距离
  168.                                 inspMartix[i][j] = 1 / cityDistance[i][j];//启发信息为两两城市距离的倒数
  169.                                 pheromoneMatrix[i][j] = 1;                //路径上信息素浓度初始值为1
  170.                         }
  171.                 }
  172.         }
  173. }
  174. #pragma endregion
  175. #pragma region 判断城市j是否在蚂蚁k的禁忌表中
  176. bool ifCityInTabu(int k, int j) {
  177.         for (int i = 0; i < CITY_NUMBER; ++i) {
  178.                 //如果城市j,已经在蚂蚁k的禁忌表中,则返回1
  179.                 if (j == antColony[k].taBu[i]) {
  180.                         return 1;
  181.                 }
  182.         }
  183.         //如果不在蚂蚁k的禁忌表中,则返回0
  184.         return 0;
  185. }
  186. #pragma endregion
  187. #pragma region 蚂蚁k从当前城市i选择下一步行进的城市j的概率
  188. double nextCitySelProb(int k, int j) {
  189.         //初始化选择概率公式的分子
  190.         double p = 0;
  191.         //初始化选择概率公式的分母
  192.         double sum = 0;
  193.         //i保存蚂蚁k的当前位置
  194.         int i = antColony[k].antLoc;
  195.         //计算P
  196.         p = pow(pheromoneMatrix[i][j], ALPHA) * pow(inspMartix[i][j], BETA);
  197.         //计算sum
  198.         for (int m = 0; m < CITY_NUMBER; ++m) {
  199.                 if (cityDistance[i][j] != -1 && !ifCityInTabu(k, m)) {
  200.                         sum += pow(pheromoneMatrix[i][m], ALPHA)*pow(inspMartix[i][m], BETA);
  201.                 }
  202.         }
  203.         //返回从i--j的选择概率
  204.         return (p / sum);
  205. }
  206. #pragma endregion
  207. #pragma region 更新蚂蚁k的禁忌表信息
  208. void updateAntTabu(int k, int j) {
  209.         //蚂蚁k的当前位置赋值为j
  210.         antColony[k].antLoc = j;
  211.         for (int i = 0; i < CITY_NUMBER; ++i) {
  212.                 //如果蚂蚁k还未访问过j,则将蚂蚁k的禁忌表信息添加j
  213.                 if (antColony[k].taBu[i] == -1) {
  214.                         antColony[k].taBu[i] = j;
  215.                         break;
  216.                 }
  217.         }
  218.         //同理,将蚂蚁k的行走路径中更新一个j
  219.         for (int i = 0; i < PATH_NUMBER; ++i) {
  220.                 if (antColony[k].antPath[i] == -1) {
  221.                         antColony[k].antPath[i] = j;
  222.                         break;
  223.                 }
  224.         }
  225. }
  226. #pragma endregion
  227. #pragma region 轮盘赌选择下一步行进的城市
  228. //蚂蚁k,选择前进的地点j的概率
  229. int nextCitySelect(int k, int f) {
  230.         //记录蚂蚁可行进的城市个数,用于后面的计算
  231.         int c = 0;
  232.         //step 1:计算可行进的各城市的选择概率
  233.         for (int m = 0; m < CITY_NUMBER; ++m) {
  234.                 //若城市(i,j)之间有路且j不在蚂蚁k的禁忌表中,则计算概率
  235.                 if (cityDistance[antColony[k].antLoc][m] != -1 && !ifCityInTabu(k, m)) {
  236.                         //对应的城市编号存入数组中
  237.                         cityArray[c].cityNum = m;
  238.                         //选择概率存入数组中
  239.                         cityArray[c].cityProb = nextCitySelProb(k, m);
  240.                         ++c;
  241.                 }
  242.         }
  243.         //step 2:线性化选择概率
  244.         for (int m = 0; m < c; ++m) {
  245.                 for (int n = m; n >= 0; n--) {
  246.                         //用于轮盘赌算法,将选择概率线性化累加
  247.                         lineCityProb[m] += cityArray[n].cityProb;
  248.                 }
  249.         }
  250.         //step 3 产生随机数选择城市
  251.         //产生随机浮点数,范围0-1
  252.         double r = randomNumber(0, 1);
  253.         //选取的目标城市
  254.         int j = 0;
  255.         for (int m = 0; m < CITY_NUMBER; ++m) {
  256.                 if (r <= lineCityProb[m]) {
  257.                         //将当前选择前进的城市编号赋值给j
  258.                         j = cityArray[m].cityNum;
  259.                         //将地点j更新至蚂蚁k的禁忌表中,代表已经访问过此点
  260.                         updateAntTabu(k, j);
  261.                         if (j == f) {
  262.                                 //若蚂蚁k下一步城市为目的地城市,则修改标志为1
  263.                                 antColony[k].flag = 1;
  264.                         }
  265.                         //return j;
  266.                         return 0;
  267.                 }
  268.         }
  269.         //return f;
  270.         return 0;
  271. }
  272. #pragma endregion
  273. #pragma region 计算路径长度
  274. int getAntLen(Ant ant) {
  275.         //定义路径长度为0
  276.         int len = 0;
  277.         for (int i = 0; i < PATH_NUMBER - 1; ++i) {
  278.                 //如果蚂蚁行走的路径中有-1,表示未走过此点,则退出循环
  279.                 if (ant.antPath[i] == -1 || ant.antPath[i + 1] == -1)
  280.                         break;
  281.                 //若走过有路径,则不断累加距离结果
  282.                 else
  283.                         len += cityDistance[ant.antPath[i]][ant.antPath[i + 1]];
  284.         }
  285.         //返回路径长度
  286.         return len;
  287. }
  288. #pragma endregion
  289. #pragma region 计算最优路径对应的蚂蚁编号
  290. int getBestPathAntNumber() {
  291.         //初始化数组d为-1,保存所有蚂蚁的行走路径
  292.         vector<int>d(ANT_NUMBER, -1);
  293.         //假设定义蚂蚁k的路线到达目的地节点最短,即走过的路径为最短路径
  294.         int k = 0;
  295.         //将所有路径距离存入d中
  296.         for (int i = 0; i < ANT_NUMBER; i++)
  297.         {
  298.                 d.push_back(getAntLen(antColony[i]));
  299.         }
  300.         //定义指向最小路径的迭代器
  301.         auto min = min_element(d.begin(), d.end());
  302.         //计算此最短路径所对应的下标,此为蚂蚁的编号k
  303.         k = distance(d.begin(), min);
  304.         vector<int>(d).swap(d);
  305.         //返回蚂蚁k
  306.         return k;
  307. }
  308. #pragma endregion
  309. #pragma region 打印最优路径所对应的蚂蚁K的路径和距离
  310. void printBestPath(int k, int f) {
  311.         //初始化变量res,存放路线
  312.         string res = "";
  313.         cout << " 最短路径为:";
  314.         for (int i = 0; i < PATH_NUMBER - 1; ++i) {
  315.                 //如果蚂蚁k的第i条路径为-1,表示没有走过此点,退出循环
  316.                 if (antColony[k].antPath[i] == -1)
  317.                         break;
  318.                 //打印蚂蚁k的第i条路径
  319.                 cout << antColony[k].antPath[i];
  320.                 //将路径转化为字符串形式存入res中,方便打印输出
  321.                 res += to_string(antColony[k].antPath[i]);
  322.                 //若蚂蚁k的i+1条路径不为-1,代表访问过此点。即i--->i+1有路线。
  323.                 if (antColony[k].antPath[i + 1] != -1) {
  324.                         //打印"->"
  325.                         cout << "->";
  326.                         // 若此点含有充电设施
  327.                         if (find(rechargeNumber.begin(), rechargeNumber.end(), antColony[k].antPath[i + 1]) != rechargeNumber.end()) {
  328.                                 res += "->(可充电站点)";
  329.                         }
  330.                         else {
  331.                                 res += "->";
  332.                         }
  333.                 }
  334.         }
  335.         cout << endl;
  336.         cout << " 对应距离为:" << getAntLen(antColony[k]) << endl;
  337.         //把每次距离结果保存到map中:关键字:路线,值:对应的路径长度
  338.         routResult[res] = getAntLen(antColony[k]);
  339.         res.swap(res);
  340.         //return routResult;
  341. }
  342. #pragma endregion
  343. #pragma region 更新信息素矩阵
  344. void updatePherMartix() {
  345.         for (int i = 0; i < ANT_NUMBER; ++i) {
  346.                 //全局更新信息素矩阵
  347.                 if (antColony[i].flag == 1) {
  348.                         for (int j = 0; j < PATH_NUMBER - 1; ++j) {
  349.                                 if (antColony[i].antPath[j] == -1 || antColony[i].antPath[j + 1] == -1)
  350.                                         break;
  351.                                 else
  352.                                         nextPheromoneMatrix[antColony[i].antPath[j]][antColony[i].antPath[j + 1]]
  353.                                         += Q / getAntLen(antColony[i]);
  354.                         }
  355.                 }
  356.         }
  357.         //更新下一代信息素矩阵
  358.         for (int i = 0; i < CITY_NUMBER; ++i) {
  359.                 for (int j = 0; j < CITY_NUMBER; ++j) {
  360.                         nextPheromoneMatrix[i][j] =
  361.                                 (1 - ROU) * pheromoneMatrix[i][j] + nextPheromoneMatrix[i][j];
  362.                 }
  363.         }
  364. }
  365. #pragma endregion
  366. #pragma region 迭代过程
  367. void evolution(int city) {
  368.         cout << "开始进行迭代........." << endl;
  369.         //初始化参数
  370.         initAntsColony();
  371.         initCity();
  372.         initVariousMatrix();
  373.         int gen = 0;
  374.         while (gen < MAX_ITERATIONS_NUMBER) {
  375.                 int p = 0;
  376.                 while (p <11) {
  377.                         for (int i = 0; i < ANT_NUMBER; ++i) {
  378.                                 if (antColony[i].flag == 1)
  379.                                         continue;
  380.                                 nextCitySelect(i, city);
  381.                                 initCity();
  382.                         }
  383.                         ++p;
  384.                 }
  385.                
  386.                 if (gen == MAX_ITERATIONS_NUMBER - 1) {
  387.                         cout << " 迭代完成,输出结果" << endl;
  388.                         printBestPath(getBestPathAntNumber(), city);
  389.                 }
  390.                
  391.                
  392.                 //更新信息素矩阵
  393.                 updatePherMartix();
  394.                 //蚁群初始化
  395.                 initAntsColony();
  396.                 pheromoneMatrix = nextPheromoneMatrix;
  397.             initMatrix(nextPheromoneMatrix, 0);
  398.                 ++gen;
  399.         }
  400. }
  401. #pragma endregion
  402. #pragma region 释放vector内存空间,避免内存无限增加而崩溃
  403. void deleArrary() {
  404.         vector<vector<double>>().swap(cityDistance);
  405.         vector<vector<double>>().swap(pheromoneMatrix);
  406.         vector<vector<double>>().swap(nextPheromoneMatrix);
  407.         vector<vector<double>>().swap(inspMartix);
  408.         //map<string, int>().swap(routResult);
  409. }
  410. #pragma endregion
  411. #pragma region 按从小到大的顺序将vec内部value值进行排序,并打印出结果
  412. void sortMap(map<string, int>t) {
  413.         //routeResult存入vec中,方便将结果进行排序
  414.         vector<pair<string, int>>vec;
  415.         for (const auto& n : t) {
  416.                 vec.push_back(make_pair(n.first, n.second));
  417.         }
  418.         //将结果按照路径,从小到达进行排序
  419.         sort(vec.begin(), vec.end(), [](const pair<string, int>& x, const pair<string, int>& y)
  420.                 -> int {
  421.                         return x.second < y.second;
  422.                 });
  423.         //输出排序好的所有结果
  424.         cout << "可供选择的路径中,从小到大为:" << endl;
  425.         int i = 1;
  426.         for (auto v : vec) {
  427.                 cout << "第" << i << "条路径为:         ";
  428.                 cout << v.first << " " << "距离为:"
  429.                         << v.second << endl;
  430.                 ++i;
  431.         }
  432.         cout << endl;
  433.         cout << "建议采纳的最佳路线为:" << vec[0].first << " "
  434.                 << "此路线距离最短,耗时最小为:"
  435.                 << vec[0].second << endl;
  436.         for (auto v : vec)
  437.         {
  438.                 if (vec[0].first.find("可充电站点") != -1) {
  439.                         break;
  440.                 }
  441.                 else if (v.first.find("可充电站点") != -1) {
  442.                         cout << "若需要中途停靠充电,建议采纳的最佳路线为:"
  443.                                 << v.first
  444.                                 << "    此路线含有充电设施,总的路径为:"
  445.                                 << v.second
  446.                                 << endl;
  447.                         break;
  448.                 }
  449.         }
  450.         t.swap(t);
  451.         vector<pair<string, int>>().swap(vec);
  452. }
  453. #pragma endregion
  454. #pragma region 程序入口
  455.         int main() {
  456.         cout << " 请输入你要去往的城市:" << endl;
  457.         int city;
  458.         cin >> city;
  459.         //不断循环迭代,将每次循环的最优结果保存到routResult中
  460.         while (1) {
  461.                 //随机化种子,用于生成随机数
  462.                 srand((unsigned)time(NULL));
  463.                 //开始进入迭代过程
  464.                 evolution(city);
  465.                 //每次迭代完成后,清空数组数据
  466.                 deleArrary();
  467.                 //判断键盘响应
  468.                 if (_kbhit()) {
  469.                         //如果读取到键盘按下'q'或'Q'键,则退出循环,结束整个过程
  470.                         if (tolower(_getch()) == 'q') {
  471.                                 break;
  472.                         }
  473.                 }
  474.         }
  475.         //int x = 20;
  476.         //while (x) {
  477.         //        //随机化种子,用于生成随机数
  478.         //        srand((unsigned)time(NULL));
  479.         //        //开始进入迭代过程
  480.         //        evolution(city);
  481.         //        //每次迭代完成后,清空数组数据
  482.         //        deleArrary();
  483.         //        --x;
  484.         //}
  485.         //将所有保存的结果按照从小到大进行排序输出,并打印出最佳结果
  486.         sortMap(routResult);
  487.         return 0;
  488. }
  489. #pragma endregion
复制代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

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

GMT+8, 2024-12-27 22:11 , Processed in 0.127513 second(s), 19 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

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