遺傳算法詳解及c++實現


1、什么是遺傳算法?

遺傳算法是模擬達爾文生物進化論的自然選擇和遺傳學機理的生物進化過程的計算模型,是一種通過模擬自然進化過程搜索最優解的方法。遺傳算法是從代表問題可能潛在的解集的一個種群開始的,而一個種群則由經過基因編碼的一定數目的個體組成。每個個體實際上是染色體帶有特征的實體。染色體作為遺傳物質的主要載體,即多個基因的集合,其內部表現(即基因型)是某種基因組合,它決定了個體的形狀的外部表現,如黑頭發的特征是由染色體中控制這一特征的某種基因組合決定的。因此,在一開始需要實現從表現型到基因型的映射即編碼工作。由於仿照基因編碼的工作很復雜,我們往往進行簡化,如二進制編碼,初代種群產生之后,按照適者生存和優勝劣汰的原理,逐代演化產生出越來越好的近似解,在每一代,根據問題域中個體的適應度大小選擇比較優秀的個體,並借助於自然遺傳學的遺傳算子進行交叉和變異,產生出代表新的解集的種群。這個過程將導致種群像自然進化一樣的后生代種群比前代更加適應於環境,末代種群中的最優個體經過解碼,可以作為問題近似最優解。

2、遺傳算法的運算過程

(a)初始化:設置進化代數計數器t=0,設置最大進化代數T,隨機生成M個個體作為初始群體P(t)。
(b)個體評價:計算群體P(t)中各個個體的適應度
(c)選擇運算:將選擇算子作用於群體。選擇的目的是把優化的個體直接遺傳到下一代或通過配對交叉產生新的個體再遺傳到下一代。選擇操作是建立在群體中個體的適應度評估基礎上的。
(d)交叉運算:將交叉算子作用於群體。遺傳算法中起核心作用的就是交叉算子。
(e)變異運算:將變異算子作用於群體。即是對群體中的個體串的某些基因座上的基因值作變動。群體P(t)經過選擇、交叉、變異運算之后得到下一代群體P(t+1)。
(f)終止條件判斷:若t=T,則以進化過程中所得到的具有最大適應度個體作為最優解輸出,終止計算。

3、遺傳算法基本框架

 

4、相關術語

基因型(genotype):性狀染色體的內部表現;

表現型(phenotype):染色體決定的性狀的外部表現,或者說,根據基因型形成的個體的外部表現;

進化(evolution):種群逐漸適應生存環境,品質不斷得到改良。生物的進化是以種群的形式進行的。

適應度(fitness):度量某個物種對於生存環境的適應程度。

選擇(selection):以一定的概率從種群中選擇若干個個體。一般,選擇過程是一種基於適應度的優勝劣汰的過程。

復制(reproduction):細胞分裂時,遺傳物質DNA通過復制而轉移到新產生的細胞中,新細胞就繼承了舊細胞的基因。

交叉(crossover):兩個染色體的某一相同位置處DNA被切斷,前后兩串分別交叉組合形成兩個新的染色體。也稱基因重組或雜交;

變異(mutation):復制時可能(很小的概率)產生某些復制差錯,變異產生新的染色體,表現出新的性狀。

編碼(coding):DNA中遺傳信息在一個長鏈上按一定的模式排列。遺傳編碼可看作從表現型到基因型的映射。

解碼(decoding):基因型到表現型的映射。

個體(individual):指染色體帶有特征的實體;

種群(population):個體的集合,該集合內個體數稱為種群

 

5、遺傳算法的應用實例

現在我以一個多元函數最值問題求解來一步步闡述遺傳算法的實現過程。

問題:求一個多元函數的最大值,f(x1,x2) = 21.5+x1*sin(4pi*x1)+x2*sin(20pi*x2),其中-3.0≤x1≤12.1,4.1≤x2≤5.8。

6、設計與實現

(a)前提條件:

const int Po_Size = 50;//種群規模
const int Ev_Algebra = 500;//進化代數
const double Ov_Probability = 0.850; //交叉概率,交叉概率用於判斷兩兩個體是否需要交叉
const double Va_Probability = 0.050;//變異概率,變異概率用於判斷任一個體是否需要變異
const int De_Variable = 2;//函數自變量的個數,如果要進行多元函數的最值求解,直接修改自變量數個De_Variable即可

