遗传算法入门到掌握(二)

此文承接上篇遗传算法入门到掌握(一)

遗传算法引擎――GenAlg

/遗传算法class GenAlg{public://这个容器将储存每一个个体的染色体vector <Genome> vecPop;//人口(种群)数量int popSize;//每一条染色体的基因的总数目int chromoLength;//所有个体对应的适应性评分的总和double totalFitness;//在所有个体当中最适应的个体的适应性评分double bestFitness;//所有个体的适应性评分的平均值double averageFitness;//在所有个体当中最不适应的个体的适应性评分double worstFitness;//最适应的个体在m_vecPop容器里面的索引号Genome fittestGenome;//基因突变的概率,一般介于0.05和0.3之间double mutationRate;//基因交叉的概率一般设为0.7double crossoverRate;//代数的记数器int generation;//最大变异步长double maxPerturbation;double leftPoint;double rightPoint;//构造函数GenAlg();//初始化变量void Reset();//初始化函数void init(int popsize, double MutRate, double CrossRate, int GenLenght,double LeftPoint,double RightPoint);//计算TotalFitness, BestFitness, WorstFitness, AverageFitness等变量void CalculateBestWorstAvTot();//轮盘赌选择函数Genome GetChromoRoulette();//基因变异函数void Mutate(vector<double> &chromo);//这函数产生新一代基因void Epoch(vector<Genome> &vecNewPop);Genome GetBestFitness();double GetAverageFitness();};

其中Reset()函数,init()函数和CalculateBestWorstAvTot()函数都比较简单,读者查看示例程序的代码就能明白了。而下面分别介绍init函数和Epoch函数。

类的初始化函数――init函数

init函数主要充当CGenAlg类的初始化工作,把一些成员变量都变成可供重新开始遗传算法的状态。(为什么我不在构造函数里面做这些工作呢?因为我的程序里面CGenAlg类是View类的成员变量,只会构造一次,所以需要另外的初始化函数。)下面是init函数的代码:

void GenAlg::init(int popsize, double MutRate, double CrossRate, int GenLenght,double LeftPoint,double RightPoint){popSize = popsize;mutationRate = MutRate;crossoverRate = CrossRate;chromoLength = GenLenght;totalFitness = 0;generation = 0;//fittestGenome = 0;bestFitness = 0.0;worstFitness = 99999999;averageFitness = 0;maxPerturbation=0.004;leftPoint=LeftPoint;rightPoint=RightPoint;//清空种群容器,以初始化vecPop.clear();for (int i=0; i<popSize; i++){//类的构造函数已经把适应性评分初始化为0vecPop.push_back(Genome());//把所有的基因编码初始化为函数区间内的随机数。for (int j=0; j<chromoLength; j++){vecPop[i].vecGenome.push_back(random() *(rightPoint – leftPoint) + leftPoint);}}}

恩,正如我之前说的,我们这个程序不但要应付基因编码只有一个浮点数的“袋鼠跳”问题的情况,还希望以后在处理一串浮点数编码的时候也一样适用,所以从这里开始我们就把基因当成串来对待。

开创新的纪元――Epoch函数

现在万事具备了,只差把所有现成的“零件”装配起来而已。而Epoch函数就正好充当这个职能。下面是这个函数的实现:

void GenEngine:: OnStartGenAlg(){//产生随机数srand( (unsigned)time( NULL ) );//初始化遗传算法引擎genAlg.init(g_popsize, g_dMutationRate, g_dCrossoverRate, g_numGen,g_LeftPoint,g_RightPoint);//清空种群容器m_population.clear();//种群容器装进经过随机初始化的种群m_population = genAlg.vecPop;//定义两个容器,以装进函数的输入与及输出(我们这个函数是单输入单输出的,但是以后往往不会那么简单,所以我们这里先做好这样的准备。)vector <double> input;double output;input.push_back(0);for(int Generation = 0;Generation <= g_Generation;Generation++){//里面是对每一条染色体进行操作for(int i=0;i<g_popsize;i++){input = m_population[i].vecGenome;//为每一个个体做适应性评价,如之前说的,评价分数就是函数值。其//Function函数的作用是输入自变量返回函数值,读者可以参考其代码。output = (double)curve.function(input);m_population[i].fitness = output;}//由父代种群进化出子代种群(长江后浪退前浪。)genAlg.Epoch(m_population);//if(genAlg.GetBestFitness().fitness>=bestFitness)bestSearch=genAlg.GetBestFitness().vecGenome[0];bestFitness=genAlg.GetBestFitness().fitness;averageFitness=genAlg.GetAverageFitness();//cout<<bestSearch<<endl;report(Generation+1);}//return bestSearch;}

恩,到这里“袋鼠跳”的主要代码就完成了。(其它一些代码,比如图形曲线的显示,和MFC的相关代码在这就不作介绍了,建议初学者不必理会那些代码,只要读懂算法引擎部分就可以了。)下面就只等着我们下达命令了!

让袋鼠在你的电脑里进化――程序的运行

我想没有什么别的方法比自己亲手写一个程序然后通过修改相关参数不断调试程序,更能理解并且掌握一种算法了。不知道你还记不记得你初学程序的日子,我想你上机动手写程序比坐在那里看一本厚厚的程序开发指南效率不知高上多少倍,兴趣也特命浓厚,激情也特别高涨。恩,你就是需要那样的感觉,学遗传算法也是一样 的。你需要把自己的代码运行起来,然后看看程序是否按照你所想象的去运行,如果没有,你就要思考原因,按照你的想法去改善代码,试着去弄清其中的内在联系。这是一个思维激活的过程,你大脑中的神经网络正在剧烈抖动(呵呵,或许学到后面你就知道你大脑的神经网络是如何“抖动”的。),试图去接受这新鲜而有 趣的知识。遗传算法(包括以后要学到的人工神经网络)包含大量的可控参数,比如进化代数、人口数目、选择概率、交叉概率、变异概率、变异的步长还有以后学到的很多。这些参数之间的搭配关系,不能指望别人用“灌输”的方式让你被动接受,这需要你自己在不断的尝试,不断的调整中去形成一种“感觉”的。很多时候 一个参数的量变在整个算法中会表现出质的变化。而算法的效果又能从宏观上反映参数的设置。

现在就让我们来对这个程序做简单的说明。

参数的设置:

这个程序有很多的需要预先设置好的参数,为了方便修改,我把它们都定义为全局变量,定义和初始化都放在Parameter.h的头文件里面。下面对几个主要参数的说明:

当然,一些主要的参数在程序运行后可以通过参数设置选项进行设置。(其中缓冲时间是每进化一代之后,暂停的时间,单位为毫秒)如图2-6。

运行程序:

无论何时何地,只要创造就有收获,只有不息的奋进,才能证明生命的存在。

遗传算法入门到掌握(二)

相关文章:

你感兴趣的文章:

标签云: