演化計算簡單實例(附代碼)


版權聲明:本文為博主原創文章,轉載請注明出處。

本周的任務一個是搞JavaWeb的學習,一個是搞演化計算的學習,可惜的是JavaWeb的東西沒搞出來,現在還是一團亂麻,演化計算終於是寫出個小程序,但是也算是對演化計算有了初步的了解,所以這次就把演化計算的心得拿出來說說,用以本周總結和搞日后的回顧。

一、演化計算的基本步驟

  1. 根據需求制定編碼方案,我的理解中編碼其實就是確定染色體的形式,這個編碼方案是整個計算的基礎,變異雜交都是基於編碼(染色體)之上的,所以首先就應該制定合理的編碼方案。

   編碼方案有很多種,最簡單的是二進制編碼,也是目前我唯一能理解的編碼形式。所謂二進制編碼就是用一串01的序列作為染色體,這種編碼某種意義上講和自然界真實的染色體是很想相似的,可以看做01是染色體顆粒,非常方便進行變異和交叉。二進制編碼是一種很自然的編碼方式。

   還有gray編碼,實數編碼,有序位串編碼結構式編碼等等其他形式,編碼會影響到之后的效率,其他的編碼形式日后再講。

  2.確定適應函數。適應函數就是描述一個個體的好壞,用以進行選擇,適應函數也分原始適應函數和標准適應函數。

  3.確定選擇策略。選擇策略是重中之重,因為這決定了進化的方向,選擇函數就是自然界中的各種災難和競爭,只有實力強大的和幸運的(?)才能存留下來,看來果然運氣也是實力的一部分。

  選擇策略也是多種多樣的,但是關注的點不太一樣,包括進化的速度,是否會陷入局部最優,群體的數目大小等等,具體的策略有繁殖池選擇,轉輪盤選擇,線性排名選擇,非線性排名選擇錦標賽選擇等等。

  4.控制參數的選擇。就是根據具體問題決定算法中種群的規模,最大代數,一些遺傳操作的概率和輔助性的參數的設置,看起來是比較微小的東西,但是像秤砣一樣左右着整個計算的結果。

  5.遺傳算子的設計。遺傳算子主要包括繁殖,雜交,變異,是對於自然的進一步模擬,能夠優化算法的性能,通過引入隨機性來克服程序化的問題。

  6.確定終止的准則。

二、一個簡單的實例

    f(x1,x2)=x1^2+x2^2,x1和x2是屬於0~7的正整數,求f的最大值。

這種弱智問題當然一眼就可以看出答案,但是就是要用遺傳算法來實現,用以熟悉相關的操作和步驟,下面開始介紹我的想法。(哦,這是用C++寫的)

1.編碼:這個0~7簡直就是為了二進制編碼設計的,對x1,x2分別用三位二進制數來表示就可以了

2.適應函數:由於是求f的最大值,就是說f越大越好,所以就直接拿f來做適應函數來評價x1,x2.

3.選擇策略:本例中我用的是輪盤賭的策略,所謂輪盤賭就是讓每一個個體根據某種和為1的概率被挑選,挑中了就留下,否則就淘汰,這個概率就是該個體適應值占總適應值的比例。也就是說適應值越高的越有可能被選中(當然,個體越多的也越容易被選中),但是夠幸運的個體即使概率很低也有機會存留下來。被挑中存留的個體才有機會參加之后的繁殖雜交突變等等,其實就相當於自然界中的種群越是厲害的越有機會留下來,但是厲害的也可能陰差陽錯就被淘汰了(比如因為站得太高被雷劈死的頭狼?),比較差的也可能碰巧沒被淘汰。

4.控制參數:我設置的是最多一百代,0.6的概率會交叉,0.006的概率會突變,這個突變必須控制在較低的范圍,否則是無法完成進化的,因為選擇了半天,留下了適應度很高的染色體,但是染色體突變率很高,執行突變時又全都給你變沒了,又恢復到隨機的一種情況之下,進化就相當於沒有發生。