vector<Individual> nowpopulation;//P(t)種群
vector<Individual> midpopulation;//中間種群,存放輪盤選擇后的優秀個體
vector<Individual> nextpopulation;//P(t+1)種群

 

(b)類的實現:

class Individual //定義個體類
{
private:
double Variable[De_Variable];//存放變量x1,x2,x3........
double Fitness;//適應值
double ReFitness;//適應值概率
double SumFitness;//累加概率,為輪盤轉做准備
public:

............

};

(c):初始化:

隨機生成兩個滿足條件范圍的x1和x2自變量,生成一個個體,初始化種群,得到第一代種群。在這里用一個隨機函數生成兩個指定范圍的小數,然后創建一個對象(個體),加入到初始種群中,直到達到種群規模。

(d)計算適應值,適應值概率,累加概率:
適應值是根據每個個體的自變量進行計算的,這里是求函數的最大值,所以把函數就作為適應值計算函數,適應值概率的計算是每個個體占所有個體適應值總和的百分比,累加概率是為了輪盤法選擇做准備。

(e)進行選擇,雜交,變異:

最重要的就是這三個算子,選擇即就是用輪盤法選出一些相對優秀的個體,在程序中我沒有用精英制選擇,讀者自己可以加以實現。雜交之前需要對自變量進行編碼,我在程序中采用的是二進制的編碼,用的是c++標准庫中的bitset類來進行編碼的,雜交的方式我用的是單點雜交,你也可以選擇其他雜交方式。變異我用的是基本位變異方式,相對比較簡單。

c++code:

GeneticAlgorithm.h文件

 1 //功能:求一個多元函數的最大值(這里是求二元函數的最大值:f(x1,x2) = 21.5+x1*sin(4pi*x1)+x2*sin(20pi*x2))
 2 #pragma once//保證文件只被編譯一次
 3 #include<random>
 4 #include<vector>
 5 #include<iostream>
 6 #include<cmath>
 7 #include<ctime>
 8 #include <cstdlib>
 9 #include <bitset>
10 #include<iomanip>
11 using namespace std;
12 const double PI = 3.141592653589793;//定義一個不可改變的常量值PI
13 const int Po_Size = 50;//種群規模
14 const int Ev_Algebra = 500;//進化代數
15 const double Ov_Probability = 0.850; //交叉概率,交叉概率用於判斷兩兩個體是否需要交叉
16 const double Va_Probability = 0.050;//變異概率,變異概率用於判斷任一個體是否需要變異
17 const int De_Variable = 2;//函數自變量的個數,如果要進行多元函數的最值求解,直接修改自變量數個De_Variable即可
18 const int length1 = 24;//精確到6位小數,用24位二進制編碼
19 const int length2 = 23;//精確到6位小數,用23位二進制編碼
20 class X_Range //自變量取值范圍類,適用於多個變量
21 {
22 private:
23     double Upper;//變量的上界取值
24     double Lower;//變量的下界取值
25 public:
26     X_Range(double m_Upper, double m_Lower);//構造函數
27     double GetUpper()const;//獲取變量上限
28     double GetLower()const;//獲取變量下限
29 };
30 class Individual //定義個體類
31 {
32 private:
33     double Variable[De_Variable];//存放變量x1,x2,x3........
34     double Fitness;//適應值
35     double ReFitness;//適應值概率
36     double SumFitness;//累加概率,為輪盤轉做准備
37 public:
38     Individual() {}//默認構造函數
39     Individual(double* m_Variable);//構造函數
40     double* GetVariable();//獲取變量值
41     void ChaFitness(const double m_fitness);//修改適應值
42     void ChaReFitness(const double m_ReFitness);//修改適應值概率
43     void ChaSumFitness(const double m_SumFitness);//修改累加概率
44     double GetFitness()const;//獲取適應值
45     double GetReFitness()const;//獲取適應值概率
46     double GetSumFitness()const;//獲取累加概率
47 };
48 void Initialize();//隨機初始化種群,得到第一代個體 
49 void CaculaFitness();//計算個體的適應值
50 void CaculaReFitness();//計算個體的適應值概率
51 void CalculaSumFitness();//計算累加個體概率
52 void seclect();//種群選擇
53 double Scand();//隨機產生0到49的隨機整數
54 void crossing();//雜交
55 void variating();//變異
56 void genetic_algorithm();//遺傳算法

