简单遗传算法求函数极值

这篇文章翻译自:http://www.codeproject.com/KB/cpp/Genetic_Algorithm_in_C.aspx

注:转载请注明原作者及链接,谢谢。

 

引言:遗传算法求函数极值算是遗传算法的一种最简单的应用,这里就介绍一种简单的,全文基本翻译自codeproject的一篇文章,作者为Luay Al-wesi,软件工程师。例子中的函数为y = -x2+ 5 ,大家可以将其改为其他复杂一些的函数,比如说f=x+10sin(5x)+7cos(4x)等。本篇文章适合遗传算法初学者阅读,大牛请绕道,呵呵。文后附C语言代码,全部代码在VC6.0上编译通过。这是求函数最大值的例子,求最小值时可以将目标函数进行一定的处理,比如说用1除等方法。然后得到的适应度函数就适合遗传算法,以及本代码了。

 

一、编码以及初始种群的产生

    编码采用二进制编码,初始种群采用矩阵的形式,每一行表示一个染色体,每一个染色体由若干个基因位组成。关于染色体的长度(即基因位的个数)可根据具体情况而定。比如说根据要求极值的函数的情况,本文的染色体长度为6,前5个二进制构成该染色体的值(十进制),第6个表示该染色体的适应度值。长度越长,表示解空间搜索范围越大。关于如何将二进制转换为十进制,这个应该很简单,文后的C代码中函数x即为转换函数。

    初始种群结构如下图所示:

 

图1

    该初始种群共有4个染色体,第一列表示各个染色体的编号,第2列表示该染色体值的正负号,0表示正,1表示负。第3列到第7列为二进制编码,第8列表示各个染色体的适应度值。第2列到第7列的0-1值都是随机产生的。

 

二、适应度函数

    一般情况下,染色体(也叫个体,或一个解)的适应度函数为目标函数的线性组合。本文直接以目标函数作为适应度函数。即每个染色体的适应度值就是它的目标函数值,f(x)=-x2+ 5。

 

三、选择算子

    初始种群产生后,要从种群中选出若干个体进行交叉、变异,那么如何选择这些个体呢?选择方法就叫做选择算子。一般有轮盘赌选择法、锦标赛选择法、排序法等。本文采用排序法来选择,即每次选择都选出适应度最高的两个个体。那么执行一次选择操作后,得到的新种群的一部分为下图所示:

图2

 

四、交叉算子

    那么接下来就要对新种群中选出的两个个体进行交叉操作,一般的交叉方法有单点交叉、两点交叉、多点交叉、均匀交叉、融合交叉。方法不同,效果不同。本文采用最简单的单点交叉。交叉点随机产生。但是交叉操作要在一定的概率下进行,这个概率称为交叉率,一般设置为0.5到0.95之间。交叉后产生的新个体组成的新种群如下:

图3

    黑体字表示子代染色体继承母代个体的基因情况。

 

五、变异

    变异就是对染色体的结构进行变异,使其改变原来的结构(值也就改变),达到突变进化的目的。变异操作也要遵从一定的概率来进行,一般设置为0到0.5之间。本文的变异方法直接采取基因位反转变异法,即0变为1,1变为0。要进行变异的基因位的选取也是随机的。

 

六、终止规则

    遗传算法是要一代一代更替的,那么什么时候停止迭代呢?这个规则就叫终止规则。一般常用的终止规则有:若干代后终止,得到的解达到一定目标后终止,计算时间达到一定限度后终止等方法。本文采用迭代数来限制。

 

全文代码如下:

#include //调用输入输出函数所需要的头文件 #include //调用getche函数所需要的头文件 #include //调用随机函数所需要的头文件 typedef struct Chrom // 结构体类型,为单个染色体的结构; { short int bit[6]; int fit; }chrom; void *evpop(chrom popcurrent[4]); //定义将会用到的几个函数; int x(chrom popcurrent); int y(int x); void *pickchroms(chrom popcurrent[4]); void *crossover(chrom popnext[4]); void *mutation(chrom popnext[4]); void main() // 主函数; { int num; // 迭代次数; int i,j,l,Max,k; Max=0; // 函数最大值 printf("/nWelcome to the Genetic Algorithm coded by Luay Al-wesi,editd by xujinpeng/n"); // introduction to the program,欢迎词; printf("The Algorithm is based on the function y = -x^2 + 5 to find the maximum value of the function./n"); enter:printf("/nPlease enter the no. of iterations/n 请输入您要设定的迭代数: "); scanf("%d",&num); // 输入迭代次数,传送给参数num; chrom popcurrent[4]; // 初始种群规模为4; chrom popnext[4]; // 更新后种群规模仍为4; if(num<1) goto enter; // 判断输入的迭代次数是否为负或零,是的话重新输入; evpop(popcurrent); // 随机产生初始种群; for(i=0;i Max) { Max=popcurrent[l].fit; k=l; } } printf("/n当x等于%d时,函数得到最大值为:%d ",k,Max); printf("/nPress any key to end ! "); flushall(); // 清除所有缓冲区; getche(); // 从控制台取字符,不以回车为结束; } void *evpop(chrom popcurrent[4]) // 函数:随机生成初始种群; { int i,j,value; int random; for(j=0;j<4;j++) // 从种群中的第1个染色体到第4个染色体 { for(i=0;i<6;i++) // 从染色体的第1个基因位到第6个基因位 { random=rand(); // 产生一个随机值 random=(random%2); // 随机产生0或者1 popcurrent[j].bit[i]=random; // 随机产生染色体上每一个基因位的值,0或1; } value=x(popcurrent[j]); // 将二进制换算为十进制,得到一个整数值; popcurrent[j].fit=y(x(popcurrent[j])); // 计算染色体的适应度值; printf("/n popcurrent[%d]=%d%d%d%d%d%d value=%d fitness = %d",j,popcurrent[j].bit[5],popcurrent[j].bit[4],popcurrent[j].bit[3],popcurrent[j].bit[2],popcurrent[j].bit[1],popcurrent[j].bit[0],value,popcurrent[j].fit); // 输出整条染色体的编码情况, } return(0); } int x(chrom popcurrent) // 函数:将二进制换算为十进制; { int z; z=(popcurrent.bit[0]*1)+(popcurrent.bit[1]*2)+(popcurrent.bit[2]*4)+(popcurrent.bit[3]*8)+(popcurrent.bit[4]*16); if(popcurrent.bit[5]==1) { z=z*(-1); // 考虑到符号; } return(z); } int y(int x) // 函数:求个体的适应度; { int y; y=-(x*x)+5; // 目标函数: y= - ( x^ 2 ) +5; return(y); } void *pickchroms(chrom popcurrent[4]) // 函数:选择个体; { int i,j; chrom temp; // 中间变量 for(i=0;i<3;i++) // 根据个体适应度来排序;(冒泡法) { for(j=0;j<3;j++) { if(popcurrent[j+1].fit>popcurrent[j].fit) { temp=popcurrent[j+1]; popcurrent[j+1]=popcurrent[j]; popcurrent[j]=temp; } } } for(i=0;i<4;i++) { printf("/nSorting:popnext[%d] fitness=%d",i,popcurrent[i].fit); printf("/n"); } flushall(); return(0); } void *crossover(chrom popnext[4]) // 函数:交叉操作; { int random; int i; random=rand(); // 随机产生交叉点; random=((random%5)+1); // 交叉点控制在1到5之间; for(i=0;i

你可能感兴趣的:(智能算法)