• C++下基于遗传算法解决TSP问题


    TSP问题的遗传算法实现(C++)_tsp问题c++_努力学习的小菜°的博客-CSDN博客

    一、原理

    遗传算法求解过程,与模拟退火类似,也是猜答案,然后根据迭代找一个最优的解。

    思路,首先随机生成100(数字自己定)个种群,这100个种群包含100条随机生成的路径,然后对这些路径按照最小代价排序,最小代价的排在最后,然后进入迭代步骤,假设迭代1000次,每次迭代包含选择、交叉、变异这3个步骤。

    选择:根据累加概率,代价小的路径被选择到的概率越大,所以最终100个种群中有许多重复的路径。

    交叉:以一定概率,例如0.9,对相邻的两个路径,随机截取其中一段,进行交换节点,然后将重复节点替换成不重复的。

    变异:以一定概率,例如0.1,对每个路径,随机选两个点进行交换。

    所以以上这些步骤,都是随机猜最优结果,每次迭代计算一下哪个猜测的结果最符合实际需要,感觉就是在撞库,对于15个城市,如果暴力枚举的话,需要计算14!=87178291200次,好像暴力破解计算量确实有些大。如果用遗传算法,大概100*1000=100000次。

    二、代码

    1. //https://blog.csdn.net/qq_45907357/article/details/125113036
    2. #include
    3. #include
    4. #include
    5. #include
    6. #include
    7. #include
    8. #include
    9. #define GROUP_NUM 100 //种群规模
    10. #define CITY_NUM 15 //城市数量
    11. #define ITERATION_NUM 1000 //最大迭代次数
    12. #define Pc 0.9 //交叉率
    13. #define Pm 0.1 //变异率
    14. using namespace std;
    15. //路线类
    16. class Route {
    17. public:
    18. vector<int> seq; //路线的城市顺序
    19. double fitness; //适应度(定义为城市序列中相邻两城的距离之和的倒数)
    20. double Ps; //生存概率(被选择概率)
    21. double dis; //路线距离
    22. //构造函数
    23. Route() {
    24. seq = vector<int>(CITY_NUM + 1);
    25. fitness = 0;
    26. Ps = 0;
    27. }
    28. };
    29. //城市坐标类
    30. class City {
    31. public:
    32. int x; //横坐标
    33. int y; //纵坐标
    34. };
    35. //为自定义类(Route)制定排序规则
    36. //升序排列,即生存概率高的排在后面
    37. bool my_cmp(Route r1, Route r2) {
    38. return r1.Ps < r2.Ps;
    39. }
    40. //城市之间的距离矩阵
    41. vectordouble>> dis(CITY_NUM, vector<double>(CITY_NUM, 0.0));
    42. //种群
    43. vector group(GROUP_NUM);
    44. //城市
    45. vector city(CITY_NUM);
    46. //城市初始化函数,随机生成CITY_NUM个二维坐标节点,计算城市间的距离并存在距离矩阵中
    47. void city_init() {
    48. //设城市全部坐落在100 * 100的二维平面内
    49. //种下随机种子,使每次运行生成的城市坐标不同
    50. srand((unsigned)time(NULL));
    51. cout << "生成的随机城市坐标:" << endl;
    52. for (int i = 0; i < CITY_NUM; i++) {
    53. //为每个城市随机生成坐标
    54. city[i].x = rand() % 100;
    55. city[i].y = rand() % 100;
    56. cout << i << " " << '(' << city[i].x << ", " << city[i].y << ')' << endl;
    57. }
    58. //计算城市距离,城市i到城市j的距离与城市j到i的距离相等
    59. for (int i = 0; i < CITY_NUM; i++) {
    60. for (int j = i; j < CITY_NUM; j++) {
    61. int temp1 = (city[i].x - city[j].x) * (city[i].x - city[j].x);
    62. int temp2 = (city[i].y - city[j].y) * (city[i].y - city[j].y);
    63. dis[i][j] = sqrt(temp1 + temp2);
    64. dis[j][i] = dis[i][j];
    65. }
    66. }
    67. }
    68. //种群初始化函数,生成GROUP_NUM个初始随机访问城市序列
    69. void group_init() {
    70. srand((unsigned)time(NULL));//随机数发生器
    71. for (int i = 0; i < GROUP_NUM; i++) {//一共生成GROUP_NUM个随机路线
    72. //用哈希表防止序列中生成重复的城市
    73. unordered_map<int, int> mp;
    74. for (int j = 0; j < CITY_NUM; j++) {
    75. int num = rand() % CITY_NUM;
    76. //如果随机生成的数重复了,则重新生成直到不重复为止
    77. while (mp[num] != 0) {//如果已经生成过了则重新生成
    78. num = rand() % CITY_NUM;
    79. }
    80. mp[num]++;
    81. group[i].seq[j] = num;//路线添加随机点
    82. }
    83. group[i].seq[CITY_NUM] = group[i].seq[0];//最后一个航点为起点
    84. }
    85. /*
    86. cout << "初始种群:" << endl;
    87. for(int i = 0; i < GROUP_NUM; i++) {
    88. for(int j = 0; j < CITY_NUM; j++) {
    89. cout << group[i].seq[j] << " ";
    90. }
    91. cout << endl;
    92. }*/
    93. }
    94. //计算初始种群中每个个体的适应度及生存概率
    95. //适应度设置为序列中相邻两城之间的距离之和
    96. void cal_group() {
    97. //种群总适应度
    98. double total_fit = 0.0;
    99. //计算每个个体的适应度
    100. for (int i = 0; i < GROUP_NUM; i++) {
    101. double total_dis = 0;
    102. for (int j = 1; j <= CITY_NUM; j++) {
    103. total_dis += dis[group[i].seq[j]][group[i].seq[j - 1]];
    104. }
    105. group[i].dis = total_dis;
    106. //个体的适应度为总距离
    107. group[i].fitness = 1.0 / total_dis;
    108. //测试计算出来的路径和是否正确
    109. //cout << total_dis << " " << group[i].fitness << endl;
    110. total_fit += group[i].fitness;
    111. }
    112. //计算每个个体的生存概率(被选择概率),为个体适应度 / 总适应度
    113. for (int i = 0; i < GROUP_NUM; i++) {
    114. group[i].Ps = group[i].fitness / total_fit;
    115. }
    116. }
    117. //打印种群信息
    118. void show() {
    119. for (int i = 0; i < GROUP_NUM; i++) {
    120. for (int j = 0; j <= CITY_NUM; j++) {
    121. if (j == CITY_NUM) {
    122. cout << group[i].seq[j];
    123. }
    124. else {
    125. cout << group[i].seq[j] << "->";
    126. }
    127. }
    128. cout << setprecision(4) << " 适应度为:" << group[i].fitness << " 生存概率为:" << group[i].Ps << endl;
    129. }
    130. }
    131. //选择
    132. void select() {
    133. //计算累计概率
    134. vector<double> acc_p(GROUP_NUM);//累计概率,例如原概率0.1 0.3 0.3 0.3,累计概率为0.1 0.4 0.7 1.0
    135. acc_p[0] = group[0].Ps; //其含义为,越优的路径,越被排在vector后面,这个路线被选择到的概率越大
    136. for (int i = 1; i < GROUP_NUM; i++) {
    137. acc_p[i] = acc_p[i - 1] + group[i].Ps;
    138. }
    139. //记录被选择的个体,利用赌轮选择法,随机生成0~1之间一个数,根据计算出来的累计概率选择个体
    140. vector sel_individual(GROUP_NUM);
    141. srand((unsigned)time(NULL));
    142. for (int i = 0; i < GROUP_NUM; i++) {
    143. //生成0~1的随机数,4位小数
    144. float random = rand() % (10000) / (float)(10000);
    145. //cout << random << " ";
    146. for (int j = 0; j < acc_p.size(); j++) { //有可能好几条相同的路径被选中
    147. if (random <= acc_p[j]) {
    148. //cout << random << " " << acc_p[j] << endl;
    149. sel_individual[i] = group[j];//被选择的路径越好,被选中的概率越大,好路径被选择,差路径被淘汰
    150. break;
    151. }
    152. }
    153. }
    154. //被选择的种群覆盖初始种群
    155. for (int i = 0; i < GROUP_NUM; i++) {
    156. group[i] = sel_individual[i];
    157. }
    158. /*cout << "打印经过自然选择后的种群序列:" << endl;
    159. for(int i = 0; i < GROUP_NUM; i++) {
    160. cout << i << "、" << " ";
    161. for(int j = 0; j < CITY_NUM; j++) {
    162. cout << group[i].seq[j] << " ";
    163. }
    164. cout << "适应度为:" << group[i].fitness << " 生存概率为:" << group[i].Ps << endl;
    165. }*/
    166. }
    167. //交叉(交配)算法
    168. //第k(k=0、2、4、...、2n)个个体和k+1个个体有一定的概率交叉变换
    169. //设置一个0~1之间的随机数,若在Pc(交配率)范围内,则该该个体k与下一个个体k+1进行交配
    170. void mating() {
    171. //随机生成子代交配时DNA交换的数量(1~CITY_NUM / 2)
    172. srand((unsigned)time(NULL));
    173. int change_num = (rand() % CITY_NUM / 2) + 1; //0~14之间的交换数字
    174. //cout << "交换DNA数量:" << change_num << endl;
    175. //开始交配
    176. for (int i = 0; i < CITY_NUM; i += 2) {
    177. //生成0-1之间的随机数(3位小数)
    178. float random = rand() % (1000) / (float)(1000);
    179. //在交配率以内,则该个体i与下一个个体i+1进行交配
    180. if (random < Pc) {//0.9的交叉概率
    181. //随机生成交配点
    182. int point = rand() % (CITY_NUM - change_num);
    183. //cout << i << " 与 " << i + 1 << " 进行交配,断点:" << point << endl;
    184. //先将双亲的交配片段进行互换,并用哈希映射记录,然后解决基因冲突
    185. unordered_map<int, int> hash1;
    186. for (int j = point; j < change_num + point; j++) {
    187. int a = group[i].seq[j];//i的点
    188. int b = group[i + 1].seq[j];//i+1的点
    189. if (hash1.find(a) != hash1.end()) {
    190. a = hash1[a];//为了解决下面的重复哈希映射,只保留一个
    191. }
    192. if (hash1.find(b) != hash1.end()) {
    193. b = hash1[b];
    194. }
    195. hash1[a] = b;//a对应b
    196. hash1[b] = a;//b对应a
    197. swap(group[i].seq[j], group[i + 1].seq[j]);//交换第i和i+1个路径中a~b的点
    198. }
    199. //处理双亲交配后可能产生的基因冲突问题(断点前)
    200. for (int j = 0; j < point; j++) {
    201. if (hash1.find(group[i].seq[j]) != hash1.end()) {
    202. group[i].seq[j] = hash1[group[i].seq[j]];
    203. }
    204. if (hash1.find(group[i + 1].seq[j]) != hash1.end()) {
    205. group[i + 1].seq[j] = hash1[group[i + 1].seq[j]];
    206. }
    207. }
    208. //断点后
    209. for (int j = point + change_num; j < CITY_NUM; j++) {
    210. if (hash1.find(group[i].seq[j]) != hash1.end()) {
    211. group[i].seq[j] = hash1[group[i].seq[j]];
    212. }
    213. if (hash1.find(group[i + 1].seq[j]) != hash1.end()) {
    214. group[i + 1].seq[j] = hash1[group[i + 1].seq[j]];
    215. }
    216. }
    217. }
    218. //最后一个城市的下一个城市是第一个城市
    219. group[i].seq[CITY_NUM] = group[i].seq[0];
    220. }
    221. /*
    222. //打印交配过后的种群
    223. for(int i = 0; i < GROUP_NUM; i++) {
    224. cout << i << "、" << " ";
    225. for(int j = 0; j < CITY_NUM; j++) {
    226. cout << group[i].seq[j] << " ";
    227. }
    228. //cout << "适应度为:" << group[i].fitness << " 生存概率为:" << group[i].Ps << endl;
    229. cout << endl;
    230. }*/
    231. }
    232. //变异算法
    233. //每个算子有一定概率(变异概率)基因多次对换。
    234. //对每个个体,若满足变异概率,则随机生成两个不相等的范围在[0,城市数 - 1]之间的随机整数。将该个体在这两个随机整数对应的位置的城市编号对换
    235. //进行上述n次对换,n是一个[1,城市数]之间的随机整数
    236. void mutate() {
    237. srand((unsigned)time(NULL));
    238. for (int i = 0; i < GROUP_NUM; i++) {
    239. //生成0-1之间的随机数(4位小数)
    240. float random = rand() % (10000) / (float)(10000);
    241. //cout << random << " ";
    242. if (random < Pm) {//0.1
    243. //cout << i << " 号个体产生变异" << endl;
    244. //随机生成基因对换次数
    245. int exchange_times = rand() % CITY_NUM + 1;
    246. while (exchange_times > 0) {
    247. //随机生成两个不相等的范围在[0,城市数 - 1]之间的随机数
    248. int a = rand() % CITY_NUM;
    249. int b = rand() % CITY_NUM;
    250. swap(group[i].seq[a], group[i].seq[b]);//随机变异
    251. exchange_times--;
    252. }
    253. }
    254. //最后一个城市的下一个城市是第一个城市
    255. group[i].seq[CITY_NUM] = group[i].seq[0];
    256. }
    257. /*cout << endl << "打印变异过后的种群" << endl;
    258. for(int i = 0; i < GROUP_NUM; i++) {
    259. cout << i << "、" << " ";
    260. for(int j = 0; j < CITY_NUM; j++) {
    261. cout << group[i].seq[j] << " ";
    262. }
    263. //cout << "适应度为:" << group[i].fitness << " 生存概率为:" << group[i].Ps << endl;
    264. cout << endl;
    265. }*/
    266. }
    267. int main()
    268. {
    269. int it = 0; //迭代次数
    270. //随机生成初始城市坐标
    271. city_init();
    272. //随机生成初始种群(100条随机路线)
    273. group_init();
    274. //计算每个路径的代价及被选择的概率
    275. cal_group();
    276. //对路径进行排序,代价小的排在后面
    277. sort(group.begin(), group.end(), my_cmp);
    278. //show();//打印100个种群信息
    279. cout << endl;
    280. cout << "初代“最优”路线为:";
    281. for (int i = 0; i < CITY_NUM + 1; i++) {
    282. cout << group[GROUP_NUM - 1].seq[i] << " ";
    283. } cout << "适应度为:" << group[GROUP_NUM - 1].fitness << endl;
    284. cout << "该路线长度为:" << group[GROUP_NUM - 1].dis << endl;
    285. cout << "该路线对应的坐标点分别为:" << endl;
    286. for (int i = 0; i < CITY_NUM + 1; i++) {
    287. int t = group[GROUP_NUM - 1].seq[i];
    288. if (i == CITY_NUM) {
    289. cout << '(' << city[t].x << ", " << city[t].y << ')' << endl;
    290. }
    291. else {
    292. cout << '(' << city[t].x << ", " << city[t].y << ')' << "->";
    293. }
    294. }
    295. while (it <= ITERATION_NUM) {//迭代1000次
    296. //计算适应度以及生存概率
    297. cal_group();
    298. //在种群中选择个体
    299. select();
    300. //种群进行交配
    301. mating();
    302. //种群中的个体产生变异
    303. mutate();
    304. it++;
    305. } cout << endl;
    306. //代价最小的排在最后面
    307. sort(group.begin(), group.end(), my_cmp);
    308. //show();//打印种群信息
    309. //cal_group();
    310. cout << "经过" << ITERATION_NUM << "次迭代后:" << endl;
    311. cout << "“最优”路线为:";
    312. for (int i = 0; i < CITY_NUM + 1; i++) {
    313. cout << group[GROUP_NUM - 1].seq[i] << " ";
    314. } cout << "适应度为:" << group[GROUP_NUM - 1].fitness << endl;
    315. cout << "该路线长度为:" << group[GROUP_NUM - 1].dis << endl;
    316. cout << "该路线对应的坐标点分别为:" << endl;
    317. for (int i = 0; i < CITY_NUM + 1; i++) {
    318. int t = group[GROUP_NUM - 1].seq[i];
    319. //cout << '(' << city[t].x << ", " << city[t].y << ')' << endl;
    320. if (i == CITY_NUM) {
    321. cout << '(' << city[t].x << ", " << city[t].y << ')' << endl;
    322. }
    323. else {
    324. cout << '(' << city[t].x << ", " << city[t].y << ')' << "->";
    325. }
    326. }
    327. return 0;
    328. }

  • 相关阅读:
    react的1.函数组件2.usestate3.useeffect4.ref5.fragment6.contex 代码加注释
    Vue学习—vuex
    Macronix MX25L25645G NOR Flash无法擦除问题分析
    Window10安装Docker
    乘风破浪,遇见未来新能源汽车(Electric Vehicle)之特斯拉提车必须知道的十个流程
    C#正则表达式总结
    15.代理模式
    LeetCode知识点总结 - 655
    修复 爱普生 EPSON L4156 打印机 无法打印,开关 WIFI 墨水 三个灯同时闪烁的问题
    代码随想录训练营第42天|416.分割等和子集
  • 原文地址:https://blog.csdn.net/ljjjjjjjjjjj/article/details/132861403