GeneticAlgorithm.cpp文件

  1 #include"GeneticAlgorithm.h"//包含頭文件
  2 //自變量取值范圍向量和種群向量定義
  3 const X_Range Range[De_Variable] = { X_Range(-3.0,12.1) ,X_Range(4.1,5.8) };//自變量(或者基因)x1,x2的取值范圍
  4 vector<Individual> nowpopulation;//P(t)種群
  5 vector<Individual> midpopulation;//中間種群,存放輪盤選擇后的優秀個體
  6 vector<Individual> nextpopulation;//P(t+1)種群
  7 //X_Range類實現
  8 X_Range::X_Range(double m_Lower, double m_Upper) :Lower(m_Lower), Upper(m_Upper){}//X_Range類構造函數實現
  9 double X_Range::GetUpper()const//獲取變量上限
 10 {
 11     return Upper;
 12 }
 13 double X_Range::GetLower()const//獲取變量下限
 14 {
 15     return Lower;
 16 }
 17 //Individual類實現
 18 Individual::Individual(double* m_Variable)//構造函數
 19 {
 20     for (int i = 0; i < De_Variable; i++)//用for循環自變量逐個賦值
 21     {
 22             if (m_Variable[i] >= Range[i].GetLower() && m_Variable[i] <= Range[i].GetUpper())//這里要進行自變量取值范圍判斷 
 23             {
 24                 Variable[i] = m_Variable[i];//自變量賦值
 25             }
 26             else//不滿足要求則發出出錯警告並返回
 27             {
 28                 cerr << "自變量取值不滿足要求" << endl;
 29                 exit(1);//停止程序,我會以隨機函數的方式生成自變量的值(基因值),這里說明基因值不在規定范圍內
 30             }
 31     }
 32     //初始化時默認適應值等值為0
 33     this->Fitness = 0;
 34     this->ReFitness = 0;
 35     this->SumFitness = 0;
 36 }
 37 double* Individual::GetVariable()//獲取基因值
 38 {
 39     return Variable;
 40 }
 41 double Individual::GetFitness()const//獲取適應值
 42 {
 43     return Fitness;
 44 }
 45 double Individual::GetReFitness()const //獲取適應值概率
 46 {
 47     return ReFitness;
 48 }
 49 double Individual::GetSumFitness()const//獲取累加概率
 50 {
 51     return SumFitness;
 52 }
 53 void Individual::ChaFitness(const double m_fitness)//修改適應值
 54 {
 55     this->Fitness = m_fitness;
 56 }
 57 void Individual::ChaReFitness(const double m_ReFitness)//修改適應值概率
 58 {
 59     this->ReFitness = m_ReFitness;
 60 }
 61 void Individual::ChaSumFitness(const double m_SumFitness)//修改累加概率
 62 {
 63     this->SumFitness = m_SumFitness;
 64 }
 65 //遺傳算法的准備工作
 66 void Initialize()//隨機初始化種群,得到第一代種群
 67 {
 68 //產生指定范圍的隨機變量(基因)
 69     double X[Po_Size][De_Variable];//為了使程序可以滿足多元函數最值的計算,用矩陣保存產生的隨機數變量值
 70     for (int j = 0; j < De_Variable; j++)
 71     {
 72         default_random_engine e(time(0));//引擎,生成隨機序列
 73         uniform_real_distribution<double> u(Range[j].GetLower(), Range[j].GetUpper());//分布
 74         for (int i = 0; i < Po_Size; i++)//先按列存儲隨機數
 75         {
 76             X[i][j] = u(e);//循環結束時,所有隨機值就保存在X矩陣中
 77         }
 78     }
 79 //生成對象(染色體)並加入到初始種群中
 80     for (int i = 0; i < Po_Size; i++)
 81     {
 82         double variable[De_Variable];
 83         for (int j = 0; j < De_Variable; j++)
 84         {
 85             variable[j] = X[i][j];//按行保存
 86         }
 87         Individual Indivi(variable);//生成一個對象(染色體)
 88         nowpopulation.push_back(Indivi);//加入到種群population中
 89     }
 90 }
 91 void CaculaFitness()//計算個體的適應值
 92 {
 93     //f(x1,x2) = 21.5+x1*sin(4pi*x1)+x2*sin(20pi*x2))為適應度計算函數
 94     double fitness = 0;//臨時適應值
 95     double x[De_Variable];//臨時存儲自變量(基因)
 96     for (int i = 0; i < Po_Size; i++)
 97     {
 98         for (int j = 0; j < De_Variable; j++)
 99             x[j] = nowpopulation.at(i).GetVariable()[j];//這樣更直觀
100         fitness = 21.5 + x[0] * sin(4 * PI*x[0]) + 2 * sin(20 * PI*x[1]);//適應度計算
101         nowpopulation.at(i).ChaFitness(fitness);//修改當前染色體的適應值
102     }
103 }
104 void CaculaReFitness()//計算適應值概率
105 {
106     double sum = 0;//適應值累加器
107     double temp = 0;
108     for (int i = 0; i < Po_Size; i++)//計算出適應值之和
109     {
110         sum += nowpopulation.at(i).GetFitness();
111     }
112     for (int j = 0; j < Po_Size; j++)
113     {
114         temp = nowpopulation.at(j).GetFitness() / sum;//計算概率
115         nowpopulation.at(j).ChaReFitness(temp);//修改個體的適應度概率
116     }
117 }
118 void CalculaSumFitness()//計算累加個體概率
119 {
120     double summation = 0;//累加器
121     for (int k = 0; k < Po_Size; k++)
122     {
123         summation += nowpopulation.at(k).GetReFitness();
124         nowpopulation.at(k).ChaSumFitness(summation);//當前累加結果賦值
125     }
126 }
127 void seclect() //種群選擇
128 {
129     //隨機生生成0到1的小數
130     double array[Po_Size];//隨機數保存變量
131     default_random_engine e(time(0));//引擎,生成隨機序列
132     uniform_real_distribution<double> u(0.0, 1.0); //分布
133     for (int i = 0; i < Po_Size; i++)
134         array[i] = u(e);
135     //輪盤進行選擇
136     for (int j = 0; j < Po_Size; j++)
137     {
138         for (int i = 1; i < Po_Size; i++)
139         {
140             if (array[j] < nowpopulation[i - 1].GetSumFitness())
141             {
142                 midpopulation.push_back(nowpopulation.at(i - 1));//加入到中間種群
143             }
144             if (array[j] >= nowpopulation.at(i - 1).GetSumFitness() && array[j] <= nowpopulation.at(i).GetSumFitness())
145             {
146                 midpopulation.push_back(nowpopulation.at(i));//加入到中間種群
147             }
148         }
149     }
150     nowpopulation.clear();//清空nowpopulation
151 } 
152 double Scand() //隨機產生0到1的小數
153 {
154     int N = rand() % 999;
155     return double(N)/1000.0;;//隨機產生0到1的小數
156 }
157 void crossing()//雜交
158 {
159     int num = 0;//記錄次數
160     double corss = 0.0;//保存隨機產生的概率值
161     srand((unsigned)time(NULL));//根據系統時間設置隨機數種子,設置一次隨機種子就行
162     double array1[De_Variable], array2[De_Variable];//臨時存儲父親和母親的變量值
163     while (num< Po_Size-1)//個體1與個體2雜交,個體3與個體4雜交......個體i和個體i+1雜交
164     {
165         //判斷雙親是否需要雜交,隨機生成一個0到1的小數,如果這個數大於雜交概率,則放棄雜交,直接遺傳給下一代,否則,對父母體進行雜交
166         corss = Scand();
167         if (corss <= Ov_Probability)//如果corss小於等於雜交概率Ov_Probability就進行單點雜交
168         {
169             //首先尋找對應下標的個體並且保存
170             for (int i = 0; i < De_Variable; i++)
171             {
172                 array1[i] = midpopulation.at(num).GetVariable()[i];//父親的自變量
173                 array2[i] = midpopulation.at(num + 1).GetVariable()[i];//母親自變量
174             }
175             int localx1, localx2;//記錄基因交叉點的位置
176             int corssx1[length1], corssx2[length2];//作為交換基因的數組
177             double newx1[2], newx2[2];//分別用來保存基因交換后所對應自變量值
178             bool p1 = true, p2 = true;
179             //然后對雙親變量進行編碼並且進行單點雜交
180             for (int j = 0; j < De_Variable; j++)//array1的x1編碼之后和array2的x1編碼后進行單點雜交,以此類推
181             {
182                 if (j == 0)//x1進行編碼並且雜交
183                 {
184                     bitset<length1> array1b1((array1[j] + 3.0)* pow(10, 6));//加上3.0形成一個unsigaed數之后在進行母體1的x1編碼
185                     bitset<length1> array2b1((array2[j] + 3.0)* pow(10, 6));//加上3.0形成一個unsigaed數之后在進行母體2的x1編碼        
186                     //現在隨機生成0到length1-1的數,確定交叉點的位置
187                     localx1 = rand() % length1;
188                     //現在進行單點交叉,交換雙親localx1后面的基因
189                     for (int i = 0; i < localx1; i++)
190                         corssx1[i] = array1b1[i];
191                     for (int k = 0; k < localx1; k++)
192                         array1b1[k] = array2b1[k];
193                     for (int s = 0; s < localx1; s++)
194                         array2b1[s] = corssx1[s];
195                     //新值保存在newx1數組中,x1基因完成單點雜交操作
196                     newx1[0] = double(array1b1.to_ullong()) / pow(10, 6) - 3.0;
197                     newx2[0] = double(array2b1.to_ullong()) / pow(10, 6) - 3.0;
198                     //對新產生的值進行判斷,判斷是否超出范圍,如果超出范圍則不雜交
199                     if (newx1[0]< Range[0].GetLower() || newx1[0]>Range[0].GetUpper() || newx2[0]<Range[0].GetLower() || newx2[0]>Range[0].GetUpper())
200                     {
201                         p1 = false;
202                         break;
203                     }
204                 }
205                 if (j == 1)//x2進行編碼並且雜交
206                 {
207                     bitset<length2> array1b2((array1[j]) * pow(10, 6));//母體1的x2編碼
208                     bitset<length2> array2b2((array2[j]) * pow(10, 6));//母體2的x2編碼
209                     //現在隨機生成0到length2-1的數,確定交叉點的位置
210                     localx2 = rand() % length2;
211                     //現在進行單點交叉,交換雙親localx2后面的基因
212                     for (int i = 0; i < localx2; i++)
213                         corssx2[i] = array1b2[i];
214                     for (int k = 0; k < localx2; k++)
215                         array1b2[k] = array2b2[k];
216                     for (int s = 0; s < localx2; s++)
217                         array2b2[s] = corssx2[s];
218                     //新值保存在newx2數組中,x2基因完成單點雜交操作
219                     newx1[1] = double(array1b2.to_ullong()) / pow(10, 6);
220                     newx2[1] = double(array2b2.to_ullong()) / pow(10, 6);
221                     //對新產生的值進行判斷,判斷是否超出范圍,如果超出范圍則不雜交
222                     if (newx1[1]< Range[1].GetLower() || newx1[1]>Range[1].GetUpper() || newx2[1]<Range[1].GetLower() || newx2[1]>Range[1].GetUpper())
223                     {
224                         p2 = false;
225                         break;
226                     }
227                 }
228             }
229             if (p1 == true && p2 == true)
230             {
231                 Individual newchiled1(newx1);
232                 Individual newchiled2(newx2);
233                 nextpopulation.push_back(newchiled1);
234                 nextpopulation.push_back(newchiled2);
235             }
236             else//將原來的個體遺傳給下一代
237             {
238                 nextpopulation.push_back(midpopulation.at(num));
239                 nextpopulation.push_back(midpopulation.at(num + 1));
240             }
241         }
242         else//否則直接遺傳給下一代nextpopulation
243         {
244             nextpopulation.push_back(midpopulation.at(num));//生成一個新的個體並且加入到nextpopulation中
245             nextpopulation.push_back(midpopulation.at(num + 1));
246         }    
247         num += 2;
248     }
249     midpopulation.clear();//清空midpopulation
250 }
251 void variating()//變異
252 {
253     int num = 0;
254     while (num<Po_Size)
255     {
256         double variation = Scand();//隨機產生一個0到1的小數,用於判斷是否進行變異
257         if (variation <= Va_Probability)//如果variation小於變異系數,則需要進行變異
258         {
259             double x[2];
260             bool p = true;
261             int x1local, x2local;
262             x[0] = nextpopulation.at(num).GetVariable()[0];
263             x[1] = nextpopulation.at(num).GetVariable()[1];
264             bitset<length1> array1((x[0]+ 3.0)* pow(10, 6));//x1編碼
265             bitset<length2> array2(x[1]*pow(10,6));//x2編碼
266             x1local = rand() % length1;//array1該位取反
267             x2local = rand() % length2;//array2該位取反
268             array1.flip(x1local);//改變array1 x1local位的狀態
269             array2.flip(x2local);//改變array2 x2local位的狀態
270             x[0] = double(array1.to_ullong()) / pow(10, 6) - 3.0;
271             x[1] = double(array2.to_ullong()) / pow(10, 6);
272             //判斷是否符合條件
273             if (x[0]< Range[0].GetLower() || x[0]>Range[0].GetUpper() || x[1]<Range[1].GetLower() || x[1]>Range[1].GetUpper())
274                 p = false;
275             if (!p)
276                 nowpopulation.push_back(nextpopulation.at(num));
277             if (p)
278             {
279                 Individual newchiled(x);
280                 nowpopulation.push_back(newchiled);
281             }
282         }
283         else
284             nowpopulation.push_back(nextpopulation.at(num));
285         num++;
286     }
287     nextpopulation.clear();//清空nextpopulation
288 }
289 void genetic_algorithm() 
290 {
291     Initialize();//初始化種群,隨機生成第一代個體
292     //進化500代
293     for (int i = 0; i < Ev_Algebra; i++)
294     {
295         CaculaFitness();//適應度計算
296         CaculaReFitness();//適應度概率計算
297         CalculaSumFitness();//計算累加個體概率
298         seclect();//選擇
299         crossing();//雜交
300         variating();//變異
301     }
302     CaculaFitness();//適應度計算
303     double maxfitness=nowpopulation.at(0).GetFitness();
304     int maxid = 0;
305     int k;
306     for (k = 0; k < Po_Size; k++)
307     {
308         if (maxfitness < nowpopulation.at(k).GetFitness())
309         {
310             maxfitness = nowpopulation.at(k).GetFitness();
311             maxid = k;
312         }    
313     }
314     //進化500代之后輸出
315     cout << "x1"<<setw(10)<<"x2" << setw(15)<<"Fitness" << endl;
316     for (int j = 0; j < Po_Size; j++)
317         cout << nowpopulation.at(j).GetVariable()[0] <<setw(10)<< nowpopulation.at(j).GetVariable()[1] << setw(10) <<nowpopulation.at(j).GetFitness() << endl;
318     cout << "x1=" << nowpopulation.at(maxid).GetVariable()[0] << "" << "x2=" << nowpopulation.at(maxid).GetVariable()[1] << "時取得最大值:" << maxfitness << endl;
319 }

main.cpp文件

1 #include"GeneticAlgorithm.h"
2 int main()
3 {
4     genetic_algorithm();
5     system("pause");
6     return 0;
7 }

結果:

遺傳算法的細節我就沒有過多的介紹,上面的代碼是本人根據f(x1,x2) = 21.5+x1*sin(4pi*x1)+x2*sin(20pi*x2),其中-3.0≤x1≤12.1,4.1≤x2≤5.8寫的,如果有不合理的地方,麻煩提出來,謝謝大家!

 


免責聲明!

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



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