演化计算简单实例(附代码)
版權聲明:本文為博主原創文章,轉載請注明出處。
本周的任務一個是搞JavaWeb的學習,一個是搞演化計算的學習,可惜的是JavaWeb的東西沒搞出來,現在還是一團亂麻,演化計算終于是寫出個小程序,但是也算是對演化計算有了初步的了解,所以這次就把演化計算的心得拿出來說說,用以本周總結和搞日后的回顧。
一、演化計算的基本步驟
編碼方案有很多種,最簡單的是二進制編碼,也是目前我唯一能理解的編碼形式。所謂二進制編碼就是用一串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
轉載于:https://www.cnblogs.com/liyuwang/p/5988358.html
總結
以上是生活随笔為你收集整理的演化计算简单实例(附代码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jQuery Layer 弹层组件
- 下一篇: 刷题向》关于一道比较优秀的递推型DP(o