遺傳演算法上機系列之用遺傳演算法求函式最值問題(附自己寫的程式碼)
阿新 • • 發佈:2018-12-19
本文基於下面的最值問題進行求解: 精度要求
遺傳演算法的基本步驟
step1:按某種編碼方式產生初始種群 ,設種群代數;
常用編碼方式:
二進位制編碼、實數編碼、整數編碼、排列編碼、有限狀態機編碼、樹編碼……(本例中我們採用二進位制編碼,需要對應解碼)
二進位制編碼
,精度要求不小於. 編碼公式:,根據取值範圍即可求出共需要多少位二進位制數才能滿足精度要求 解碼公式:(其中為無符號二進位制換算成十進位制之後的值)
本例中,按照精度要求及取值範圍,需要18位二進位制編碼,需要15位二進位制編碼。 以下為初始化部分程式碼:
//初始化種群 void init() { double p=0.5; //srand(time(0)); for(int i=0;i<NUMBER;i++) { string s; for(int j=0;j<33;j++) { double r = rand(); if(r>RAND_MAX/2) s.append("1"); else s.append("0"); } pop.push_back(s); } }
step2:計算種群中每個個體的適應度值;
即按照解碼公式進行解碼之後帶入函式中計算的值,即為本例的適應度值。相關程式碼如下(individual為自定義結構體,包含當前計算個體對應的三個值:
//計算適應度值
individual Fitness(string s)
{
individual T;
double x1a = -3.0;
double x1b = 12.1;
double x2a = 4.1;
double x2b = 5.8;
string s1 = s.substr(0,18);
string s2 = s.substr(18,15);
int t1 = Transform(s1);
int t2 = Transform(s2);
T.x1 = x1a+(x1b-x1a)*t1/(pow(2.0,18)-1);
T.x2 = x2a+(x2b-x2a)*t2/(pow(2.0,15)-1);
T.fit = 21.5+T.x1*sin(4*PI*T.x1) +T.x2*sin(20*PI*T.x2);
return T;
}
step3:按某種規則從中選擇一個子群體作為產生後代的父母,用交叉運算元作用於產生交叉後代集合,再用變異運算元作用於產生變異後代集合,計算中每個後代的適應度;
某種規則:本例使用輪盤賭選擇(根據每個個體適應度所佔總適應度的比例,以及產生隨機數的大小,選擇出一部分個體進入交叉池,這樣適應度大的的個體會有更大的可能性被選為父母進行交叉,優良基因得以保留下去) 下面是輪盤賭選擇部分程式碼:
//輪盤賭選擇
vector<string> choose(vector<string> population,int x)
{
//srand(time(0));
double fitness_sum=0;
vector<double> q;
vector<individual> indi_vec;
vector<string> result;
double p=0;
vector<double> random;
for (int i = 0; i < x; i++)
{
double r = rand();
random.push_back(r / (double)RAND_MAX);
}
for(int i=0;i<population.size();i++)
{
individual Indi=Fitness(population[i]);
indi_vec.push_back(Indi);
fitness_sum=fitness_sum+Indi.fit;
}
for(int i=0;i<population.size();i++)
{
individual Indi=indi_vec[i];
p=p+Indi.fit/fitness_sum;
if(i==population.size()-1)
q.push_back(1.0);
else
q.push_back(p);
}
for(int i=0;i<random.size();i++)
{
for(int j=0;j<q.size();j++)
{
if (random[i]>q[j] && random[i] <= q[j + 1])
{
result.push_back(population[j + 1]);
break;
}
}
}
return result;
}
交叉運算元:單點交叉、兩點交叉、多點交叉、均勻交叉、部分匹配交叉、順序交叉、迴圈交叉、二維交叉…… 本例使用兩點交叉做為交叉運算元,在一對個體上選擇兩個相同的點位,切斷之後進行交換即產生兩個後代,下面是交叉部分程式碼:
//兩點交叉
vector<string> crossover(vector<string>population)
{
//srand(time(0));
vector<string> result;
for(int i=0;i+1<population.size();i=i+2)
{
string s1=population[i];
string s2=population[i+1];
double r = rand();
if (r<pc*RAND_MAX)
{
int i1 = (int)(rand()/RAND_MAX)*33;
int i2 = (int)(rand() / RAND_MAX) * 33;
if(i1<i2)
{
string sub1=s1.substr(i1,i2-i1);
string sub2=s2.substr(i1,i2-i1);
s1.replace(i1,i2-i1,sub2);
s2.replace(i1,i2-i1,sub1);
result.push_back(s1);
result.push_back(s2);
}
else{
if (i1 > i2)
{
string sub1 = s1.substr(i2, i1 - i2);
string sub2 = s2.substr(i2, i1 - i2);
s1.replace(i2, i1 - i2, sub2);
s2.replace(i2, i1 - i2, sub1);
result.push_back(s1);
result.push_back(s2);
}
else
{
result.push_back(s1);
result.push_back(s2);
}
}
}
else
{
result.push_back(s1);
result.push_back(s2);
}
}
return result;
}
變異運算元:單點變異、對換變異、移位變異、插入便宜、均勻變異、正態變異、非一致變異…… 本例使用單點變異作為變異運算元,即產生隨機數,將對應位上的二進位制編碼取反即可,下面為變異部分程式碼:
//單點變異
string mutation(string s)
{
//srand(time(0));
for(int i=0;i<33;i++)
{
double r = rand();
if (r<pm*RAND_MAX)
{
if(s[i]=='0')
s[i]='1';
else
s[i]='0';
}
}
return s;
}
step4:用選擇運算元從集合中選出下一代種群,令;
此步驟同樣用輪盤賭選擇作為選擇運算元選出進入下一代中的個體
step5:如演算法滿足終止條件,則輸出中具有最大適應度值的個體作為最優解,中止演算法。否則,進入步驟2.
終止條件:要麼迭代代數達到限制範圍,要麼很多代後的最好個體沒有繼續改進即可停止。
以下為全部程式碼(一般在執行到300+代時即可達到最優解):
#include "stdafx.h"
#include <string>
#include <time.h>
#include <windows.h>
#include <math.h>
#include <iostream>
#include <vector>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
#define NUMBER 20
#define PI 3.14159265358979323
vector<string> pop;
double pc = 0.5;
double pm = 0.1;
string max_s;
//個體結構體
struct individual
{
double x1=0;
double x2=0;
double fit=0;
};
//初始化種群
void init()
{
double p=0.5;
//srand(time(0));
for(int i=0;i<NUMBER;i++)
{
string s;
for(int j=0;j<33;j++)
{
double r = rand();
if(r>RAND_MAX/2)
s.append("1");
else
s.append("0");
}
pop.push_back(s);
}
}
//二進位制字串轉換成整形
int Transform(string s)
{
int n =0;
for(int i = 0; i<s.size(); i++)
{
n = n*2 + s[i]-'0';
}
return n;
}
//解碼並計算適應度
individual Fitness(string s)
{
individual T;
double x1a = -3.0;
double x1b = 12.1;
double x2a = 4.1;
double x2b = 5.8;
string s1 = s.substr(0,18);
string s2 = s.substr(18,15);
int t1 = Transform(s1);
int t2 = Transform(s2);
T.x1 = x1a+(x1b-x1a)*t1/(pow(2.0,18)-1);
T.x2 = x2a+(x2b-x2a)*t2/(pow(2.0,15)-1);
T.fit = 21.5+T.x1*sin(4*PI*T.x1) +T.x2*sin(20*PI*T.x2);
return T;
}
//輪盤賭選擇
vector<string> choose(vector<string> population,int x)
{
//srand(time(0));
double fitness_sum=0;
vector<double> q;
vector<individual> indi_vec;
vector<string> result;
double p=0;
vector<double> random;
for (int i = 0; i < x; i++)
{
double r = rand();
random.push_back(r / (double)RAND_MAX);
}
for(int i=0;i<population.size();i++)
{
individual Indi=Fitness(population[i]);
indi_vec.push_back(Indi);
fitness_sum=fitness_sum+Indi.fit;
}
for(int i=0;i<population.size();i++)
{
individual Indi=indi_vec[i];
p=p+Indi.fit/fitness_sum;
if(i==population.size()-1)
q.push_back(1.0);
else
q.push_back(p);
}
for(int i=0;i<random.size();i++)
{
for(int j=0;j<q.size();j++)
{
if (random[i]>q[j] && random[i] <= q[j + 1])
{
result.push_back(population[j + 1]);
break;
}
}
}
return result;
}
//兩點交叉
vector<string> crossover(vector<string>population)
{
//srand(time(0));
vector<string> result;
for(int i=0;i+1<population.size();i=i+2)
{
string s1=population[i];
string s2=population[i+1];
double r = rand();
if (r<pc*RAND_MAX)
{
int i1 = (int)(rand()/RAND_MAX)*33;
int i2 = (int)(rand() / RAND_MAX) * 33;
if(i1<i2)
{
string sub1=s1.substr(i1,i2-i1);
string sub2=s2.substr(i1,i2-i1);
s1.replace(i1,i2-i1,sub2);
s2.replace(i1,i2-i1,sub1);
result.push_back(s1);
result.push_back(s2);
}
else{
if (i1 > i2)
{
string sub1 = s1.substr(i2, i1 - i2);
string sub2 = s2.substr(i2, i1 - i2);
s1.replace(i2, i1 - i2, sub2);
s2.replace(i2, i1 - i2, sub1);
result.push_back(s1);
result.push_back(s2);
}
else
{
result.push_back(s1);
result.push_back(s2);
}
}
}
else
{
result.push_back(s1);
result.push_back(s2);
}
}
return result;
}
//單點變異
string mutation(string s)
{
//srand(time(0));
for(int i=0;i<33;i++)
{
double r = rand();
if (r<pm*RAND_MAX)
{
if(s[i]=='0')
s[i]='1';
else
s[i]='0';
}
}
return s;
}
//返回本輪適應度值最大的個體
individual print_(vector<string> generation)
{
individual max;
for (int i = 0; i < generation.size(); i++)
{
individual Ind = Fitness(generation[i]);
if (Ind.fit>max.fit)
{
max = Ind;
max_s = generation[i];
}
}
return max;
}
int main(int argc, _TCHAR* argv[])
{
srand(time(0));
individual Max;
init();
int j = 0;
double MAX = 0;
while (j < 5000)
{
vector<string> pop1, pop2, pop3, pop_new;
pop1 = choose(pop,NUMBER);
pop2 = crossover(pop1);
double max1 = 0;
double max2 = 0;
string s1;
string s2;
for (int i = 0; i < pop2.size(); i++)
{
string s = mutation(pop2[i]);
pop3.push_back(s);
double d = Fitness(pop2[i]).fit;
if ( d> max1)
{
s1 = pop2[i];
max1 = d;
}
}
for (int i = 0; i < pop3.size(); i++)
{
double d = Fitness(pop3[i]).fit;
if (d > max2)
{
s2 = pop3[i];
max2 = d;
}
}
pop_new = choose(pop, NUMBER-2);
pop_new.push_back(s1);
pop_new.push_back(s2);
pop = pop_new;
individual max = print_(pop);
if (max.fit > Max.fit)
{
cout << j + 1 << ":" << max.x1 << ";" << max.x2 << ";" << max.fit << endl;
Max = max;
pop.push_back(max_s);
}
else
cout << j + 1 << ":" << Max.x1 << ";" << Max.x2 << ";" << Max.fit << endl;
j++;
}
system("pause");
return 0;
}