遗传算法详解及c++实现
1、什么是遗传算法?
遗传算法是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。遗传算法是从代表问题可能潜在的解集的一个种群开始的,而一个种群则由经过基因编码的一定数目的个体组成。每个个体实际上是染色体带有特征的实体。染色体作为遗传物质的主要载体,即多个基因的集合,其内部表现(即基因型)是某种基因组合,它决定了个体的形状的外部表现,如黑头发的特征是由染色体中控制这一特征的某种基因组合决定的。因此,在一开始需要实现从表现型到基因型的映射即编码工作。由于仿照基因编码的工作很复杂,我们往往进行简化,如二进制编码,初代种群产生之后,按照适者生存和优胜劣汰的原理,逐代演化产生出越来越好的近似解,在每一代,根据问题域中个体的适应度大小选择比较优秀的个体,并借助于自然遗传学的遗传算子进行交叉和变异,产生出代表新的解集的种群。这个过程将导致种群像自然进化一样的后生代种群比前代更加适应于环境,末代种群中的最优个体经过解码,可以作为问题近似最优解。
2、遗传算法的运算过程
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文件
//功能:求一个多元函数的最大值(这里是求二元函数的最大值:f(x1,x2) = 21.5+x1*sin(4pi*x1)+x2*sin(20pi*x2))
#pragma once//保证文件只被编译一次
#include<random>
#include<vector>
#include<iostream>
#include<cmath>
#include<ctime>
#include <cstdlib>
#include <bitset>
#include<iomanip>
using namespace std;
const double PI = 3.141592653589793;//定义一个不可改变的常量值PI
const int Po_Size = ;//种群规模
const int Ev_Algebra = ;//进化代数
const double Ov_Probability = 0.850; //交叉概率,交叉概率用于判断两两个体是否需要交叉
const double Va_Probability = 0.050;//变异概率,变异概率用于判断任一个体是否需要变异
const int De_Variable = ;//函数自变量的个数,如果要进行多元函数的最值求解,直接修改自变量数个De_Variable即可
const int length1 = ;//精确到6位小数,用24位二进制编码
const int length2 = ;//精确到6位小数,用23位二进制编码
class X_Range //自变量取值范围类,适用于多个变量
{
private:
double Upper;//变量的上界取值
double Lower;//变量的下界取值
public:
X_Range(double m_Upper, double m_Lower);//构造函数
double GetUpper()const;//获取变量上限
double GetLower()const;//获取变量下限
};
class Individual //定义个体类
{
private:
double Variable[De_Variable];//存放变量x1,x2,x3........
double Fitness;//适应值
double ReFitness;//适应值概率
double SumFitness;//累加概率,为轮盘转做准备
public:
Individual() {}//默认构造函数
Individual(double* m_Variable);//构造函数
double* GetVariable();//获取变量值
void ChaFitness(const double m_fitness);//修改适应值
void ChaReFitness(const double m_ReFitness);//修改适应值概率
void ChaSumFitness(const double m_SumFitness);//修改累加概率
double GetFitness()const;//获取适应值
double GetReFitness()const;//获取适应值概率
double GetSumFitness()const;//获取累加概率
};
void Initialize();//随机初始化种群,得到第一代个体
void CaculaFitness();//计算个体的适应值
void CaculaReFitness();//计算个体的适应值概率
void CalculaSumFitness();//计算累加个体概率
void seclect();//种群选择
double Scand();//随机产生0到49的随机整数
void crossing();//杂交
void variating();//变异
void genetic_algorithm();//遗传算法
GeneticAlgorithm.cpp文件
#include"GeneticAlgorithm.h"//包含头文件
//自变量取值范围向量和种群向量定义
const X_Range Range[De_Variable] = { X_Range(-3.0,12.1) ,X_Range(4.1,5.8) };//自变量(或者基因)x1,x2的取值范围
vector<Individual> nowpopulation;//P(t)种群
vector<Individual> midpopulation;//中间种群,存放轮盘选择后的优秀个体
vector<Individual> nextpopulation;//P(t+1)种群
//X_Range类实现
X_Range::X_Range(double m_Lower, double m_Upper) :Lower(m_Lower), Upper(m_Upper){}//X_Range类构造函数实现
double X_Range::GetUpper()const//获取变量上限
{
return Upper;
}
double X_Range::GetLower()const//获取变量下限
{
return Lower;
}
//Individual类实现
Individual::Individual(double* m_Variable)//构造函数
{
for (int i = ; i < De_Variable; i++)//用for循环自变量逐个赋值
{
if (m_Variable[i] >= Range[i].GetLower() && m_Variable[i] <= Range[i].GetUpper())//这里要进行自变量取值范围判断
{
Variable[i] = m_Variable[i];//自变量赋值
}
else//不满足要求则发出出错警告并返回
{
cerr << "自变量取值不满足要求" << endl;
exit();//停止程序,我会以随机函数的方式生成自变量的值(基因值),这里说明基因值不在规定范围内
}
}
//初始化时默认适应值等值为0
this->Fitness = ;
this->ReFitness = ;
this->SumFitness = ;
}
double* Individual::GetVariable()//获取基因值
{
return Variable;
}
double Individual::GetFitness()const//获取适应值
{
return Fitness;
}
double Individual::GetReFitness()const //获取适应值概率
{
return ReFitness;
}
double Individual::GetSumFitness()const//获取累加概率
{
return SumFitness;
}
void Individual::ChaFitness(const double m_fitness)//修改适应值
{
this->Fitness = m_fitness;
}
void Individual::ChaReFitness(const double m_ReFitness)//修改适应值概率
{
this->ReFitness = m_ReFitness;
}
void Individual::ChaSumFitness(const double m_SumFitness)//修改累加概率
{
this->SumFitness = m_SumFitness;
}
//遗传算法的准备工作
void Initialize()//随机初始化种群,得到第一代种群
{
//产生指定范围的随机变量(基因)
double X[Po_Size][De_Variable];//为了使程序可以满足多元函数最值的计算,用矩阵保存产生的随机数变量值
for (int j = ; j < De_Variable; j++)
{
default_random_engine e(time());//引擎,生成随机序列
uniform_real_distribution<double> u(Range[j].GetLower(), Range[j].GetUpper());//分布
for (int i = ; i < Po_Size; i++)//先按列存储随机数
{
X[i][j] = u(e);//循环结束时,所有随机值就保存在X矩阵中
}
}
//生成对象(染色体)并加入到初始种群中
for (int i = ; i < Po_Size; i++)
{
double variable[De_Variable];
for (int j = ; j < De_Variable; j++)
{
variable[j] = X[i][j];//按行保存
}
Individual Indivi(variable);//生成一个对象(染色体)
nowpopulation.push_back(Indivi);//加入到种群population中
}
}
void CaculaFitness()//计算个体的适应值
{
//f(x1,x2) = 21.5+x1*sin(4pi*x1)+x2*sin(20pi*x2))为适应度计算函数
double fitness = ;//临时适应值
double x[De_Variable];//临时存储自变量(基因)
for (int i = ; i < Po_Size; i++)
{
for (int j = ; j < De_Variable; j++)
x[j] = nowpopulation.at(i).GetVariable()[j];//这样更直观
fitness = 21.5 + x[] * sin( * PI*x[]) + * sin( * PI*x[]);//适应度计算
nowpopulation.at(i).ChaFitness(fitness);//修改当前染色体的适应值
}
}
void CaculaReFitness()//计算适应值概率
{
double sum = ;//适应值累加器
double temp = ;
for (int i = ; i < Po_Size; i++)//计算出适应值之和
{
sum += nowpopulation.at(i).GetFitness();
}
for (int j = ; j < Po_Size; j++)
{
temp = nowpopulation.at(j).GetFitness() / sum;//计算概率
nowpopulation.at(j).ChaReFitness(temp);//修改个体的适应度概率
}
}
void CalculaSumFitness()//计算累加个体概率
{
double summation = ;//累加器
for (int k = ; k < Po_Size; k++)
{
summation += nowpopulation.at(k).GetReFitness();
nowpopulation.at(k).ChaSumFitness(summation);//当前累加结果赋值
}
}
void seclect() //种群选择
{
//随机生生成0到1的小数
double array[Po_Size];//随机数保存变量
default_random_engine e(time());//引擎,生成随机序列
uniform_real_distribution<double> u(0.0, 1.0); //分布
for (int i = ; i < Po_Size; i++)
array[i] = u(e);
//轮盘进行选择
for (int j = ; j < Po_Size; j++)
{
for (int i = ; i < Po_Size; i++)
{
if (array[j] < nowpopulation[i - ].GetSumFitness())
{
midpopulation.push_back(nowpopulation.at(i - ));//加入到中间种群
}
if (array[j] >= nowpopulation.at(i - ).GetSumFitness() && array[j] <= nowpopulation.at(i).GetSumFitness())
{
midpopulation.push_back(nowpopulation.at(i));//加入到中间种群
}
}
}
nowpopulation.clear();//清空nowpopulation
}
double Scand() //随机产生0到1的小数
{
int N = rand() % ;
return double(N)/1000.0;;//随机产生0到1的小数
}
void crossing()//杂交
{
int num = ;//记录次数
double corss = 0.0;//保存随机产生的概率值
srand((unsigned)time(NULL));//根据系统时间设置随机数种子,设置一次随机种子就行
double array1[De_Variable], array2[De_Variable];//临时存储父亲和母亲的变量值
while (num< Po_Size-)//个体1与个体2杂交,个体3与个体4杂交......个体i和个体i+1杂交
{
//判断双亲是否需要杂交,随机生成一个0到1的小数,如果这个数大于杂交概率,则放弃杂交,直接遗传给下一代,否则,对父母体进行杂交
corss = Scand();
if (corss <= Ov_Probability)//如果corss小于等于杂交概率Ov_Probability就进行单点杂交
{
//首先寻找对应下标的个体并且保存
for (int i = ; i < De_Variable; i++)
{
array1[i] = midpopulation.at(num).GetVariable()[i];//父亲的自变量
array2[i] = midpopulation.at(num + ).GetVariable()[i];//母亲自变量
}
int localx1, localx2;//记录基因交叉点的位置
int corssx1[length1], corssx2[length2];//作为交换基因的数组
double newx1[], newx2[];//分别用来保存基因交换后所对应自变量值
bool p1 = true, p2 = true;
//然后对双亲变量进行编码并且进行单点杂交
for (int j = ; j < De_Variable; j++)//array1的x1编码之后和array2的x1编码后进行单点杂交,以此类推
{
if (j == )//x1进行编码并且杂交
{
bitset<length1> array1b1((array1[j] + 3.0)* pow(, ));//加上3.0形成一个unsigaed数之后在进行母体1的x1编码
bitset<length1> array2b1((array2[j] + 3.0)* pow(, ));//加上3.0形成一个unsigaed数之后在进行母体2的x1编码
//现在随机生成0到length1-1的数,确定交叉点的位置
localx1 = rand() % length1;
//现在进行单点交叉,交换双亲localx1后面的基因
for (int i = ; i < localx1; i++)
corssx1[i] = array1b1[i];
for (int k = ; k < localx1; k++)
array1b1[k] = array2b1[k];
for (int s = ; s < localx1; s++)
array2b1[s] = corssx1[s];
//新值保存在newx1数组中,x1基因完成单点杂交操作
newx1[] = double(array1b1.to_ullong()) / pow(, ) - 3.0;
newx2[] = double(array2b1.to_ullong()) / pow(, ) - 3.0;
//对新产生的值进行判断,判断是否超出范围,如果超出范围则不杂交
if (newx1[]< Range[].GetLower() || newx1[]>Range[].GetUpper() || newx2[]<Range[].GetLower() || newx2[]>Range[].GetUpper())
{
p1 = false;
break;
}
}
if (j == )//x2进行编码并且杂交
{
bitset<length2> array1b2((array1[j]) * pow(, ));//母体1的x2编码
bitset<length2> array2b2((array2[j]) * pow(, ));//母体2的x2编码
//现在随机生成0到length2-1的数,确定交叉点的位置
localx2 = rand() % length2;
//现在进行单点交叉,交换双亲localx2后面的基因
for (int i = ; i < localx2; i++)
corssx2[i] = array1b2[i];
for (int k = ; k < localx2; k++)
array1b2[k] = array2b2[k];
for (int s = ; s < localx2; s++)
array2b2[s] = corssx2[s];
//新值保存在newx2数组中,x2基因完成单点杂交操作
newx1[] = double(array1b2.to_ullong()) / pow(, );
newx2[] = double(array2b2.to_ullong()) / pow(, );
//对新产生的值进行判断,判断是否超出范围,如果超出范围则不杂交
if (newx1[]< Range[].GetLower() || newx1[]>Range[].GetUpper() || newx2[]<Range[].GetLower() || newx2[]>Range[].GetUpper())
{
p2 = false;
break;
}
}
}
if (p1 == true && p2 == true)
{
Individual newchiled1(newx1);
Individual newchiled2(newx2);
nextpopulation.push_back(newchiled1);
nextpopulation.push_back(newchiled2);
}
else//将原来的个体遗传给下一代
{
nextpopulation.push_back(midpopulation.at(num));
nextpopulation.push_back(midpopulation.at(num + ));
}
}
else//否则直接遗传给下一代nextpopulation
{
nextpopulation.push_back(midpopulation.at(num));//生成一个新的个体并且加入到nextpopulation中
nextpopulation.push_back(midpopulation.at(num + ));
}
num += ;
}
midpopulation.clear();//清空midpopulation
}
void variating()//变异
{
int num = ;
while (num<Po_Size)
{
double variation = Scand();//随机产生一个0到1的小数,用于判断是否进行变异
if (variation <= Va_Probability)//如果variation小于变异系数,则需要进行变异
{
double x[];
bool p = true;
int x1local, x2local;
x[] = nextpopulation.at(num).GetVariable()[];
x[] = nextpopulation.at(num).GetVariable()[];
bitset<length1> array1((x[]+ 3.0)* pow(, ));//x1编码
bitset<length2> array2(x[]*pow(,));//x2编码
x1local = rand() % length1;//array1该位取反
x2local = rand() % length2;//array2该位取反
array1.flip(x1local);//改变array1 x1local位的状态
array2.flip(x2local);//改变array2 x2local位的状态
x[] = double(array1.to_ullong()) / pow(, ) - 3.0;
x[] = double(array2.to_ullong()) / pow(, );
//判断是否符合条件
if (x[]< Range[].GetLower() || x[]>Range[].GetUpper() || x[]<Range[].GetLower() || x[]>Range[].GetUpper())
p = false;
if (!p)
nowpopulation.push_back(nextpopulation.at(num));
if (p)
{
Individual newchiled(x);
nowpopulation.push_back(newchiled);
}
}
else
nowpopulation.push_back(nextpopulation.at(num));
num++;
}
nextpopulation.clear();//清空nextpopulation
}
void genetic_algorithm()
{
Initialize();//初始化种群,随机生成第一代个体
//进化500代
for (int i = ; i < Ev_Algebra; i++)
{
CaculaFitness();//适应度计算
CaculaReFitness();//适应度概率计算
CalculaSumFitness();//计算累加个体概率
seclect();//选择
crossing();//杂交
variating();//变异
}
CaculaFitness();//适应度计算
double maxfitness=nowpopulation.at().GetFitness();
int maxid = ;
int k;
for (k = ; k < Po_Size; k++)
{
if (maxfitness < nowpopulation.at(k).GetFitness())
{
maxfitness = nowpopulation.at(k).GetFitness();
maxid = k;
}
}
//进化500代之后输出
cout << "x1"<<setw()<<"x2" << setw()<<"Fitness" << endl;
for (int j = ; j < Po_Size; j++)
cout << nowpopulation.at(j).GetVariable()[] <<setw()<< nowpopulation.at(j).GetVariable()[] << setw() <<nowpopulation.at(j).GetFitness() << endl;
cout << "x1=" << nowpopulation.at(maxid).GetVariable()[] << " ," << "x2=" << nowpopulation.at(maxid).GetVariable()[] << "时取得最大值:" << maxfitness << endl;
}
main.cpp文件
#include"GeneticAlgorithm.h"
int main()
{
genetic_algorithm();
system("pause");
return ;
}
结果:
遗传算法的细节我就没有过多的介绍,上面的代码是本人根据f(x1,x2) = 21.5+x1*sin(4pi*x1)+x2*sin(20pi*x2),其中-3.0≤x1≤12.1,4.1≤x2≤5.8写的,如果有不合理的地方,麻烦提出来,谢谢大家!
遗传算法详解及c++实现的更多相关文章
- 遗传算法详解(LINGO及MatlabGA工具箱求解实现)
遗传算法 1.前言 遗传算法是一种基于生物界自然群体遗传进化机制的自适应全局优化概率搜索算法.它与传统算法不同,不依赖梯度信息,而是通过模拟自然进化过程来搜索最优解. 例子:兔子的遗传进化 有人说,现 ...
- 详解用python实现简单的遗传算法
详解用python实现简单的遗传算法 今天整理之前写的代码,发现在做数模期间写的用python实现的遗传算法,感觉还是挺有意思的,就拿出来分享一下. 首先遗传算法是一种优化算法,通过模拟基因的优胜劣汰 ...
- Rserve详解,R语言客户端RSclient【转】
R语言服务器程序 Rserve详解 http://blog.fens.me/r-rserve-server/ Rserve的R语言客户端RSclient https://blog.csdn.net/u ...
- Linq之旅:Linq入门详解(Linq to Objects)
示例代码下载:Linq之旅:Linq入门详解(Linq to Objects) 本博文详细介绍 .NET 3.5 中引入的重要功能:Language Integrated Query(LINQ,语言集 ...
- 架构设计:远程调用服务架构设计及zookeeper技术详解(下篇)
一.下篇开头的废话 终于开写下篇了,这也是我写远程调用框架的第三篇文章,前两篇都被博客园作为[编辑推荐]的文章,很兴奋哦,嘿嘿~~~~,本人是个很臭美的人,一定得要截图为证: 今天是2014年的第一天 ...
- EntityFramework Core 1.1 Add、Attach、Update、Remove方法如何高效使用详解
前言 我比较喜欢安静,大概和我喜欢研究和琢磨技术原因相关吧,刚好到了元旦节,这几天可以好好学习下EF Core,同时在项目当中用到EF Core,借此机会给予比较深入的理解,这里我们只讲解和EF 6. ...
- Java 字符串格式化详解
Java 字符串格式化详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 文中如有纰漏,欢迎大家留言指出. 在 Java 的 String 类中,可以使用 format() 方法 ...
- Android Notification 详解(一)——基本操作
Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...
- Android Notification 详解——基本操作
Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...
随机推荐
- git 命令收藏
git init # 初始化本地git仓库(创建新仓库) git config --global user.name "xxx" # 配置用户名 git config -- ...
- vim使用方法----转载
转载自:http://www.cnblogs.com/itech/archive/2009/04/17/1438439.html vi/vim 基本使用方法本文介绍了vi (vim)的基本使用方法,但 ...
- Jmeter CSV 参数化/检查点/断言
当参数的值没有规律且量不太大时,可以使用CSV Data set config这种方法. 案例: 应用Sogou自动搜索关键字: 软件开发测试,web功能自动化测试,性能自动化测试,Selenium以 ...
- HTML 段落p
可以把 HTML 文档分割为若干段落. HTML 段落 段落是通过 <p> 标签定义的. 实例 <p>This is a paragraph</p> <p&g ...
- Java初学者 编译能通过,但显示有错误,并且不会自动弹出方法的解决方法。
因为使用了 @Data注解,关于注解的作用尚未深入理解,此处先做一个记录. 解决方法是,添加lombok插件
- Eclipse编码格式
来源:http://e-ant.javaeye.com/blog/177579 如果要使插件开发应用能有更好的国际化支持,能够最大程度的支持中文输出,则最好使 Java文件使用UTF-8编码.然而,E ...
- eclipse 实用快捷键(最全)
注释: (1)Ctrl+Space 说明:内容助理.提供对方法,变量,参数,javadoc等得提示, 应运在多种场合,总之需要提示的时候可先按此快捷键. 注:避免输入法的切换设置与此设置冲突 ( ...
- ES6 Map遍历、filter()筛选对象
目录: -------- 1.map() -------- 2.filter(): ------------- 2.1.filter函数可以看成是一个过滤函数,返回符合条件的元素的数组 ------- ...
- January 30 2017 Week 5 Monday
I can accept defeat but could not accept to give up. 我可以接受失败,但不能接受放弃. Fortune has not always smiled ...
- ZT JAVA WeakReference
JAVA WeakReference 分类: JAVA 2012-08-28 16:08 305人阅读 评论(0) 收藏 举报 javareferencehashmapcacheclassnull 在 ...