八皇后問題遺傳演算法實現(C語言)
八皇后問題的遺傳演算法實現過程詳解
1、八皇后問題描述
19 世紀著名的數學家Gauss 在1850 年提出八皇后問題後, 該問題成為各類語言程式設計的經典題目。八皇后問題要求在8×8 格的國際象棋上擺放八個皇后,使橫、豎、斜方向上都不能有兩個及兩個以上皇后在同一條直線上, 問題也可以推廣到N 個皇后。窮舉法在問題規模不大的情況下還可適用,回溯法是求解此問題的經典演算法。但N 皇后問題是個NP 難問題, 隨著皇后數目的增多, 求解複雜度激增, 就需利用非常規的技術求
解。遺傳演算法在求解一些NP 完全問題上得到了廣泛地應用,本文用遺傳演算法求解八皇后問題,給出詳細的實現過程。
2、基本遺傳演算法求解過程
基本遺傳以初始種群為基點, 經過選擇、交叉、變異操作生成新的種群,如此更新種群直到滿足終止條件。其計算步驟如下:
(1) 將問題空間轉換為遺傳空間, 也就是編
碼;
(2)隨機生成P 個染色體作為初始種群;
(3)染色體評價,也就是按確定的適應度函式
計算各個染色體的適應度;
(4)根據染色體適應度,按選擇運算元進行染色
體的選擇;
(5)按交叉概率進行交叉操作;
(6)按變異概率進行變異操作;
(7)返回(4)形成新的種群,繼續迭代,直到滿足終止條件。
基本遺傳演算法給出了基本框架, 針對求解的問題不同, 遺傳演算法在相應的計算步驟中有不同的設計。本文針對八皇后問題, 設計了相應的編碼,適應度計算方法,交叉和變異操作。
3、用遺傳演算法求解八皇后問題實現過程詳解
3.1 編碼
遺傳演算法中傳統的編碼是二進位制編碼, 本文采用多值編碼。染色體長度取決於皇后的個數。染色體中每個基因所處的位置表示其在棋譜中所在的行數, 基因值表示其所在的列數。如染色體40752613 表示:從0 開始數,第0 個4 表示在第零行的皇后在第4 列, 第1 個0 表示第一行的皇后在第0 列,以此類推。八皇后問題中皇后不能處於同行同列, 意味著染色體中0~7 的基因取值不能出現重複。
3.2 個體評價
染色體通常表示了問題的可行解, 對可行解進行遺傳操作尋找最優解。但在八皇后問題中,染色體僅僅體現同行同列中未出現互攻, 在對角線上是否出現互攻還未做考慮。在對皇后的位置做比較的時候,
好,而互攻次數為越小越好,所以可以將適應度的計算函式設定為:F=1/Value。
3.3 選擇
選擇使用的是經典的賭輪選擇方法, 與基本遺傳演算法的實現無特別之處,此處不贅述。
3.4 交叉
經典的單點, 多點等交叉因染色體中不能出現重複的基因值,在該問題中不適用。本文使用部分匹配交叉,具體操作如下:
1)在染色體中隨機選取兩個點標記為y,
如:染色體a:01y24y3675;
染色體b:12y30y4576;
兩個y 之間的基因段稱為中間段, 記錄其對應關係2-3,4-0;
2)對染色體a,b 的中間段進行交換,
形成染色體a':01y30y3675;染色體b': 12y24y4576;
3)利用對應關係,對染色體a', b' 中間段外的基因進行交換,
形成染色體a'': 41y30y2675;
染色體b'': 13y24y0576;
交叉完成。
3.5 變異
採用多值編碼後, 變異操作並不能通過簡單的0,1 反轉實現。
本文采取隨機地選取染色體內的兩個基因進行交換來實現。
例如隨機選取的是
6 和1 兩個基因,那麼
變異前染色體: 7 (6) 5 4 3 2 (1) 0
變異後染色體: 7 (1) 5 4 3 2 (6) 0
3.6 終止策略
本文采用的終止策略為: 當群體中出現染色體的適應值為0 時, 即表示演算法搜尋到了一個可行解,終止演算法。若演算法執行設定的代數還未找到可行解,同樣終止程式執行。
4、總結
本文詳細介紹了用遺傳演算法求解八皇后問題的求解過程, 但要注意的是這只是其中的一種編碼,交叉,變異等操作設計方法,還有許多其他的方法可以選擇。對於各操作採取不同設計方案的遺傳演算法,其演算法效能值得比較討論。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <time.h> 4 #include <stdbool.h> 5 #include <math.h> 6 7 // 8 // 程式設計題 9 // 遺傳演算法(八皇后問題) 10 // 第二版(終) 最佳值27 11 12 13 #define N 8 //皇后數 14 #define Cluster_size 12//預設種群大小 15 #define LASTG 100 /*終止後代*/ 16 #define MRATE 0.8 /*突變的概率*/ 17 int array[Cluster_size][N];//染色體集合 18 int narray[Cluster_size * 2][N];//下一代染色體集合 19 int values[Cluster_size];//評估陣列 20 int generation = 0; //記錄代數 21 int signal = -1;//訊號 22 int max=-1;//記錄當前最優值 23 int max_generation;//記錄當前最優值代數 24 25 26 struct MyStruct 27 { 28 int key[Cluster_size]; 29 int values[Cluster_size]; 30 } rember; //交叉時記錄對應關係 31 32 33 /* 34 1、八皇后問題描述 35 19 世紀著名的數學家Gauss 在1850 年提出 36 八皇后問題後, 該問題成為各類語言程式設計的 37 經典題目。八皇后問題要求在8×8 格的國際象棋 38 上擺放八個皇后,使橫、豎、斜方向上都不能有兩 39 個及兩個以上皇后在同一條直線上, 問題也可以 40 推廣到N 個皇后。窮舉法在問題規模不大的情況 41 下還可適用,回溯法是求解此問題的經典演算法。但 42 N 皇后問題是個NP 難問題, 隨著皇后數目的增 43 多, 求解複雜度激增, 就需利用非常規的技術求 44 解。遺傳演算法在求解一些NP 完全問題上得到了 45 廣泛地應用,本文用遺傳演算法求解八皇后問題,給 46 出詳細的實現過程。 47 */ 48 49 50 /* 基本遺傳演算法求解過程 51 基本遺傳以初始種群為基點, 經過選擇、交 52 叉、變異操作生成新的種群,如此更新種群直到滿 53 足終止條件。其計算步驟如下: 54 (1) 將問題空間轉換為遺傳空間, 也就是編 55 碼; 56 (2)隨機生成P 個染色體作為初始種群; 57 (3)染色體評價,也就是按確定的適應度函式 58 計算各個染色體的適應度; 59 (4)根據染色體適應度,按選擇運算元進行染色 60 體的選擇; 61 (5)按交叉概率進行交叉操作; 62 (6)按變異概率進行變異操作; 63 (7)返回(4)形成新的種群,繼續迭代,直到滿 64 足終止條件。*/ 65 66 //用遺傳演算法求解八皇后問題實現過程詳解 67 68 69 /*1 編碼 70 遺傳演算法中傳統的編碼是二進位制編碼, 本文 71 採用多值編碼。染色體長度取決於皇后的個數。染 72 色體中每個基因所處的位置表示其在棋譜中所在 73 的行數, 基因值表示其所在的列數。如染色體 74 40752613 表示:從0 開始數,第0 個4 表示在第 75 零行的皇后在第4 列, 第1 個0 表示第一行的皇 76 後在第0 列,以此類推。八皇后問題中皇后不能處 77 於同行同列, 意味著染色體中0~7 的基因取值不 78 能出現重複*/ 79 80 81 /*2 個體評價 82 染色體通常表示了問題的可行解, 對可行解 83 進行遺傳操作尋找最優解。但在八皇后問題中,染 84 色體僅僅體現同行同列中未出現互攻, 在對角線 85 上是否出現互攻還未做考慮。在對皇后的位置做 86 比較的時候, 可以對兩個棋子的行數差與列數差 87 進行對比, 實現了互攻次數的統計。公式為: 88 |(y2-y1)/(x2-x1)|=1。公式中(x1,y1),(x2,y2)分別表示兩個 89 皇后所在的位置,即所在的行數和列數。當兩個皇 90 後的行數差與列數差比值的絕對值為1 的時候, 91 兩皇后在同一對角線上,即出現了互攻。每個染色 92 體內的互攻次數為Value,初始值設為0;第0 行 93 與1~7 行進行比較, 每出現一次互攻則Value 的 94 值增加1;第1 行與2~7 行進行比較,以此類推來 95 計算Value 值。當Value 為0 表示沒有發生互攻, 96 此染色體就是其中的一個可行解。當Value 不為0 97 則進行適應度的計算。一般來說, 適應度越大越 98 好,而互攻次數為越小越好,所以可以將適應度的 99 計算函式設定為:F=1/Value。*/ 100 101 102 103 104 105 /*編碼方案 106 遺傳演算法的常用編碼方案有排列編碼、二進位制編碼、實值編碼等,考慮到編碼方案需要較好地對應一種棋盤皇后排列順序, 107 同時八皇后問題需要解決的是實體目標(即皇后)的排列問題,不涉及到浮點數層面的逼近問題,本程式採用排列編碼作為編碼方案,具體描述如下: 108 用一維n元陣列x0,1…,n-1來表示一個個體,其中x[i]∈{0,1…,n-1},x[i]表示皇后i放在棋盤的第i行第x[i]列,即第i行第x[i]列放置一個皇后。 109 例如,x[0]=0表示棋盤的第0行第0列放一個皇后。陣列第i個元素表示第i行的放置情況,可以看作一個基因。 110 這種編碼可以自然的解決了某一行只能放一個皇后的約束,如果陣列的每一個元素x[i]都不重複,可以看成0 — 7的一種排列,就自然保證每一列只能放一個皇后。 111 在編碼方案實現過程中,我們對染色體編碼是否可重複問題進行了討論,經實驗發現不允許染色體編碼重複的方案, 112 即每一個編碼必須為0-7排列的編碼方案的搜尋空間為8!種,而允許編碼出現重複的方案搜尋空間為8^8種, 113 在實驗執行過程中第一種編碼方案執行所需代數平均值比第二種方案要小一個數量級,因此最終決定採用了不允許染色體編碼重複的方案, 114 n元陣列x[0,1…,n-1]中,每一個x[i]不允許重複,在程式執行過程中,無論是初始種群生成,選擇,交叉,變異階段都會維護這一編碼準則。 115 */ 116 117 118 /*初始種群 119 初始化種群的主要工作為:確定種群的大小及產生初始種群.種群的大小能夠對遺傳演算法的收斂性產生很大的影響, 120 種群較小演算法收斂速度較快,但它的搜尋面不夠廣,容易導致區域性收斂;而種群較大演算法收斂全域性最優的概率也大, 121 但是演算法的收斂速度較慢。根據N皇后問題的特點,預設初始種群大小一般為N ~ 2N (N為皇后數)。經多次實驗, 122 初始種群大小的設定從8、12、16、24沒有較大區別,對遺傳代數沒有突出影響,因此將預設初始種群大小設為12,介面中提供了使用者自行設定初始種群大小的方法。 123 除此之外,程式提供了兩種初始種群的生成方法,第一種是隨機生成,程式將自動生成使用者設定數量的隨機個體作為初始種群, 124 第二種是使用者輸入,程式初始種群一欄提供了使用者自行輸入每一個個體基因編碼的介面。值得注意的是,以上無論哪種方法, 125 生成的都是0-7不含重複的8位隨機排列。系統不會接受任何編碼出現重複的非法個體。 126 */ 127 128 129 130 int rndn(int l) 131 { 132 int rndno; 133 rndno = ((double)rand() / RAND_MAX) * l; 134 return rndno; 135 136 } 137 138 139 140 141 //判斷是否有最優解 142 int the_answer(int* values, int size) 143 { 144 for (int i = 0; i < size; i++) 145 if (values[i] == 28) 146 return i; 147 return -1; 148 } 149 150 151 152 int judge(int a[], int n) 153 { 154 int i, j; 155 int value = -1; 156 for (i = 0; i < n; i++) { 157 value = a[i]; 158 for (j = i + 1; j < n; j++) { 159 if ((value == a[j])) { 160 return 0; 161 } 162 } 163 } 164 return 1; 165 166 } 167 168 169 170 171 172 //計算初始適應度值 173 void count_collidecount() //適應度函式設定為:value[i]=28-value 174 { 175 176 int i, j, x1, x2, y1, y2, m, value = 0; 177 for (i = 0; i < Cluster_size; i++) { 178 179 for (j = 0; j < N; j++) 180 { 181 x1 = j; 182 y1 = array[i][j]; 183 for (m = j + 1; m < N; m++) 184 { 185 x2 = m; 186 y2 = array[i][m]; 187 if (abs((y2 - y1) / (x2 - x1))==1) 188 value++; 189 } 190 191 } 192 values[i] = 28 - value; 193 value = 0; 194 } 195 196 signal = the_answer(values, Cluster_size); 197 198 } 199 200 201 //計運算元代適應度值 202 void count_generation_collidecount(int* values, int cluster_size) 203 { 204 205 int i, j, x1, x2, y1, y2, m, value = 0; 206 for (i = 0; i < cluster_size; i++) { 207 208 for (j = 0; j < N; j++) 209 { 210 x1 = j; 211 y1 = narray[i][j]; 212 for (m = j + 1; m < N; m++) 213 { 214 x2 = m; 215 y2 = narray[i][m]; 216 if (abs((y2 - y1) / (x2 - x1))==1) 217 value++; 218 } 219 220 } 221 values[i] = 28 - value; 222 value = 0; 223 } 224 225 226 } 227 228 229 230 231 232 /************************/ 233 /* selectp()函式 */ 234 /* 父代的選擇 */ 235 /************************/ 236 int selectp(int roulette[], int totalfitness) 237 { 238 int i;/* 迴圈的控制變數 */ 239 int ball;/* 球(選擇位置的數值)*/ 240 int acc = 0;/*評價值的累積值*/ 241 242 ball = rndn(totalfitness); 243 for (i = 0; i < Cluster_size; ++i) { 244 acc += roulette[i];/*評價值的累積*/ 245 if (acc > ball) break;/*對應的基因*/ 246 } 247 return i; 248 } 249 250 251 252 253 254 255 bool takeoutrepeat(int position)//去除有重複元素的陣列,並新增無重複值的陣列(染色體) 256 { 257 258 int i; 259 int value; 260 bool signal = true; 261 262 263 for (i = 0; i < N; i++) 264 { 265 value = narray[position*2][i]; 266 for (int j = i + 1; j < N; j++) 267 if (narray[position*2][j] == value) 268 { 269 printf("there have reapt number: %d\n", (position*2)); 270 signal = false; 271 } 272 273 } 274 275 for (i = 0; i < N; i++) 276 { 277 value = narray[position * 2+1][i]; 278 for (int j = i + 1; j < N; j++) 279 if (narray[position * 2+1][j] == value) 280 { 281 printf("there have reapt number: %d\n", (position*2+1)); 282 signal = false; 283 } 284 } 285 286 return signal; 287 288 289 } 290 291 292 //判斷兩個陣列是否相等 293 bool judge_reapt(int c,int cluster) 294 { 295 296 int i,j; 297 int value=0; 298 bool arraysEqual = true; 299 300 for (i=0,j = 0; j < cluster; j++) 301 { 302 while (arraysEqual && i < N) 303 { 304 if (narray[c][i] != narray[j][i]) 305 arraysEqual = false; 306 i++; 307 } 308 //顯示合適的訊息 309 if (arraysEqual) 310 value++; 311 else 312 arraysEqual = true; 313 314 i = 0;//i置0 315 } 316 317 if (value > 0) 318 return false; 319 else 320 return true; 321 322 } 323 324 325 /************************/ 326 /* selection()函式 */ 327 /* 選擇下一代 */ 328 /************************/ 329 void selection() 330 { 331 int i, j, c;/* 迴圈控制引數 */ 332 int totalfitness = 0;/*適應度的總計值*/ 333 int roulette[Cluster_size * 2];/*存放適應度*/ 334 int ball;/* 球(選擇位置的數值)*/ 335 int acc = 0;/*適應度的累積值*/ 336 bool judge; 337 338 /*迴圈進行選擇*/ 339 for (i = 0; i < Cluster_size; ++i) { 340 /* 生成輪盤 */ 341 totalfitness = 0; 342 count_generation_collidecount(roulette, Cluster_size * 2); 343 344 for (c = 0; c < Cluster_size * 2; ++c) { 345 /*計算適應度的總計值*/ 346 totalfitness += roulette[c]; 347 } 348 signal = the_answer(roulette, Cluster_size * 2);//判斷是否有最優解 349 350 do 351 { 352 /*選擇一個染色體*/ 353 ball = rndn(totalfitness); 354 acc = 0; 355 for (c = 0; c < Cluster_size * 2; ++c) { 356 acc += roulette[c];/*累積評價值*/ 357 if (acc > ball) break;/*對應的基因*/ 358 } 359 judge= judge_reapt(c, Cluster_size ); 360 361 } while (judge==false); 362 363 364 /*染色體的複製*/ 365 for (j = 0; j < N; ++j) { 366 array[i][j] = narray[c][j]; 367 } 368 } 369 370 for (int q = 0; q< Cluster_size *2; q++) 371 { 372 if (roulette[q] > max) 373 { 374 max = roulette[q]; 375 max_generation = generation; 376 } 377 printf("%3.1d", roulette[q]); 378 379 } 380 printf("\n"); 381 382 } 383 384 385 int judgein(int m, int location1, int location2) 386 { 387 for (int i = location1; i <= location2; i++) 388 if ((m == rember.key[i]) | (m == rember.values[i])) 389 return i; 390 return -1; 391 } 392 393 394 395 396 /************************/ 397 /* crossing()函式 */ 398 /* 特定2染色體的交叉 */ 399 /************************/ 400 void crossing(int mama, int papa, int position) 401 { 402 bool signal; 403 int m; 404 int cp1;/*交叉的點*/ 405 int cp2;/*交叉的點*/ 406 int location1; 407 int location2; 408 printf("mama=%d,papa=%d\n", mama, papa); 409 410 do{ 411 412 /*確定交叉點*/ 413 do 414 { 415 cp1 = rndn(N); 416 cp2 = rndn(N); 417 // m = abs((cp2 - cp1)); 418 } while (cp1 == cp2); 419 printf("tuytuiyiyoiouoio\n"); 420 printf("%d,%d\n", cp1, cp2); 421 if (cp1 < cp2) 422 { 423 location1 = cp1; 424 location2 = cp2; 425 } 426 else 427 { 428 location1 = cp2; 429 location2 = cp1; 430 } 431 432 433 for (int i = location1; i <= location2; i++) 434 { 435 int temp; //中間變數 436 rember.key[i] = array[mama][i]; 437 rember.values[i] = array[papa][i]; 438 //交換中間段 439 narray[position * 2][i] = array[papa][i]; 440 narray[position * 2 + 1][i] = array[mama][i]; 441 } 442 //利用對應關係,對染色體mama和papa, 中間段外的基因進行交換 443 /*交換前半部分*/ 444 for (int j = 0; j < location1; j++) 445 { 446 int weizhi = judgein(array[mama][j], location1, location2); 447 printf("weizhi=%d\n", weizhi); 448 if (weizhi == -1) 449 { 450 narray[position * 2][j] = array[mama][j]; 451 } 452 else 453 { 454 if (array[mama][j] == rember.key[weizhi]) 455 narray[position * 2][j] = rember.values[weizhi]; 456 else 457 narray[position * 2][j] = rember.key[weizhi]; 458 } 459 460 weizhi = judgein(array[papa][j], location1, location2); 461 if (weizhi == -1) 462 { 463 narray[position * 2 + 1][j] = array[papa][j]; 464 } 465 else 466 { 467 if (array[papa][j] == rember.key[weizhi]) 468 narray[position * 2 + 1][j] = rember.values[weizhi]; 469 else 470 narray[position * 2 + 1][j] = rember.key[weizhi]; 471 } 472 473 } 474 475 476 ///*交換後半部分*/ 477 for (int j = location2 + 1; j < N; j++) 478 { 479 int weizhi = judgein(array[mama][j], location1, location2); 480 if (weizhi == -1) 481 { 482 narray[position * 2][j] = array[mama][j]; 483 } 484 else 485 { 486 if (array[mama][j] == rember.key[weizhi]) 487 narray[position * 2][j] = rember.values[weizhi]; 488 else 489 narray[position * 2][j] = rember.key[weizhi]; 490 } 491 492 weizhi = judgein(array[papa][j], location1, location2); 493 if (weizhi == -1) 494 { 495 narray[position * 2 + 1][j] = array[papa][j]; 496 } 497 else 498 { 499 if (array[papa][j] == rember.key[weizhi]) 500 narray[position * 2 + 1][j] = rember.values[weizhi]; 501 else 502 narray[position * 2 + 1][j] = rember.key[weizhi]; 503 504 } 505 } 506 507 signal = takeoutrepeat(position); 508 printf("\n--------------signal=%d--------------\n",signal); 509 } while (signal==false); 510 511 512 } 513 514 515 /************************/ 516 /* notval()函式 */ 517 /* */ 518 /************************/ 519 void notval(int i) 520 { 521 int position1; 522 int position2; 523 int temp; //兩個基因點交換的中間變數 524 do 525 { 526 position1 = rndn(N); 527 position2 = rndn(N); 528 529 } while (position2 == position1); //當兩個變異基因點相同時,迴圈。 530 531 temp = narray[i][position2]; 532 narray[i][position2] = narray[i][position1]; 533 narray[i][position1] = temp; 534 535 } 536 537 538 539 /***********************/ 540 /* mutation()函式 */ 541 /* 突變 */ 542 /***********************/ 543 void mutation() 544 { 545 int i, j;/* 迴圈的控制變數 */ 546 547 for (i = 0; i < Cluster_size * 2; ++i) 548 if ((double)rndn(100) / 100.0 <= MRATE) 549 /*染色體突變*/ 550 notval(i); 551 printf("\nmutation is complete\n"); 552 553 } 554 555 556 557 558 void mating() 559 { 560 561 int i; 562 int totalfitness = 0; 563 int roulette[Cluster_size];//存放評價值 564 int mama, papa; //父代的基因的號碼 565 566 // 生成輪盤 567 for (i = 0; i < Cluster_size; ++i) 568 { 569 roulette[i] = values[i]; 570 totalfitness += roulette[i]; 571 } 572 573 //選擇和交叉的迴圈 574 for (i = 0; i < Cluster_size; i++) 575 { 576 do { //父代的選擇 577 mama = selectp(roulette, totalfitness); 578 papa = selectp(roulette, totalfitness); 579 } while (mama == papa); 580 581 //特定2染色體的交叉 582 crossing(mama, papa, i); 583 } 584 585 } 586 587 588 589 590 591 592 void outputrember() 593 { 594 for (int i = 0; i < N; i++) 595 printf("key=%d,values=%d\n", rember.key[i], rember.values[i]); 596 } 597 598 599 void outputnarray() 600 { 601 for (int i = 0; i < Cluster_size * 2; i++) 602 { 603 if (i % 2 == 0) 604 printf("---------------------------------------------------\n"); 605 for (int j = 0; j < N; j++) 606 printf("%d", narray[i][j]); 607 printf("\n"); 608 } 609 610 } 611 612 613 614 615 void outputarray() 616 { 617 for (int i = 0; i < Cluster_size; i++) 618 { 619 for (int j = 0; j < N; j++) 620 printf("%d", array[i][j]); 621 printf("\n"); 622 } 623 624 } 625 626 627 void output() 628 { 629 int i; 630 for (i = 0; i < Cluster_size; i++) 631 { 632 if (values[i] > max) 633 { 634 max = values[i]; 635 max_generation = generation; 636 } 637 printf("%3.1d", values[i]); 638 639 } 640 printf("\n"); 641 } 642 643 644 645 646 647 void init_Cluster() 648 { 649 int a[8]; 650 int x, y; 651 int count = 0; 652 653 for (; count < Cluster_size; count++) 654 { 655 656 for (y = 0; y < 8; y++) 657 { 658 x = rand() % 8; 659 a[y] = x; 660 } 661 662 if (judge(a, 8)) 663 { 664 for (int i = 0; i < 8; i++) 665 { 666 array[count][i] = a[i]; 667 } 668 } 669 else 670 --count; 671 672 } 673 674 } 675 676 677 678 679 int main() 680 { 681 srand((int)time(NULL)); //隨機種子 682 init_Cluster(); 683 684 for (; generation < LASTG; generation++) 685 { 686 687 if (signal != -1) 688 break; 689 else 690 { 691 printf("\n%d代數\n", generation); 692 count_collidecount(); 693 printf("-------------output----------values----------------------------\n"); 694 output(); 695 mating(); 696 printf("------------- outputarray----------------------------------------\n"); 697 outputarray(); 698 printf("-----------------mating選擇交叉---------outputnarray---------------------------\n"); 699 outputnarray(); 700 printf("---------- mutation-變異------------outputnarray------------------------------\n"); 701 mutation(); 702 outputnarray(); 703 printf("------------selection------outputarray下一代種群------------------------------\n"); 704 selection(); 705 outputarray(); 706 } 707 } 708 printf("\nsignal = %d, max = %d, max_generation = %d\n", signal, max ,max_generation); //max為最佳值, max_generation為產生最佳值的代數 709 710 return 0; 711 712 713 }
- #include<stdio.h>
- #include<stdlib.h>
- #include<time.h>
- #include<stdbool.h>
- #include<math.h>
- //
- //程式設計題
- //遺傳演算法(八皇后問題)
- //
- #defineN8//皇后數
- #defineCluster_size12//預設種群大小
- #defineLASTG100/*終止後代*/
- #defineMRATE0.8/*突變的概率*/
- intarray[Cluster_size][N];//染色體集合
- intnarray[Cluster_size*2][N];//下一代染色體集合
- intvalues[Cluster_size];//評估陣列
- intgeneration=0;//記錄代數
- intsignal=-1;//訊號
- intmax=-1;//記錄當前最優值
- intmax_generation;//記錄當前最優值代數
- structMyStruct
- {
- intkey[Cluster_size];
- intvalues[Cluster_size];
- }rember;//交叉時記錄對應關係
- /*
- 1、八皇后問題描述
- 19世紀著名的數學家Gauss在1850年提出
- 八皇后問題後,該問題成為各類語言程式設計的
- 經典題目。八皇后問題要求在8×8格的國際象棋
- 上擺放八個皇后,使橫、豎、斜方向上都不能有兩
- 個及兩個以上皇后在同一條直線上,問題也可以
- 推廣到N個皇后。窮舉法在問題規模不大的情況
- 下還可適用,回溯法是求解此問題的經典演算法。但
- N皇后問題是個NP難問題,隨著皇后數目的增
- 多,求解複雜度激增,就需利用非常規的技術求
- 解。遺傳演算法在求解一些NP完全問題上得到了
- 廣泛地應用,本文用遺傳演算法求解八皇后問題,給
- 出詳細的實現過程。
- */
- /*基本遺傳演算法求解過程
- 基本遺傳以初始種群為基點,經過選擇、交
- 叉、變異操作生成新的種群,如此更新種群直到滿
- 足終止條件。其計算步驟如下:
- (1)將問題空間轉換為遺傳空間,也就是編
- 碼;
- (2)隨機生成P個染色體作為初始種群;
- (3)染色體評價,也就是按確定的適應度函式
- 計算各個染色體的適應度;
- (4)根據染色體適應度,按選擇運算元進行染色
- 體的選擇;
- (5)按交叉概率進行交叉操作;
- (6)按變異概率進行變異操作;
- (7)返回(4)形成新的種群,繼續迭代,直到滿
- 足終止條件。*/
- //用遺傳演算法求解八皇后問題實現過程詳解
- /*1編碼
- 遺傳演算法中傳統的編碼是二進位制編碼,本文
- 採用多值編碼。染色體長度取決於皇后的個數。染
- 色體中每個基因所處的位置表示其在棋譜中所在
- 的行數,基因值表示其所在的列數。如染色體
- 40752613表示:從0開始數,第0個4表示在第
- 零行的皇后在第4列,第1個0表示第一行的皇
- 後在第0列,以此類推。八皇后問題中皇后不能處
- 於同行同列,意味著染色體中0~7的基因取值不
- 能出現重複*/
- /*2個體評價
- 染色體通常表示了問題的可行解,對可行解
- 進行遺傳操作尋找最優解。但在八皇后問題中,染
- 色體僅僅體現同行同列中未出現互攻,在對角線
- 上是否出現互攻還未做考慮。在對皇后的位置做
- 比較的時候,可以對兩個棋子的行數差與列數差
- 進行對比,實現了互攻次數的統計。公式為:
- |(y2-y1)/(x2-x1)|=1。公式中(x1,y1),(x2,y2)分別表示兩個
- 皇后所在的位置,即所在的行數和列數。當兩個皇
- 後的行數差與列數差比值的絕對值為1的時候,
- 兩皇后在同一對角線上,即出現了互攻。每個染色
- 體內的互攻次數為Value,初始值設為0;第0行
- 與1~7行進行比較,每出現一次互攻則Value的
- 值增加1;第1行與2~7行進行比較,以此類推來
- 計算Value值。當Value為0表示沒有發生互攻,
- 此染色體就是其中的一個可行解。當Value不為0
- 則進行適應度的計算。一般來說,適應度越大越
- 好,而互攻次數為越小越好,所以可以將適應度的
- 計算函式設定為:F=1/Value。*/
- /*編碼方案
- 遺傳演算法的常用編碼方案有排列編碼、二進位制編碼、實值編碼等,考慮到編碼方案需要較好地對應一種棋盤皇后排列順序,
- 同時八皇后問題需要解決的是實體目標(即皇后)的排列問題,不涉及到浮點數層面的逼近問題,本程式採用排列編碼作為編碼方案,具體描述如下:
- 用一維n元陣列x0,1…,n-1來表示一個個體,其中x[i]∈{0,1…,n-1},x[i]表示皇后i放在棋盤的第i行第x[i]列,即第i行第x[i]列放置一個皇后。
- 例如,x[0]=0表示棋盤的第0行第0列放一個皇后。陣列第i個元素表示第i行的放置情況,可以看作一個基因。
- 這種編碼可以自然的解決了某一行只能放一個皇后的約束,如果陣列的每一個元素x[i]都不重複,可以看成0—7的一種排列,就自然保證每一列只能放一個皇后。
- 在編碼方案實現過程中,我們對染色體編碼是否可重複問題進行了討論,經實驗發現不允許染色體編碼重複的方案,
- 即每一個編碼必須為0-7排列的編碼方案的搜尋空間為8!種,而允許編碼出現重複的方案搜尋空間為8^8種,
- 在實驗執行過程中第一種編碼方案執行所需代數平均值比第二種方案要小一個數量級,因此最終決定採用了不允許染色體編碼重複的方案,
- n元陣列x[0,1…,n-1]中,每一個x[i]不允許重複,在程式執行過程中,無論是初始種群生成,選擇,交叉,變異階段都會維護這一編碼準則。
- */
- /*初始種群
- 初始化種群的主要工作為:確定種群的大小及產生初始種群.種群的大小能夠對遺傳演算法的收斂性產生很大的影響,
- 種群較小演算法收斂速度較快,但它的搜尋面不夠廣,容易導致區域性收斂;而種群較大演算法收斂全域性最優的概率也大,
- 但是演算法的收斂速度較慢。根據N皇后問題的特點,預設初始種群大小一般為N~2N(N為皇后數)。經多次實驗,
- 初始種群大小的設定從8、12、16、24沒有較大區別,對遺傳代數沒有突出影響,因此將預設初始種群大小設為12,介面中提供了使用者自行設定初始種群大小的方法。
- 除此之外,程式提供了兩種初始種群的生成方法,第一種是隨機生成,程式將自動生成使用者設定數量的隨機個體作為初始種群,
- 第二種是使用者輸入,程式初始種群一欄提供了使用者自行輸入每一個個體基因編碼的介面。值得注意的是,以上無論哪種方法,
- 生成的都是0-7不含重複的8位隨機排列。系統不會接受任何編碼出現重複的非法個體。
- */
- intrndn(intl)
- {
- intrndno;
- rndno=((double)rand()/RAND_MAX)*l;
- returnrndno;
- }
- //判斷是否有最優解
- intthe_answer(int*values,intsize)
- {
- for(inti=0;i<size;i++)
- if(values[i]==28)
- returni;
- return-1;
- }
- intjudge(inta[],intn)
- {
- inti,j;
- intvalue=-1;
- for(i=0;i<n;i++){
- value=a[i];
- for(j=i+1;j<n;j++){
- if((value==a[j])){
- return0;
- }
- }
- }
- return1;
- }
- //計算初始適應度值
- voidcount_collidecount()//適應度函式設定為:value[i]=28-value
- {
- inti,j,x1,x2,y1,y2,m,value=0;
- for(i=0;i<Cluster_size;i++){
- for(j=0;j<N;j++)
- {
- x1=j;
- y1=array[i][j];
- for(m=j+1;m<N;m++)
- {
- x2=m;
- y2=array[i][m];
- if(abs((y2-y1)/(x2-x1))==1)
- value++;
- }
- }
- values[i]=28-value;
- value=0;
- }
- signal=the_answer(values,Cluster_size);
- }
- //計運算元代適應度值
- voidcount_generation_collidecount(int*values,intcluster_size)
- {
- inti,j,x1,x2,y1,y2,m,value=0;
- for(i=0;i<cluster_size;i++){
- for(j=0;j<N;j++)
- {
- x1=j;
- y1=narray[i][j];
- for(m=j+1;m<N;m++)
- {
- x2=m;
- y2=narray[i][m];
- if(abs((y2-y1)/(x2-x1))==1)
- value++;
- }
- }
- values[i]=28-value;
- value=0;
- }
- }
- /************************/
- /*selectp()函式*/
- /*父代的選擇*/
- /************************/
- intselectp(introulette[],inttotalfitness)
- {
- inti;/*迴圈的控制變數*/
- intball;/*球(選擇位置的數值)*/
- intacc=0;/*評價值的累積值*/
- ball=rndn(totalfitness);
- for(i=0;i<Cluster_size;++i){
- acc+=roulette[i];/*評價值的累積*/
- if(acc>ball)break;/*對應的基因*/
- }
- returni;
- }
- booltakeoutrepeat(intposition)//去除有重複元素的陣列,並新增無重複值的陣列(染色體)
- {
- inti;
- intvalue;
- boolsignal=true;
- for(i=0;i<N;i++)
- {
- value=narray[position*2][i];
- for(intj=i+1;j<N;j++)
- if(narray[position*2][j]==value)
- {
- printf("therehavereaptnumber:%d\n",(position*2));
- signal=false;
- }
- }
- for(i=0;i<N;i++)
- {
- value=narray[position*2+1][i];
- for(intj=i+1;j<N;j++)
- if(narray[position*2+1][j]==value)
- {
- printf("therehavereaptnumber:%d\n",(position*2+1));
- signal=false;
- }
- }
- returnsignal;
- }
- //判斷兩個陣列是否相等
- booljudge_reapt(intc,intcluster)
- {
- inti,j;
- intvalue=0;
- boolarraysEqual=true;
- for(i=0,j=0;j<cluster;j++)
- {
- while(arraysEqual&&i<N)
- {
- if(narray[c][i]!=narray[j][i])
- arraysEqual=false;
- i++;
- }
- //顯示合適的訊息
- if(arraysEqual)
- value++;
- else
- arraysEqual=true;
- i=0;//i置0
- }
- if(value>0)
- returnfalse;
- else
- returntrue;
- }
- /************************/
- /*selection()函式*/
- /*選擇下一代*/
- /************************/
- voidselection()
- {
- inti,j,c;/*迴圈控制引數*/
- inttotalfitness=0;/*適應度的總計值*/
- introulette[Cluster_size*2];/*存放適應度*/
- intball;/*球(選擇位置的數值)*/
- intacc=0;/*適應度的累積值*/
- booljudge;
- /*迴圈進行選擇*/
- for(i=0;i<Cluster_size;++i){
- /*生成輪盤*/
- totalfitness=0;
- count_generation_collidecount(roulette,Cluster_size*2);
- for(c=0;c<Cluster_size*2;++c){
- /*計算適應度的總計值*/
- totalfitness+=roulette[c];
- }
- signal=the_answer(roulette,Cluster_size*2);//判斷是否有最優解
- do
- {
- /*選擇一個染色體*/
- ball=rndn(totalfitness);
- acc=0;
- for(c=0;c<Cluster_size*2;++c){
- acc+=roulette[c];/*累積評價值*/
- if(acc>ball)break;/*對應的基因*/
- }
- judge=judge_reapt(c,Cluster_size);
- }while(judge==false);
- /*染色體的複製*/
- for(j=0;j<N;++j){
- array[i][j]=narray[c][j];
- }
- }
- for(intq=0;q<Cluster_size*2;q++)
- {
- if(roulette[q]>max)
- {
- max=roulette[q];
- max_generation=generation;
- }
- printf("%3.1d",roulette[q]);
- }
- printf("\n");
- }
- intjudgein(intm,intlocation1,intlocation2)
- {
- for(inti=location1;i<=location2;i++)
- if((m==rember.key[i])|(m==rember.values[i]))
- returni;
- return-1;
- }
- /************************/
- /*crossing()函式*/
- /*特定2染色體的交叉*/
- /************************/
- voidcrossing(intmama,intpapa,intposition)
- {
- boolsignal;
- intm;
- intcp1;/*交叉的點*/
- intcp2;/*交叉的點*/
- intlocation1;
- intlocation2;
- printf("mama=%d,papa=%d\n",mama,papa);
- do{
- /*確定交叉點*/
- do
- {
- cp1=rndn(N);
- cp2=rndn(N);
- //m=abs((cp2-cp1));
- }while(cp1==cp2);
- printf("tuytuiyiyoiouoio\n");
- printf("%d,%d\n",cp1,cp2);
- if(cp1<cp2)
- {
- location1=cp1;
- location2=cp2;
- }
- else
- {
- location1=cp2;
- location2=cp1;
- }
- for(inti=location1;i<=location2;i++)
- {
- inttemp;//中間變數
- rember.key[i]=array[mama][i];
- rember.values[i]=array[papa][i];
- //交換中間段
- narray[position*2][i]=array[papa][i];
- narray[position*2+1][i]=array[mama][i];
- }
- //利用對應關係,對染色體mama和papa,中間段外的基因進行交換
- /*交換前半部分*/
- for(intj=0;j<location1;j++)
- {
- intweizhi=judgein(array[mama][j],location1,location2);
- printf("weizhi=%d\n",weizhi);
- if(weizhi==-1)
- {
- narray[position*2][j]=array[mama][j];
- }
- else
- {
- if(array[mama][j]==rember.key[weizhi])
- narray[position*2][j]=rember.values[weizhi];
- else
- narray[position*2][j]=rember.key[weizhi];
- }
- weizhi=judgein(array[papa][j],location1,location2);
- if(weizhi==-1)
- {
- narray[position*2+1][j]=array[papa][j];
- }
- else
- {
- if(array[papa][j]==rember.key[weizhi])
- narray[position*2+1][j]=rember.values[weizhi];
- else
- narray[position*2+1][j]=rember.key[weizhi];
- }
- }
- ///*交換後半部分*/
- for(intj=location2+1;j<N;j++)
- {
- intweizhi=judgein(array[mama][j],location1,location2);
- if(weizhi==-1)
- {
- narray[position*2][j]=array[mama][j];
- }
- else
- {
- if(array[mama][j]==rember.key[weizhi])
- narray[position*2][j]=rember.values[weizhi];
- else
- narray[position*2][j]=rember.key[weizhi];
- }
- weizhi=judgein(array[papa][j],location1,location2);
- if(weizhi==-1)
- {
- narray[position*2+1][j]=array[papa][j];
- }
- else
- {
- if(array[papa][j]==rember.key[weizhi])
- narray[position*2+1][j]=rember.values[weizhi];
- else
- narray[position*2+1][j]=rember.key[weizhi];
- }
- }
- signal=takeoutrepeat(position);
- printf("\n--------------signal=%d--------------\n",signal);
- }while(signal==false);
- }
- /************************/
- /*notval()函式*/
- /**/
- /************************/
- voidnotval(inti)
- {
- intposition1;
- intposition2;
- inttemp;//兩個基因點交換的中間變數
- do
- {
- position1=rndn(N);
- position2=rndn(N);
- }while(position2==position1);//當兩個變異基因點相同時,迴圈。
- temp=narray[i][position2];
- narray[i][position2]=narray[i][position1];
- narray[i][position1]=temp;
- }
- /***********************/
- /*mutation()函式*/
- /*突變*/
- /***********************/
- voidmutation()
- {
- inti,j;/*迴圈的控制變數*/
- for(i=0;i<Cluster_size*2;++i)
- if((double)rndn(100)/100.0<=MRATE)
- /*染色體突變*/
- notval(i);
- printf("\nmutationiscomplete\n");
- }
- voidmating()
- {
- inti;
- inttotalfitness=0;
- introulette[Cluster_size];//存放評價值
- intmama,papa;//父代的基因的號碼
- //生成輪盤
- for(i=0;i<Cluster_size;++i)
- {
- roulette[i]=values[i];
- totalfitness+=roulette[i];
- }
- //選擇和交叉的迴圈
- for(i=0;i<Cluster_size;i++)
- {
- do{//父代的選擇
- mama=selectp(roulette,totalfitness);
- papa=selectp(roulette,totalfitness);
- }while(mama==papa);
- //特定2染色體的交叉
- crossing(mama,papa,i);
- }
- }
- voidoutputrember()
- {
- for(inti=0;i<N;i++)
- printf("key=%d,values=%d\n",rember.key[i],rember.values[i]);
- }
- voidoutputnarray()
- {
- for(inti=0;i<Cluster_size*2;i++)
- {
- if(i%2==0)
- printf("---------------------------------------------------\n");
- for(intj=0;j<N;j++)
- printf("%d",narray[i][j]);
- printf("\n");
- }
- }
- voidoutputarray()
- {
- for(inti=0;i<Cluster_size;i++)
- {
- for(intj=0;j<N;j++)
- printf("%d",array[i][j]);
- printf("\n");
- }
- }
- voidoutput()
- {
- inti;
- for(i=0;i<Cluster_size;i++)
- {
- if(values[i]>max)
- {
- max=values[i];
- max_generation=generation;
- }
- printf("%3.1d",values[i]);
- }
- printf("\n");
- }
- voidinit_Cluster()
- {
- inta[8];
- intx,y;
- intcount=0;
- for(;count<Cluster_size;count++)
- {
- for(y=0;y<8;y++)
- {
- x=rand()%8;
- a[y]=x;
- }
- if(judge(a,8))
- {
- for(inti=0;i<8;i++)
- {
- array[count][i]=a[i];
- }
- }
- else
- --count;
- }
- }
- intmain()
- {
- srand((int)time(NULL));//隨機種子
- init_Cluster();
- for(;generation<LASTG;generation++)
- {
- if(signal!=-1)
- break;
- else
- {
- printf("\n%d代數\n",generation);
- count_collidecount();
- printf("-------------output------------values----------------------------\n");
- output();
- mating();
- printf("-------------outputarray----------------------------------------\n");
- outputarray();
- printf("-----------------mating選擇交叉---------outputnarray---------------------------\n");
- outputnarray();
- printf("----------mutation-變異------------outputnarray------------------------------\n");
- mutation();
- outputnarray();
- printf("------------selection------outputarray下一代種群------------------------------\n");
- selection();
- outputarray();
- }
- }
- printf("\nsignal=%d,max=%d,max_generation=%d\n",signal,max,max_generation);
- return0;
- }