5.遺傳算子:我這里有突變和交叉兩個算子,突變是每一條染色體的每一個基因都有Pm的概率取反,交叉是隨機設置交叉點,兩個染色體有一定的概率發生交叉(單點交叉)。

  1 #include<iostream>
  2 #include<string>
  3 #include<time.h>
  4 #include<sstream>
  5 using namespace std;
  6 
  7 
  8 //種群總數
  9 const int popSize=100; 
 10 //染色體長度
 11 const int chromosomeSize=6;
 12 //變異概率
 13 const double Pm=0.001;
 14 //最多代數
 15 const int MaxGen=100;
 16 //變異概率
 17 const double Pc=0.1;
 18 
 19 
 20 
 21 
 22 
 23 //遺傳個體類
 24 class individual
 25 {
 26 public:
 27     //個體目標值
 28     double ObjectValue;
 29     //個體適應值
 30     double FitValue;
 31     //染色體編碼
 32     string Chromosome;
 33     //構造函數
 34     individual()
 35     {
 36         ObjectValue=0;
 37         FitValue=0;
 38         Chromosome="000000";
 39     }
 40 };
 41 
 42 //進化處理類
 43 class Evaluation
 44 {
 45 private:
 46     
 47     //種群
 48     individual Population[popSize];
 49     //進行選擇
 50     void SelectPop();
 51     //進行變異
 52     void VaryPop();
 53     //進行雜交
 54     void CrossPop();
 55     //優化
 56     void OptimizePop();
 57     //初始化種群,隨機構造一個群體
 58     void Initialization();
 59     //找出最優和最差及平均值
 60     void Statistics();
 61     //評價種群
 62     void EvaluatePop();
 63     //最好個體
 64     individual Best;
 65     //最壞個體
 66     individual Worst;
 67     //最壞個體下標
 68     int WorstIndex;
 69     //歷史最佳
 70     individual HistoryBest;
 71     //平均值
 72     double avg;
 73     
 74 public:
 75     //構造函數,調用初始化函數
 76     Evaluation();
 77     //產生下一代
 78     void NextPopulation();
 79     //打印
 80     void output();
 81     //代數
 82     int generation; 
 83 };
 84 
 85 //構造函數,調用初始化函數
 86 Evaluation::Evaluation()
 87 {
 88     Initialization();
 89     generation=0;
 90 
 91 }
 92 
 93 //初始化構造初始種群
 94 void Evaluation::Initialization()
 95 {
 96     //對染色體進行初始化設置,逐顆粒隨機賦值
 97     char temp;
 98     int Index=0,bitIndex=0;
 99     for(;Index<popSize;Index++)
100     {
101         for(bitIndex=0;bitIndex<chromosomeSize;bitIndex++)
102         {
103             int r=rand()%2;
104             if (r==0) 
105                 temp='0';
106             else
107                 temp='1';
108             Population[Index].Chromosome[bitIndex]=temp;
109         }
110     }
111     //初始化時設置歷史最佳為第一個
112     HistoryBest=Population[0];
113     //調用目標值和適應值的初始化函數,評價種群
114     EvaluatePop();
115     Statistics();
116 }
117 
118 //評價函數,就是用函數f(x1,x2)=x1^2+x2^2,因為是數值函數,所以適應值和目標值是一致的
119 void Evaluation::EvaluatePop()
120 {
121     string num1,num2;
122     int value1,value2;
123     int Index=0;
124     for(;Index<popSize;Index++)
125     {
126             num1=Population[Index].Chromosome.substr(0,3);
127             num2=Population[Index].Chromosome.substr(3,3);
128             //二進制轉化為十進制
129             stringstream ss;
130             ss<<num1;
131             ss>>value1;
132             //清空緩沖區
133             ss.clear();
134             ss.str("");
135             ss<<num2;
136             ss>>value2;
137             
138             int a,b,c;
139             a=value1/100;
140             b=(value1-a*100)/10;
141             c=value1-a*100-b*10;
142             value1=a*4+b*2+c;
143             a=value2/100;
144             b=(value2-a*100)/10;
145             c=value2-a*100-b*10;
146             value2=a*4+b*2+c;
147             //計算適應值和目標值
148             Population[Index].FitValue=value1*value1+value2*value2;
149             Population[Index].ObjectValue=value1*value1+value2*value2;
150     }
151 }
152 
153 //生成下一代
154 void Evaluation::NextPopulation()
155 {
156     SelectPop();
157     VaryPop();
158     CrossPop();
159     EvaluatePop();
160     Statistics();
161     OptimizePop();
162     EvaluatePop();
163     Statistics();
164     generation++;
165 }
166 
167 //選擇算子,輪盤賭
168 void Evaluation::SelectPop()
169 {
170     double FitSum=0,selection[popSize];
171     individual newPopulation[popSize];
172     int index=0,popindex=0;
173     //求適應值的總和
174     for(;index<popSize;index++)
175     {
176         FitSum+=Population[index].FitValue;
177     }
178     
179     //確定輪盤分布
180     for(index=0;index<popSize;index++)
181     {
182         selection[index]=Population[index].FitValue/FitSum;
183     }
184     for(index=1;index<popSize;index++)
185     {
186         selection[index]=selection[index]+selection[index-1];
187     }
188     //用輪盤進行隨機選取,形成新的種群
189     for(popindex=0;popindex<popSize;popindex++)
190     {
191         double p=  (rand()%100);
192         p/=100;
193         index=0;
194         while(p>selection[index])
195             index++;
196         newPopulation[popindex]=Population[index];
197     }
198     //將剛產生的群體替換為系統的群體
199     for(index=0;index<popSize;index++)
200     {
201         Population[index]=newPopulation[index];
202     }
203 
204 }
205 
206 //雜交算子,隨機選取交叉點
207 void Evaluation::CrossPop()
208 {
209     int Localtion;
210     int index=0;
211     string str1,str2,str3,str4;
212     //打亂順序
213     for(;index<popSize;index++)
214     {
215         individual temp;
216         int r=rand()%popSize;
217         temp=Population[index];
218         Population[index]=Population[r];
219         Population[r]=temp;
220     }
221     //隨機選取交叉點,將染色體分裂,然后交叉,得到新的染色體
222     for(index=0;index<popSize;index+=2)
223     {
224         double temp=rand()%1000/1000.0;
225         if(temp<Pc)
226         {
227             Localtion=rand()%chromosomeSize;
228             str1=Population[index].Chromosome.substr(0,Localtion);
229             str2=Population[index].Chromosome.substr(Localtion);
230             str3=Population[index+1].Chromosome.substr(0,Localtion);
231             str4=Population[index+1].Chromosome.substr(Localtion);
232             Population[index].Chromosome=str1+str4;
233             Population[index+1].Chromosome=str3+str2;
234         }
235     }
236 }
237 
238 //變異算子,對所有染色體每一位的隨機位置以變異概率進行變異
239 void Evaluation::VaryPop()
240 {
241     int index=0,bitindex=0;
242     string str1="0";
243     string str2="1";
244 
245     for(;index<popSize;index++)
246     {
247         for(bitindex=0;bitindex<chromosomeSize;bitindex++)
248         {
249             double r=rand()%1000;
250             r/=1000;
251             if(r<Pm)
252             {
253                 if(Population[index].Chromosome[bitindex]==str1[0])
254                     Population[index].Chromosome[bitindex]=str2[0];
255                 else
256                     Population[index].Chromosome[bitindex]=str1[0];
257             }
258         }
259     }
260 }
261 
262 //優化
263 void Evaluation::OptimizePop()
264 {
265         Population[WorstIndex] = HistoryBest;  
266 }
267 
268 //統計
269 void Evaluation::Statistics()
270 {
271     Best=Population[0];
272     Worst=Population[0];
273     int index=0;
274     double sum=0;
275     for(;index<popSize;index++)
276     {
277         if(Best.FitValue<Population[index].FitValue)
278             Best=Population[index];
279         if(Worst.FitValue>Population[index].FitValue)
280         {
281             Worst=Population[index];
282             WorstIndex=index;
283         }
284         sum+=Population[index].FitValue;
285     }
286     if(HistoryBest.FitValue<Best.FitValue)
287         HistoryBest=Best;
288     avg=sum/popSize;
289     
290 }
291 
292 
293 //輸出文本
294 void Evaluation::output()
295 {
296     cout<<"Generation:"<<generation<<"   Average:"<<avg<<"   Best:"<<Best.FitValue<<"  Chromosome"<<Best.Chromosome<<endl;
297 }
298 
299 
300 int main()
301 {
302     Evaluation eva;
303     eva.output();
304     while(eva.generation<MaxGen)
305     {
306         eva.NextPopulation();
307         eva.output();
308     }
309 
310 
311 }

