|
| 1 | +# 遗传算法——旅行商问题 |
| 2 | +## 问题定义 |
| 3 | +旅行商问题的目标是找到一条经过所有给定城市且每个城市仅访问一次的最短路径,并返回起点。 |
| 4 | + |
| 5 | +## 核心代码 |
| 6 | + |
| 7 | +```dart |
| 8 | +/// [cityNumbers] : 这个参数指的是基因的长度,通常用来表示问题的解或个体的编码长度。在旅行商问题中,[cityNumbers]可以表示城市的数量,即有多少个基因就表示有多少个城市。 |
| 9 | +/// [popNumbers] : 这个参数表示种群中个体的数量,即在每一代中会有多少个解同时存在。种群中的每个个体都是一种可能的解决方案。 |
| 10 | +/// [genNumbers] : 这是遗传算法中的代数,表示算法将运行多少代来寻找最优解。每一代代表一轮进化,通过选择、交叉和变异操作来更新种群中的个体。 |
| 11 | +/// [mutateProb] : 变异概率,表示在每次个体进行变异操作时,每个基因发生变异的概率。变异是为了保持种群的多样性,有助于跳出局部最优解。 |
| 12 | +
|
| 13 | +import 'dart:math'; |
| 14 | +// 随机生成路径邻接矩阵的方法 |
| 15 | +List<List<int>> generateDistanceMatrix(int size) { |
| 16 | + List<List<int>> matrix = List.generate(size, (_) => List.filled(size, 0)); |
| 17 | + Random rand = Random(); |
| 18 | +
|
| 19 | + for (int i = 0; i < size; i++) { |
| 20 | + for (int j = i + 1; j < size; j++) { |
| 21 | + int distance = rand.nextInt(20) + 1; // 距离为1到20之间的随机数 |
| 22 | + matrix[i][j] = distance; |
| 23 | + matrix[j][i] = distance; |
| 24 | + } |
| 25 | + } |
| 26 | + return matrix; |
| 27 | +} |
| 28 | +
|
| 29 | +// 生成邻接矩阵的markdown代码 |
| 30 | +String generateMarkdownTable(List<List<int>> matrix) { |
| 31 | + int size = matrix.length; |
| 32 | + StringBuffer buffer = StringBuffer(); |
| 33 | +
|
| 34 | + // Generate header |
| 35 | + buffer.write('| |'); |
| 36 | + for (int i = 0; i < size; i++) { |
| 37 | + buffer.write(' ${String.fromCharCode(65 + i)} |'); |
| 38 | + } |
| 39 | + buffer.write('\n|---|'); |
| 40 | + for (int i = 0; i < size; i++) { |
| 41 | + buffer.write('----|'); |
| 42 | + } |
| 43 | + buffer.write('\n'); |
| 44 | +
|
| 45 | + // Generate rows |
| 46 | + for (int i = 0; i < size; i++) { |
| 47 | + buffer.write('| ${String.fromCharCode(65 + i)} |'); |
| 48 | + for (int j = 0; j < size; j++) { |
| 49 | + buffer.write(' ${matrix[i][j]} |'); |
| 50 | + } |
| 51 | + buffer.write('\n'); |
| 52 | + } |
| 53 | +
|
| 54 | + return buffer.toString(); |
| 55 | +} |
| 56 | +
|
| 57 | +const cityNumbers = 5; |
| 58 | +const popNumbers = 60; |
| 59 | +const genNumbers = 50; |
| 60 | +const mutateProb = .25; |
| 61 | +
|
| 62 | +// 模拟城市道路——基于邻接矩阵 |
| 63 | +List<List<int>> distanceMatrix = [ |
| 64 | + [0, 2, 9, 10, 7], |
| 65 | + [2, 0, 6, 4, 3], |
| 66 | + [9, 6, 0, 8, 5], |
| 67 | + [10, 4, 8, 0, 3], |
| 68 | + [7, 3, 5, 3, 0], |
| 69 | +]; |
| 70 | +
|
| 71 | +class Tour { |
| 72 | + late List<int> cities; |
| 73 | + late int cityNumbers; |
| 74 | + late int popNumbers; |
| 75 | + late int genNumbers; |
| 76 | + late double mutateProb; |
| 77 | + late List<List<int>> distanceMatrix; |
| 78 | +
|
| 79 | + // 传入表示城市的索引即可——比如:A~E五个城市可以用0~4五个数表示 |
| 80 | + // 同时索引也可以很好的表示对每个城市的编码,索引对换位置,相当于城市先后顺序变了 |
| 81 | + Tour({ |
| 82 | + required this.distanceMatrix, |
| 83 | + required this.cities, |
| 84 | + required this.cityNumbers, |
| 85 | + required this.popNumbers, |
| 86 | + required this.genNumbers, |
| 87 | + required this.mutateProb |
| 88 | + }); |
| 89 | +
|
| 90 | + // 计算行程总距离 |
| 91 | + int calculateDistance() { |
| 92 | + int totalDistance = 0; |
| 93 | + for (int i = 0; i < cityNumbers - 1; i++) { |
| 94 | + totalDistance += distanceMatrix[cities[i]][cities[i + 1]]; |
| 95 | + } |
| 96 | + totalDistance += distanceMatrix[cities.last][cities.first]; |
| 97 | + print("totalDistance : $totalDistance"); |
| 98 | + return totalDistance; |
| 99 | + } |
| 100 | +
|
| 101 | + // 变异——交换变异 |
| 102 | + void mutate() { |
| 103 | + // 随机生成一个0~1的数,表示概率,低于变异概率即发生变异 |
| 104 | + if (Random().nextDouble() < mutateProb) { |
| 105 | + // 介于0(包含)和 cityNumbers(不包含)之间的随机整数 |
| 106 | + int index1 = Random().nextInt(cityNumbers); |
| 107 | + int index2 = Random().nextInt(cityNumbers); |
| 108 | + int temp = cities[index1]; |
| 109 | + cities[index1] = cities[index2]; |
| 110 | + cities[index2] = temp; |
| 111 | + } |
| 112 | + } |
| 113 | +} |
| 114 | +
|
| 115 | +class GeneticAlgorithm { |
| 116 | +
|
| 117 | + late List<int> cities; |
| 118 | + late int cityNumbers; |
| 119 | + late int popNumbers; |
| 120 | + late int genNumbers; |
| 121 | + late double mutateProb; |
| 122 | + late List<List<int>> distanceMatrix; |
| 123 | + // 种群 |
| 124 | + List<Tour> population = []; |
| 125 | +
|
| 126 | + GeneticAlgorithm({ |
| 127 | + required this.distanceMatrix, |
| 128 | + required this.cities, |
| 129 | + required this.cityNumbers, |
| 130 | + required this.popNumbers, |
| 131 | + required this.genNumbers, |
| 132 | + required this.mutateProb |
| 133 | + }){ |
| 134 | + // 创建并且初始化随机种群 |
| 135 | + for (int i = 0; i < popNumbers; i++) { |
| 136 | + List<int> randomTour = List.generate(cityNumbers, (int index) => index); |
| 137 | + randomTour.shuffle(); |
| 138 | + population.add( |
| 139 | + Tour( |
| 140 | + distanceMatrix: distanceMatrix, |
| 141 | + cities: cities, |
| 142 | + popNumbers: popNumbers, |
| 143 | + genNumbers: genNumbers, |
| 144 | + mutateProb: mutateProb, |
| 145 | + cityNumbers: cityNumbers |
| 146 | + ) |
| 147 | + ); |
| 148 | + } |
| 149 | + } |
| 150 | +
|
| 151 | +
|
| 152 | + // 基于锦标赛方案筛选 |
| 153 | + // 选的指标是总路程 |
| 154 | + List<Tour> selection() { |
| 155 | + List<Tour> selectedParents = []; |
| 156 | + for (int i = 0; i < popNumbers; i++) { |
| 157 | + int index1 = Random().nextInt(popNumbers); |
| 158 | + int index2 = Random().nextInt(popNumbers); |
| 159 | + Tour parent1 = population[index1]; |
| 160 | + Tour parent2 = population[index2]; |
| 161 | + selectedParents.add(parent1.calculateDistance() < parent2.calculateDistance() ? parent1 : parent2); |
| 162 | + } |
| 163 | + return selectedParents; |
| 164 | + } |
| 165 | +
|
| 166 | + // 交叉 |
| 167 | + List<Tour> crossover(List<Tour> parents) { |
| 168 | + List<Tour> offspring = []; |
| 169 | + for (int i = 0; i < parents.length; i += 2) { |
| 170 | + Tour parent1 = parents[i]; |
| 171 | + Tour parent2 = parents[i + 1]; |
| 172 | +
|
| 173 | + // 选段 |
| 174 | + List<int> child1 = List.filled(cityNumbers, -1); |
| 175 | + List<int> child2 = List.filled(cityNumbers, -1); |
| 176 | +
|
| 177 | + int startPos = Random().nextInt(cityNumbers); |
| 178 | + int endPos = Random().nextInt(cityNumbers - startPos) + startPos; |
| 179 | +
|
| 180 | + for (int j = startPos; j <= endPos; j++) { |
| 181 | + child1[j] = parent1.cities[j]; |
| 182 | + child2[j] = parent2.cities[j]; |
| 183 | + } |
| 184 | + // 确保交叉区间外的基因不重复,调整重复基因的位置。 |
| 185 | + for (int j = 0; j < cityNumbers; j++) { |
| 186 | + if (!child1.contains(parent2.cities[j])) { |
| 187 | + for (int k = 0; k < cityNumbers; k++) { |
| 188 | + if (child1[k] == -1) { |
| 189 | + child1[k] = parent2.cities[j]; |
| 190 | + break; |
| 191 | + } |
| 192 | + } |
| 193 | + } |
| 194 | + if (!child2.contains(parent1.cities[j])) { |
| 195 | + for (int k = 0; k < cityNumbers; k++) { |
| 196 | + if (child2[k] == -1) { |
| 197 | + child2[k] = parent1.cities[j]; |
| 198 | + break; |
| 199 | + } |
| 200 | + } |
| 201 | + } |
| 202 | + } |
| 203 | +
|
| 204 | + offspring.add(Tour( |
| 205 | + distanceMatrix: distanceMatrix, |
| 206 | + cities: child1, |
| 207 | + popNumbers: popNumbers, |
| 208 | + genNumbers: genNumbers, |
| 209 | + mutateProb: mutateProb, |
| 210 | + cityNumbers: cityNumbers |
| 211 | + )); |
| 212 | + offspring.add(Tour( |
| 213 | + distanceMatrix: distanceMatrix, |
| 214 | + cities: child2, |
| 215 | + popNumbers: popNumbers, |
| 216 | + genNumbers: genNumbers, |
| 217 | + mutateProb: mutateProb, |
| 218 | + cityNumbers: cityNumbers |
| 219 | + )); |
| 220 | + } |
| 221 | + return offspring; |
| 222 | + } |
| 223 | +
|
| 224 | + // 迭代进化 |
| 225 | + void evolve() { |
| 226 | + for (int generation = 0; generation < genNumbers; generation++) { |
| 227 | + List<Tour> parents = selection(); |
| 228 | + List<Tour> offspring = crossover(parents); |
| 229 | +
|
| 230 | + for (Tour tour in offspring) { |
| 231 | + tour.mutate(); |
| 232 | + } |
| 233 | +
|
| 234 | + // 适者生存 |
| 235 | + population = List.from(offspring); |
| 236 | +
|
| 237 | + // 每代结束后输出当前最佳路径和距离 |
| 238 | + Tour bestTour = population.reduce((a, b) => a.calculateDistance() < b.calculateDistance() ? a : b); |
| 239 | + int bestDistance = bestTour.calculateDistance(); |
| 240 | + print("Generation $generation: Best tour: ${bestTour.cities}, Distance: $bestDistance"); |
| 241 | + } |
| 242 | +
|
| 243 | + // 最终输出最优解 |
| 244 | + Tour bestTour = population.reduce((a, b) => a.calculateDistance() < b.calculateDistance() ? a : b); |
| 245 | + int bestDistance = bestTour.calculateDistance(); |
| 246 | + print("Final best tour: ${bestTour.cities}, Distance: $bestDistance"); |
| 247 | + } |
| 248 | +} |
| 249 | +
|
| 250 | +void main() { |
| 251 | + GeneticAlgorithm ga = GeneticAlgorithm( |
| 252 | + distanceMatrix: distanceMatrix, |
| 253 | + cities: List.generate(cityNumbers, (e) => e), |
| 254 | + cityNumbers: cityNumbers, |
| 255 | + popNumbers: popNumbers, |
| 256 | + mutateProb: mutateProb, |
| 257 | + genNumbers: genNumbers |
| 258 | + ); |
| 259 | + ga.evolve(); |
| 260 | +} |
| 261 | +
|
| 262 | +``` |
0 commit comments