|
| 1 | +package GA; |
| 2 | + |
| 3 | +import java.util.ArrayList; |
| 4 | +import java.util.Random; |
| 5 | + |
| 6 | +/** |
| 7 | + * 遗传算法工具类 |
| 8 | + * |
| 9 | + * @author lyq |
| 10 | + * |
| 11 | + */ |
| 12 | +public class GATool { |
| 13 | + // 变量最小值 |
| 14 | + private int minNum; |
| 15 | + // 变量最大值 |
| 16 | + private int maxNum; |
| 17 | + // 单个变量的编码位数 |
| 18 | + private int codeNum; |
| 19 | + // 初始种群的数量 |
| 20 | + private int initSetsNum; |
| 21 | + // 随机数生成器 |
| 22 | + private Random random; |
| 23 | + // 初始群体 |
| 24 | + private ArrayList<int[]> initSets; |
| 25 | + |
| 26 | + public GATool(int minNum, int maxNum, int initSetsNum) { |
| 27 | + this.minNum = minNum; |
| 28 | + this.maxNum = maxNum; |
| 29 | + this.initSetsNum = initSetsNum; |
| 30 | + |
| 31 | + this.random = new Random(); |
| 32 | + produceInitSets(); |
| 33 | + } |
| 34 | + |
| 35 | + /** |
| 36 | + * 产生初始化群体 |
| 37 | + */ |
| 38 | + private void produceInitSets() { |
| 39 | + this.codeNum = 0; |
| 40 | + int num = maxNum; |
| 41 | + int[] array; |
| 42 | + |
| 43 | + initSets = new ArrayList<>(); |
| 44 | + |
| 45 | + // 确定编码位数 |
| 46 | + while (num != 0) { |
| 47 | + codeNum++; |
| 48 | + num /= 2; |
| 49 | + } |
| 50 | + |
| 51 | + for (int i = 0; i < initSetsNum; i++) { |
| 52 | + array = produceInitCode(); |
| 53 | + initSets.add(array); |
| 54 | + } |
| 55 | + } |
| 56 | + |
| 57 | + /** |
| 58 | + * 产生初始个体的编码 |
| 59 | + * |
| 60 | + * @return |
| 61 | + */ |
| 62 | + private int[] produceInitCode() { |
| 63 | + int num = 0; |
| 64 | + int num2 = 0; |
| 65 | + int[] tempArray; |
| 66 | + int[] array1; |
| 67 | + int[] array2; |
| 68 | + |
| 69 | + tempArray = new int[2 * codeNum]; |
| 70 | + array1 = new int[codeNum]; |
| 71 | + array2 = new int[codeNum]; |
| 72 | + |
| 73 | + num = 0; |
| 74 | + while (num < minNum || num > maxNum) { |
| 75 | + num = random.nextInt(maxNum) + 1; |
| 76 | + } |
| 77 | + numToBinaryArray(array1, num); |
| 78 | + |
| 79 | + while (num2 < minNum || num2 > maxNum) { |
| 80 | + num2 = random.nextInt(maxNum) + 1; |
| 81 | + } |
| 82 | + numToBinaryArray(array2, num2); |
| 83 | + |
| 84 | + // 组成总的编码 |
| 85 | + for (int i = 0, k = 0; i < tempArray.length; i++, k++) { |
| 86 | + if (k < codeNum) { |
| 87 | + tempArray[i] = array1[k]; |
| 88 | + } else { |
| 89 | + tempArray[i] = array2[k - codeNum]; |
| 90 | + } |
| 91 | + } |
| 92 | + |
| 93 | + return tempArray; |
| 94 | + } |
| 95 | + |
| 96 | + /** |
| 97 | + * 选择操作,把适值较高的个体优先遗传到下一代 |
| 98 | + * |
| 99 | + * @param initCodes |
| 100 | + * 初始个体编码 |
| 101 | + * @return |
| 102 | + */ |
| 103 | + private ArrayList<int[]> selectOperate(ArrayList<int[]> initCodes) { |
| 104 | + double randomNum = 0; |
| 105 | + double sumAdaptiveValue = 0; |
| 106 | + ArrayList<int[]> resultCodes = new ArrayList<>(); |
| 107 | + double[] adaptiveValue = new double[initSetsNum]; |
| 108 | + |
| 109 | + for (int i = 0; i < initSetsNum; i++) { |
| 110 | + adaptiveValue[i] = calCodeAdaptiveValue(initCodes.get(i)); |
| 111 | + sumAdaptiveValue += adaptiveValue[i]; |
| 112 | + } |
| 113 | + |
| 114 | + // 转成概率的形式,做归一化操作 |
| 115 | + for (int i = 0; i < initSetsNum; i++) { |
| 116 | + adaptiveValue[i] = adaptiveValue[i] / sumAdaptiveValue; |
| 117 | + } |
| 118 | + |
| 119 | + for (int i = 0; i < initSetsNum; i++) { |
| 120 | + randomNum = random.nextInt(100) + 1; |
| 121 | + randomNum = randomNum / 100; |
| 122 | + |
| 123 | + sumAdaptiveValue = 0; |
| 124 | + // 确定区间 |
| 125 | + for (int j = 0; j < initSetsNum; j++) { |
| 126 | + if (randomNum > sumAdaptiveValue |
| 127 | + && randomNum <= sumAdaptiveValue + adaptiveValue[j]) { |
| 128 | + //采用拷贝的方式避免引用重复 |
| 129 | + resultCodes.add(initCodes.get(j).clone()); |
| 130 | + break; |
| 131 | + } else { |
| 132 | + sumAdaptiveValue += adaptiveValue[j]; |
| 133 | + } |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | + return resultCodes; |
| 138 | + } |
| 139 | + |
| 140 | + /** |
| 141 | + * 交叉运算 |
| 142 | + * |
| 143 | + * @param selectedCodes |
| 144 | + * 上步骤的选择后的编码 |
| 145 | + * @return |
| 146 | + */ |
| 147 | + private ArrayList<int[]> crossOperate(ArrayList<int[]> selectedCodes) { |
| 148 | + int randomNum = 0; |
| 149 | + // 交叉点 |
| 150 | + int crossPoint = 0; |
| 151 | + ArrayList<int[]> resultCodes = new ArrayList<>(); |
| 152 | + // 随机编码队列,进行随机交叉配对 |
| 153 | + ArrayList<int[]> randomCodeSeqs = new ArrayList<>(); |
| 154 | + |
| 155 | + // 进行随机排序 |
| 156 | + while (selectedCodes.size() > 0) { |
| 157 | + randomNum = random.nextInt(selectedCodes.size()); |
| 158 | + |
| 159 | + randomCodeSeqs.add(selectedCodes.get(randomNum)); |
| 160 | + selectedCodes.remove(randomNum); |
| 161 | + } |
| 162 | + |
| 163 | + int temp = 0; |
| 164 | + int[] array1; |
| 165 | + int[] array2; |
| 166 | + // 进行两两交叉运算 |
| 167 | + for (int i = 1; i < randomCodeSeqs.size(); i++) { |
| 168 | + if (i % 2 == 1) { |
| 169 | + array1 = randomCodeSeqs.get(i - 1); |
| 170 | + array2 = randomCodeSeqs.get(i); |
| 171 | + crossPoint = random.nextInt(2 * codeNum - 1) + 1; |
| 172 | + |
| 173 | + // 进行交叉点位置后的编码调换 |
| 174 | + for (int j = 0; j < 2 * codeNum; j++) { |
| 175 | + if (j >= crossPoint) { |
| 176 | + temp = array1[j]; |
| 177 | + array1[j] = array2[j]; |
| 178 | + array2[j] = temp; |
| 179 | + } |
| 180 | + } |
| 181 | + |
| 182 | + // 加入到交叉运算结果中 |
| 183 | + resultCodes.add(array1); |
| 184 | + resultCodes.add(array2); |
| 185 | + } |
| 186 | + } |
| 187 | + |
| 188 | + return resultCodes; |
| 189 | + } |
| 190 | + |
| 191 | + /** |
| 192 | + * 变异操作 |
| 193 | + * |
| 194 | + * @param crossCodes |
| 195 | + * 交叉运算后的结果 |
| 196 | + * @return |
| 197 | + */ |
| 198 | + private ArrayList<int[]> variationOperate(ArrayList<int[]> crossCodes) { |
| 199 | + // 变异点 |
| 200 | + int variationPoint = 0; |
| 201 | + ArrayList<int[]> resultCodes = new ArrayList<>(); |
| 202 | + |
| 203 | + for (int[] array : crossCodes) { |
| 204 | + variationPoint = random.nextInt(codeNum * 2); |
| 205 | + |
| 206 | + for (int i = 0; i < array.length; i++) { |
| 207 | + // 变异点进行变异 |
| 208 | + if (i == variationPoint) { |
| 209 | + array[i] = (array[i] == 0 ? 1 : 0); |
| 210 | + break; |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + resultCodes.add(array); |
| 215 | + } |
| 216 | + |
| 217 | + return resultCodes; |
| 218 | + } |
| 219 | + |
| 220 | + /** |
| 221 | + * 数字转为二进制形式 |
| 222 | + * |
| 223 | + * @param binaryArray |
| 224 | + * 转化后的二进制数组形式 |
| 225 | + * @param num |
| 226 | + * 待转化数字 |
| 227 | + */ |
| 228 | + private void numToBinaryArray(int[] binaryArray, int num) { |
| 229 | + int index = 0; |
| 230 | + int temp = 0; |
| 231 | + while (num != 0) { |
| 232 | + binaryArray[index] = num % 2; |
| 233 | + index++; |
| 234 | + num /= 2; |
| 235 | + } |
| 236 | + |
| 237 | + //进行数组前和尾部的调换 |
| 238 | + for(int i=0; i<binaryArray.length/2; i++){ |
| 239 | + temp = binaryArray[i]; |
| 240 | + binaryArray[i] = binaryArray[binaryArray.length - 1 - i]; |
| 241 | + binaryArray[binaryArray.length - 1 - i] = temp; |
| 242 | + } |
| 243 | + } |
| 244 | + |
| 245 | + /** |
| 246 | + * 二进制数组转化为数字 |
| 247 | + * |
| 248 | + * @param binaryArray |
| 249 | + * 待转化二进制数组 |
| 250 | + */ |
| 251 | + private int binaryArrayToNum(int[] binaryArray) { |
| 252 | + int result = 0; |
| 253 | + |
| 254 | + for (int i = binaryArray.length-1, k=0; i >=0 ; i--, k++) { |
| 255 | + if (binaryArray[i] == 1) { |
| 256 | + result += Math.pow(2, k); |
| 257 | + } |
| 258 | + } |
| 259 | + |
| 260 | + return result; |
| 261 | + } |
| 262 | + |
| 263 | + /** |
| 264 | + * 计算个体编码的适值 |
| 265 | + * |
| 266 | + * @param codeArray |
| 267 | + */ |
| 268 | + private int calCodeAdaptiveValue(int[] codeArray) { |
| 269 | + int result = 0; |
| 270 | + int x1 = 0; |
| 271 | + int x2 = 0; |
| 272 | + int[] array1 = new int[codeNum]; |
| 273 | + int[] array2 = new int[codeNum]; |
| 274 | + |
| 275 | + for (int i = 0, k = 0; i < codeArray.length; i++, k++) { |
| 276 | + if (k < codeNum) { |
| 277 | + array1[k] = codeArray[i]; |
| 278 | + } else { |
| 279 | + array2[k - codeNum] = codeArray[i]; |
| 280 | + } |
| 281 | + } |
| 282 | + |
| 283 | + // 进行适值的叠加 |
| 284 | + x1 = binaryArrayToNum(array1); |
| 285 | + x2 = binaryArrayToNum(array2); |
| 286 | + result = x1 * x1 + x2 * x2; |
| 287 | + |
| 288 | + return result; |
| 289 | + } |
| 290 | + |
| 291 | + /** |
| 292 | + * 进行遗传算法计算 |
| 293 | + */ |
| 294 | + public void geneticCal() { |
| 295 | + // 最大适值 |
| 296 | + int maxFitness; |
| 297 | + //迭代遗传次数 |
| 298 | + int loopCount = 0; |
| 299 | + boolean canExit = false; |
| 300 | + ArrayList<int[]> initCodes; |
| 301 | + ArrayList<int[]> selectedCodes; |
| 302 | + ArrayList<int[]> crossedCodes; |
| 303 | + ArrayList<int[]> variationCodes; |
| 304 | + |
| 305 | + int[] maxCode = new int[2*codeNum]; |
| 306 | + //计算最大适值 |
| 307 | + for(int i=0; i<2*codeNum; i++){ |
| 308 | + maxCode[i] = 1; |
| 309 | + } |
| 310 | + maxFitness = calCodeAdaptiveValue(maxCode); |
| 311 | + |
| 312 | + initCodes = initSets; |
| 313 | + while (true) { |
| 314 | + for (int[] array : initCodes) { |
| 315 | + // 遗传迭代的终止条件为存在编码达到最大适值 |
| 316 | + if (maxFitness == calCodeAdaptiveValue(array)) { |
| 317 | + canExit = true; |
| 318 | + break; |
| 319 | + } |
| 320 | + } |
| 321 | + |
| 322 | + if (canExit) { |
| 323 | + break; |
| 324 | + } |
| 325 | + |
| 326 | + selectedCodes = selectOperate(initCodes); |
| 327 | + crossedCodes = crossOperate(selectedCodes); |
| 328 | + variationCodes = variationOperate(crossedCodes); |
| 329 | + initCodes = variationCodes; |
| 330 | + |
| 331 | + loopCount++; |
| 332 | + } |
| 333 | + |
| 334 | + System.out.println("总共遗传进化了" + loopCount +"次" ); |
| 335 | + printFinalCodes(initCodes); |
| 336 | + } |
| 337 | + |
| 338 | + /** |
| 339 | + * 输出最后的编码集 |
| 340 | + * |
| 341 | + * @param finalCodes |
| 342 | + * 最后的结果编码 |
| 343 | + */ |
| 344 | + private void printFinalCodes(ArrayList<int[]> finalCodes) { |
| 345 | + int j = 0; |
| 346 | + |
| 347 | + for (int[] array : finalCodes) { |
| 348 | + System.out.print("个体" + (j + 1) + ":"); |
| 349 | + for (int i = 0; i < array.length; i++) { |
| 350 | + System.out.print(array[i]); |
| 351 | + } |
| 352 | + System.out.println(); |
| 353 | + j++; |
| 354 | + } |
| 355 | + } |
| 356 | + |
| 357 | +} |
0 commit comments