八皇后問題遺傳演算法實現(python版)
八皇后問題的遺傳演算法實現過程詳解
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 個體評價
染色體通常表示了問題的可行解,對可行解進行遺傳操作尋找最優解。但在八皇后問題中,染色體僅僅體現同行同列中未出現互攻,在對角線上是否出現互攻還未做考慮。在對皇后的位置做比較的時候,可以對兩個棋子的行數差與列數差進行對比,實現了互攻次數的統計。公式為:|絕對值((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。
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 # 2 # 遺傳演算法(八皇后問題) 3 # 4 import random 5 import numpy 6 7 8 N=8 #皇后數 9 Cluster_size=12 #預設種群大小 10 LASTG=100 #/*終止後代*/ 11 MRATE=0.8 #/*突變的概率*/ 12 array=numpy.zeros((Cluster_size,N)).astype(int) #染色體集合 13 narray=numpy.zeros((Cluster_size * 2,N)).astype(int) #下一代染色體集合 14 _array=numpy.zeros((Cluster_size,N)).astype(int)#array陣列的副本 15 values=numpy.zeros(Cluster_size).astype(int) #評估陣列 16 max_array=numpy.zeros(N).astype(int) #儲存最佳陣列 17 generation = 0 #記錄代數 18 signal = -1 # 訊號 19 max = -1 # 記錄當前最優值 20 max_generation = -1 #記錄當前最優值代數 21 22 23 24 class Struct: 25 key = numpy.zeros(N).astype(int) 26 values = numpy.zeros(N).astype(int) 27 28 29 rember=Struct() 30 31 32 def rndn(l): 33 rndno =random.randint(0,l-1) 34 return rndno 35 36 37 def copy_array(): 38 for i in range(Cluster_size): 39 for j in range(N): 40 _array[i][j]=array[i][j] 41 42 43 def output_copy_array(): 44 for i in range(Cluster_size): 45 for j in range(N): 46 print(_array[i][j],end=" ") 47 print() 48 49 50 def the_answer(values,size): 51 for i in range(size): 52 if values[i]==28: 53 return i 54 return -1 55 56 57 def judge(a,n): 58 value = -1 59 for i in range(n): 60 value = a[i] 61 j=i+1 62 while j<n: 63 if(value==a[j]): 64 return 0 65 j+=1 66 return 1 67 68 69 70 def count_collidecount(): 71 value=0 72 global signal 73 for i in range(Cluster_size): 74 for j in range(N): 75 x1=j 76 y1=array[i][j] 77 m=j+1 78 while m<N: 79 x2=m 80 y2=array[i][m] 81 if (abs((y2-y1)/(x2-x1))==1): 82 value+=1 83 m+=1 84 values[i]=28-value 85 value=0 86 signal= the_answer(values,Cluster_size) 87 88 89 90 def count_generation_collidecount(values,cluster_size): 91 value=0 92 for i in range(cluster_size): 93 for j in range(N): 94 x1=j 95 y1=narray[i][j] 96 m=j+1 97 while m<N: 98 x2=m 99 y2=narray[i][m] 100 if(abs((y2 - y1) / (x2 - x1))==1): 101 value+=1 102 m+=1 103 values[i]=28-value 104 value=0 105 106 107 # /************************/ 108 # /* selectp()函式 */ 109 # /* 父代的選擇 */ 110 # /************************/ 111 def selectp(roulette,totalfitness): 112 acc=0 113 ball=rndn(totalfitness) 114 for i in range(Cluster_size): 115 acc+=roulette[i] 116 if (acc>ball): 117 break 118 return i 119 120 121 def takeoutrepeat( position): 122 signal=True 123 for i in range(N): 124 value = narray[position*2][i] 125 j=i+1 126 while j<N: 127 if (narray[position*2][j]==value): 128 # print("there have reapt number: "+str(position*2)) 129 signal=False 130 j+=1 131 for i in range(N): 132 value=narray[position*2+1][i] 133 j=i+1 134 while j<N: 135 if (narray[position*2+1][j]==value): 136 # print("there have reapt number: "+str(position*2+1)) 137 signal=False 138 j+=1 139 return signal 140 141 142 143 144 def judge_reapt(c, cluster): 145 value =0 146 arraysEqual =True 147 i=0 148 for j in range(cluster): 149 while (arraysEqual and i<N): 150 if (narray[c][i] !=_array[j][i]): 151 arraysEqual=False 152 i+=1 153 if(arraysEqual): 154 value+=1 155 else: 156 arraysEqual=True 157 i=0 158 159 if(value>0): 160 return False 161 else: 162 return True 163 164 165 166 167 # /************************/ 168 # /* selection()函式 */ 169 # /* 選擇下一代 */ 170 # /************************ 171 172 def selection(): 173 global signal 174 global max_generation 175 global max 176 totalfitness=0 177 roulette=numpy.zeros(Cluster_size*2).astype(int) 178 acc=0 179 for i in range(Cluster_size): 180 totalfitness=0 181 count_generation_collidecount(roulette,Cluster_size*2) 182 c=0 183 while c<Cluster_size*2: 184 totalfitness+=roulette[c] 185 c+=1 186 signal=the_answer(roulette,Cluster_size*2) 187 188 while True: 189 ball =rndn(totalfitness) 190 acc=0 191 c=0 192 while c<Cluster_size*2: 193 acc+=roulette[c] 194 if acc>ball: 195 break 196 c+=1 197 judge=judge_reapt(c,Cluster_size) 198 if judge==True: 199 break 200 #/ *染色體的複製 * / 201 for j in range(N): 202 array[i][j]=narray[c][j] 203 204 for q in range(Cluster_size*2): 205 if roulette[q]>max: 206 max =roulette[q] 207 max_generation = generation 208 for i in range(N): 209 max_array[i]=narray[q][i] 210 print(roulette[q], end=" ") 211 print() 212 213 214 215 216 217 218 def judgein(m,location1,location2): 219 i=location1 220 while i<=location2: 221 if ((m == rember.key[i]) | (m == rember.values[i])): 222 return i 223 i+=1 224 return -1 225 226 227 228 229 230 231 # /************************/ 232 # /* crossing()函式 */ 233 # /* 特定2染色體的交叉 */ 234 # /************************/ 235 def crossing(mama, papa,position): 236 while True: 237 while True: 238 cp1 = rndn(N) 239 cp2 = rndn(N) 240 if cp1 != cp2: 241 break 242 # print("cp1="+str(cp1)+"cp2="+str(cp2)) 243 if cp1<cp2: 244 location1 = cp1 245 location2 = cp2 246 else: 247 location1 = cp2 248 location2 = cp1 249 250 i = location1 251 while i<=location2: 252 rember.key[i] = array[mama][i] 253 rember.values[i] = array[papa][i] 254 # 交換中間段 255 narray[position*2][i] = array[papa][i] 256 narray[position*2+1][i] = array[mama][i] 257 i+=1 258 # 利用對應關係,對染色體mama和papa, 中間段外的基因進行交換 259 #/ * 交換前半部分 * / 260 for j in range(location1): 261 weizhi = judgein(array[mama][j],location1,location2) 262 # print("weizhi= "+str(weizhi)) 263 if (weizhi == -1): 264 narray[position*2][j] = array[mama][j] 265 else: 266 if (array[mama][j] == rember.key[weizhi]): 267 narray[position*2][j] = rember.values[weizhi] 268 else: 269 narray[position*2][j] = rember.key[weizhi] 270 weizhi = judgein(array[papa][j], location1, location2) 271 if (weizhi == -1): 272 narray[position*2+1][j] = array[papa][j] 273 else: 274 if (array[papa][j] == rember.key[weizhi]): 275 narray[position*2+1][j] = rember.values[weizhi] 276 else: 277 narray[position*2+1][j] = rember.key[weizhi] 278 279 280 #/ *交換後半部分 * / 281 j = location2+1 282 while j<N: 283 weizhi = judgein(array[mama][j], location1, location2) 284 if (weizhi == -1): 285 narray[position*2][j] = array[mama][j] 286 else: 287 if (array[mama][j] == rember.key[weizhi]): 288 narray[position*2][j] = rember.values[weizhi] 289 else: 290 narray[position*2][j] = rember.key[weizhi] 291 weizhi = judgein(array[papa][j],location1,location2) 292 if (weizhi == -1): 293 narray[position*2+1][j] = array[papa][j] 294 else: 295 if (array[papa][j] == rember.key[weizhi]): 296 narray[position*2+1][j] = rember.values[weizhi] 297 else: 298 narray[position*2+1][j] = rember.key[weizhi] 299 j+=1 300 301 signal = takeoutrepeat(position) 302 # print("--------------signal= "+str(signal)) 303 if (signal != False): 304 break 305 306 307 308 309 310 311 # /************************/ 312 # /* notval()函式 */ 313 # /* */ 314 # /************************/ 315 def notval(i): 316 while True: 317 position1 =rndn(N) 318 position2 =rndn(N) 319 if position1 != position2: 320 break 321 temp=narray[i][position2] 322 narray[i][position2] = narray[i][position1] 323 narray[i][position1] =temp 324 325 326 327 328 # /***********************/ 329 # /* mutation()函式 */ 330 # /* 突變 */ 331 # /***********************/ 332 def mutation(): 333 for i in range(Cluster_size*2): 334 if (rndn(100) / 100 <= MRATE): 335 #/ *染色體突變 * / 336 notval(i) 337 print("mutation is complete") 338 339 340 341 342 343 def mating(): 344 totalfitness =0 345 roulette=numpy.zeros(Cluster_size).astype(int) 346 print(len(roulette)) 347 # 生成輪盤 348 for i in range(Cluster_size): 349 roulette[i] = values[i] 350 totalfitness += roulette[i] 351 # 選擇和交叉的迴圈 352 for i in range(Cluster_size): 353 while True: 354 mama=selectp(roulette,totalfitness) 355 papa=selectp(roulette,totalfitness) 356 if mama != papa: 357 break 358 # 特定2染色體的交叉 359 crossing(mama,papa,i) 360 361 362 363 def outputrember(): 364 for i in range(N): 365 print("key= "+rember.key[i]+"values= "+rember.values[i]) 366 367 368 369 370 def outputnarray(): 371 for i in range(Cluster_size*2): 372 if (i % 2 ==0): 373 print("------------------------------") 374 for j in range(N): 375 print(narray[i][j],end=" ") 376 print() 377 378 379 380 381 def output(): 382 for i in range(Cluster_size): 383 # if values[i]>max: 384 # max =values[i] 385 # max_generation=max_generation 386 print(values[i],end=" ") 387 print() 388 389 390 391 392 def outputarray(): 393 for i in range(Cluster_size): 394 for j in range(8): 395 print(array[i][j], end =" ") 396 print() 397 398 399 400 401 402 def init_Cluster(): 403 print('fsadsgagasgs') 404 a=[0 for n in range(8)] 405 count=0 406 while count < Cluster_size: 407 for y in range(8): 408 x= random.randint(0,7) #產生0到7的隨機數 409 a[y]=x 410 if(judge(a,8)): 411 for i in range(8): 412 array[count][i]=a[i] 413 else: 414 count=count-1 415 count+=1 416 417 418 419 420 421 def main(): 422 global generation 423 init_Cluster() 424 while generation<LASTG: 425 if signal!=-1: 426 break 427 else: 428 print("代數= "+str(generation)) 429 count_collidecount() 430 print("-------------output------------values--------------------------") 431 output() 432 print("------------- outputarray--------------------------------------") 433 outputarray() 434 mating() 435 print("------------- outputarray--------------------------------------") 436 outputarray() 437 print("-----------------mating選擇交叉---------outputnarray---------------") 438 outputnarray() 439 mutation() 440 print("-----------------mutation變異---------outputnarray---------------") 441 outputnarray() 442 print("------------- outputarray--------------------------------------") 443 outputarray() 444 copy_array() 445 # print("---------------------------------------------------------------") 446 # output_copy_array() 447 selection() 448 print("-----------------selection選擇下一代--------outputarray---------------") 449 outputarray() 450 generation+=1 451 print("signal = "+str(signal)+" max ="+str(max)+" max_generation = "+str(max_generation)) #max為最佳值, max_generation為產生最佳值的代數 452 print(max_array,end=" ") 453 454 455 if __name__ == "__main__": 456 main()