1. 程式人生 > 實用技巧 >八皇后問題遺傳演算法實現(C語言)

八皇后問題遺傳演算法實現(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 個體評價
染色體通常表示了問題的可行解, 對可行解進行遺傳操作尋找最優解。但在八皇后問題中,染色體僅僅體現同行同列中未出現互攻, 在對角線上是否出現互攻還未做考慮。在對皇后的位置做比較的時候,

可以對兩個棋子的行數差與列數差進行對比, 實現了互攻次數的統計。公式為:|絕對值((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 #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 }  

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