結果:

    寫完這個小程序之后我就在思考選擇,交叉,突變的意義是什么,目前的想法是這樣:選擇的目的是在當前種群中尋求當前最優的,只考慮現狀,留下來的趨向於當前群體的最優,但是是不是整體最優是未知的,所以又引入了交叉和突變。

    交叉可以讓優質基因和劣質基因在種群中變得更加均勻,方便於選擇(就是留下好的去除壞的,因為好的和壞的都可能因此顯得更加突出了,而平庸的在選擇中並沒有優勢),同時也會產生新的個體。

    而變異就是為了產生新個體才引入的,事實上我覺得他會降低選擇的速度,因為選擇出的好的個體經過突變很可能就沒有之前那么好了,但是引入新的個體的意義是顯然的,這可以避免局部最優。

為了更好的說明,我們走兩個極端,一是只選擇不變異不交叉,很快這個種群就會被目前最好的個體占據,二是選擇交叉,百分百變異,那么得到的群體幾乎就是一個全新的群體了,選擇已經失去了留下優質基因淘汰劣質基因的能力因為很快又會到原點,選了半天和沒選一樣。所以我目前認為選擇是為了獲得局部最優,交叉和變異是為了一定程度上擴充這個群體的基因庫,努力向全局最優靠近,交叉和突變都會降低選擇的速度,因為被選擇留下的個體發生了變化。

附上不同變異概率的進化結果:

Pm=0

Pm=0.001

Pm=0.01

Pm=0.1

Pm=0.3

0.01以下的還算是進化了,但是隨着Pm的增大,進化到最優的狀態需要的代數也越來越多(即進化速度變慢了),0.1之后的基本上已經陷入無序的狀態,選擇已經沒有用了。

改變了Pc(交叉概率),沒有什么太大的影響。

下周的話盡量了解其他的編碼方式,並嘗試不同的選擇策略,本次總結就到此為止。

時間:2016/10/22


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM