版權聲明:本文為博主原創文章,轉載請注明出處。
我們來看一個很簡單的小問題f=x1+x2+x3+x4,x1、x2、x3、x4是大於等於10小於等於100的實數,求f的最大值。
這個小學生就能解決的問題我今天打算用遺傳算法來解決,你可能說這不是智障嗎?但是其實這只是一個小例子,因為用同樣的方法,你可以解決f=x1^x2*x3^x4/x2^x1*x4^x3甚至是更復雜的問題,下面就來詳細講一講。
基於對遺傳算法的一般性了解,我就不再贅述詳細過程(其實是因為上一篇寫過了懶得再寫一遍),只談談實數編碼和線性排名選擇策略。
實數編碼顧名思義就是用實數進行編碼,實數來做染色體的基因,實數構成染色體,他的本質其實是用問題的一個解空間來做一個染色體,比如{20.5658.15.2385,89.0000,56.4400},就是上面小問題的一個解空間,就可以把它作為一個染色體用於進化,其中的每一個x1,x2都是一個基因,那些交叉,變異都是基於這樣的設定的。
在這里插一句,實數編碼和整數編碼的思想是極為類似的,但是實數編碼的解空間更大更復雜。
現在來講講實數編碼的交叉和變異
1、交叉
實數編碼的雜交方式有離散雜交,算數雜交等等,本例只講解離散雜交。
離散雜交和二進制的雜交是十分類似的,(可以)選定一個基因位,然后將選定的兩個染色體在這個位置之后的基因進行交換(注意基因的定義區間是不變的)。
注意,在實數編碼中,交叉的作用不是很大。
2、變異
實數編碼的變異包括均勻性變異、正態性變異、非一致性變異、自適應變異、多級變異等,本例只講解自適應變異和非一致性變異。
(1)非一致性變異
在傳統的遺傳算法中,突變的情況是與代數無關的。但是進化剛開始時,就是需要向各個方向大步發展進行嘗試,進化到了后期,解已經相對較優了,進行局部搜索可能更有利於找到更好的解。顯然傳統的方法是不行的,必須找到一種將變異幅度和代數相聯系的策略。
所以給出如下方法:s={v1,v2,v3,v4……},vk被選中進行變異,它的定義區間為{ak,bk},
vk1=vk+h(t,bk-vk); vk2=vk-h(t,vk-ak);
(t為代數,h(t,y)=y*(1-r^(1-t/T)^p),r是0~1的隨機數,T為最大代數,p式一個參數,一般取值為2~5)
新的vk隨機在vk1和vk2中進行選取,這樣就實現了之前提出要求。
(2)自適應性變異
非一致性變異加強了局部搜索能力,但是與解的好壞無關,但是我們可能希望的是好的解搜索范圍較小,壞的解搜索范圍較大這樣,所以在非一致性變異上進行一些修正。
我們只需要將h(t,y)中的t換為T,T是一個與解的質量有關的數,
T=1-f(s)/fmax
f(s)是某個體的適應值,fmax是所解問題的最優結果,當然fmax不太好確定,所以找一個近似替代就可以了,比如當代最優解或是歷史最優解。
3、線性排名選擇策略
基於適應值比例的算法(輪盤賭、繁殖池)比較容易陷入過早收斂和停滯現象,排名選擇策略可以避免這個問題。
我們只介紹線性排名選擇策略。
顧名思義,排名策略嘛,就是要所有的解按照適應值進行排排坐,各自都有一個排名,然后我們按照大家的排名制定一個被選中的概率(然后根據這個概率進行輪盤賭),這就避免了某些個體很多就可以在輪盤賭中獲得很大優勢,變成了強的更有機會,但是弱者的生存機會也沒被剝奪。
被選中的概率為pi=(a-b/(n+1))/n,n為排名,a,b有個一般取值,1≤a≤2,一般取1.1,b=2a-2.
接下來是代碼部分。
1 #include <iostream> 2 #include <string> 3 #include <algorithm> 4 #include <vector> 5 #include <math.h> 6 using namespace std; 7 8 9 //種群總數 10 const int popSize=1000; 11 //執行代數 12 const int maxGen=200; 13 //染色體長度 14 const int chromSize=4; 15 //交叉概率 16 const double Pc=0.7; 17 //變異概率 18 const double Pm=0.3; 19 //線性排名選擇參數1 20 const double Pa=1.1; 21 //線性排名選擇參數2 22 const double Pb=0.2; 23 //變量定義上限 24 const double up=100; 25 //變量定義下限 26 const double down=10; 27 28 29 30 //遺傳個體類 31 class indivadual 32 { 33 public: 34 //構造函數 35 void indivadula(){}; 36 //染色體 37 vector<double> chrom; 38 //目標值 39 double objectValue; 40 //適應值 41 double fitValue; 42 }; 43 44 45 //進化處理類 46 class Evalution 47 { 48 private: 49 //種群 50 vector <indivadual> Population; 51 //最好個體 52 indivadual Best; 53 //最好個體下標 54 int BestIndex; 55 //最壞個體 56 indivadual Worst; 57 //最壞個體下標 58 int WorstIndex; 59 //歷史最佳 60 indivadual HistoryBest; 61 //平均適應值 62 double Avg; 63 //初始化種群 64 void Initialization(); 65 //選擇算子 66 void SeletPop(); 67 //交叉算子 68 void CrossPop(); 69 //變異算子 70 void VaryPop(); 71 //優化 72 void OptimizePop(); 73 //排序輔助函數 74 //bool compare(indivadual a,indivadual b); 75 public: 76 //構造函數,調用初始化函數 77 Evalution(); 78 //評價函數 79 void Evaluate(); 80 //生成下一代函數 81 void NextPopulation(); 82 //輸出 83 void OutPut(); 84 //代數 85 int gen; 86 }; 87 88 Evalution::Evalution() 89 { 90 Initialization(); 91 } 92 93 //初始化種群 94 void Evalution::Initialization() 95 { 96 int i=0,j=0; 97 Population.resize(popSize); 98 for(i=0;i<popSize;i++) 99 { 100 Population[i].chrom.resize(chromSize); 101 for(j=0;j<chromSize;j++) 102 { 103 //隨機值范圍為10-100 104 double r=(rand()%9000)/100.0+10.01; 105 Population[i].chrom[j]=r; 106 } 107 } 108 Best=Population[0]; 109 Best.fitValue=0; 110 Worst=Population[0]; 111 Worst.fitValue=1000; 112 BestIndex=0; 113 WorstIndex=0; 114 gen=0; 115 Avg=0; 116 } 117 118 //評價適應值,目標值,最好個體,最壞個體,歷史最佳個體 119 void Evalution::Evaluate() 120 { 121 122 int i=0,j=0; 123 vector<double> value; 124 value.resize(chromSize); 125 for(i=0;i<popSize;i++) 126 { 127 for (j=0;j<chromSize;j++) 128 { 129 value[j]=Population[i].chrom[j]; 130 } 131 //評估函數為f=(a/b+c/d)/(b/a+d/c); 132 //Population[i].objectValue=Population[i].fitValue=(value[0]/value[1]+value[2]/value[3])/(value[1]/value[0]+value[3]/value[2]); 133 //評估函數為f=a+b+c+d 134 Population[i].objectValue=Population[i].fitValue=value[0]+value[1]+value[2]+value[3]; 135 if (Population[i].fitValue>Best.fitValue) 136 { 137 Best=Population[i]; 138 BestIndex=i; 139 } 140 if (Population[i].fitValue<Worst.fitValue) 141 { 142 Worst=Population[i]; 143 WorstIndex=i; 144 } 145 } 146 if (Best.fitValue>HistoryBest.fitValue) 147 { 148 HistoryBest=Best; 149 } 150 for (i=0;i<popSize;i++) 151 { 152 Avg+=Population[i].fitValue; 153 } 154 Avg/=popSize; 155 } 156 157 158 //生成新一代個體 159 void Evalution::NextPopulation() 160 { 161 SeletPop(); 162 CrossPop(); 163 VaryPop(); 164 Evaluate(); 165 OptimizePop(); 166 } 167 168 //輸出 169 void Evalution::OutPut() 170 { 171 cout<<"當前代數"<<++gen<<" 平均值"<<Avg<<" 最好個體適應值"<<Best.fitValue<<"最好個體基因"; 172 int i=0; 173 for (i=0;i<chromSize;i++) 174 { 175 cout<<Best.chrom[i]<<" "; 176 } 177 cout<<endl; 178 } 179 180 //sort函數的輔助函數 181 bool compare(indivadual a,indivadual b) 182 { 183 if (a.fitValue>b.fitValue) 184 { 185 return true; 186 } 187 if (a.fitValue>b.fitValue) 188 { 189 return false; 190 } 191 return false; 192 } 193 194 //線性排名選擇 195 void Evalution::SeletPop() 196 { 197 sort(Population.begin(),Population.end(),compare); 198 double p[popSize],selection[popSize]; 199 indivadual newPopulation[popSize]; 200 double FitSum=0; 201 int i=0,j=0,index=0,popindex=0; 202 //計算分配概率 203 for (i=0;i<popSize;i++) 204 { 205 j=i+1; 206 p[i]=(Pa-Pb/(j+1))/j; 207 } 208 //求分配概率的總和 209 for(index=0;index<popSize;index++) 210 { 211 FitSum+=p[index]; 212 } 213 214 //確定輪盤分布 215 for(index=0;index<popSize;index++) 216 { 217 selection[index]=p[index]/FitSum; 218 } 219 for(index=1;index<popSize;index++) 220 { 221 selection[index]=selection[index]+selection[index-1]; 222 } 223 //用輪盤進行隨機選取,形成新的種群 224 for(popindex=0;popindex<popSize;popindex++) 225 { 226 double n= (rand()%100)/100.0; 227 index=0; 228 while(n>selection[index]) 229 index++; 230 newPopulation[popindex]=Population[index]; 231 } 232 //將剛產生的群體替換為系統的群體 233 for(index=0;index<popSize;index++) 234 { 235 Population[index]=newPopulation[index]; 236 } 237 } 238 239 240 241 242 //雜交算子,離散雜交 243 void Evalution::CrossPop() 244 { 245 int index=0,position=0,i=0,temp=0,t=0; 246 for(;index<popSize;index++) 247 { 248 indivadual temp; 249 int r=rand()%popSize; 250 temp=Population[index]; 251 Population[index]=Population[r]; 252 Population[r]=temp; 253 } 254 for(index=0;index<popSize;index+=2) 255 { 256 t=rand()%1000/1000.0; 257 if (t<Pc) 258 { 259 position=rand()%chromSize; 260 for (i=position+1;i<chromSize;i++) 261 { 262 temp=Population[index+1].chrom[i]; 263 Population[index+1].chrom[i]=Population[index].chrom[i]; 264 Population[index].chrom[i]=temp; 265 266 } 267 268 } 269 270 } 271 } 272 273 274 //變異算子,自適應性變異 275 void Evalution::VaryPop() 276 { 277 int i=0,j=0; 278 for (i=0;i<popSize;i++) 279 { 280 for (j=0;j<chromSize;j++) 281 { 282 double r=rand()%1000/1000.0; 283 if (r<Pm) 284 { 285 double t=1-Population[i].fitValue*0.9999/Best.fitValue; 286 //突變區間 287 double u=(1-pow(r,pow(t,2)))*(up-Population[i].chrom[j]); 288 if (u>up) 289 { 290 u=up; 291 } 292 if (u<down) 293 { 294 u=down; 295 } 296 double l=(1-pow(r,pow(t,2)))*(Population[i].chrom[j]-down); 297 if (l>up) 298 { 299 l=up; 300 } 301 if (l<down) 302 { 303 l=down; 304 } 305 306 int p=rand()%2; 307 if (p==0) 308 { 309 Population[i].chrom[j]=u; 310 } 311 else 312 Population[i].chrom[j]=l; 313 } 314 } 315 } 316 } 317 318 //優化 319 void Evalution::OptimizePop() 320 { 321 Population[WorstIndex] = HistoryBest; 322 } 323 324 int main() 325 { 326 Evalution eva; 327 eva.Evaluate(); 328 eva.OutPut(); 329 while(eva.gen<maxGen) 330 { 331 eva.NextPopulation(); 332 eva.Evaluate(); 333 eva.OutPut(); 334 } 335 }
這個代碼中是有個問題的,就是優化函數的策略是把歷史最佳替代當代最差,這個可能會容易陷入局部最優。目前想到的更好的策略是如果連續三代的歷史最佳都不變,就對歷史最佳以一個很小的概率進行突變。
附上這個代碼的結果:
是不是覺得有點問題?這個結果不是最優結果啊,最優的結果明明是100,100,100,100,這個結果和最終的結果差距還是比較大的。
這個結果其實某種程度的體現了遺傳算法的根本目的:遺傳算法的目的通過一系列操作最終得到在適應值上更高的群體,而不是為了求最優而存在的,它是一種近似最優的算法,而這個近似的程度和種群的規模,進化的代數,進化的策略都是有關的,所以不能強求通過遺傳算法非要得到最優解。對於實數編碼來講,解空間的范圍實在是非常大,也就是說進化的方向是非常多的,在這個情況下,突變可能已經很難產生新的有利基因了,也就是說算法收斂了,當然這是可以通過算法進行調節的,但是收斂到了什么程度,也是比較難以判斷。
附上不同規模的結果(大規模的遺傳算法非常耗時):
popsize=100
popsize=500
popsize=1000
popsize=5000
popsize=10000
可以看出種群數量對於最終結果的影響是比較大的。
ps.然后說點vector,如果想不初始化就用,就用resize函數定義一個大小,那么會初始化這個大小的空間,然后就可以隨便使用了。