这是一个新手都不算的新手写个更新的手们的,纯属个人见解,欢迎前辈批评指正。
遗传算法曾经也是被人埋没的,据说曾经是一位教授的头脑中所想,但是一直没有实现在计算机上,只是在纸上来来回回的演算,也并未发生什么效率。后来在经过一个学生的手笔,于是诞生了惊动世人的遗传算法。我也是刚刚接触遗传算法,整理这些天在白天忙完软件设计大赛的任务之后,晚上的一些心得,写下如下的一些话。只为了起抛砖引玉的作用,为同是菜鸟的后来者尽上绵薄之力。
在做遗传算法的这些时间里,给我最大的感叹就是对大自然的魅力。能够创造如此美妙的最优化方法,也就只有神秘的大自然才有如此的想象力和实现能力。在编写遗传算法的程序时,也觉得得心应手,在字里行间也觉得满是有趣的。
首先说说生物体的遗传。
大家都知道,人的细胞核里有染色体,也就是人体中的遗传物质,染色体是由DNA组成的,一种双螺旋结构,而DNA就是由不同的碱基对组成的。ATGC四种嘌呤。不同的碱基对都对蛋白质的制造(生物的绝大部分可以说是蛋白质)提供了指导作用,也就是说,dna上怎么写的,除非被转化成RNA的过程中出现变异或者错误,不然的话产生的蛋白质就是一模一样的了。这也就是生物表现型的稳定性,因为变异或者出现错误的几率非常小,说以每个人都是两个眼睛一个鼻子,及特殊情况会有三个脑袋的情况,这也就是变异所产生的。但是变异并不一定都是坏的,比如说人吧,人类在向越来越聪明进化,可以这样说,自然界上生物的进化绝大多数是由于变异,剩下的绝大多数是由于基因表达时出现错误。当然说变异产生在dna转化成rna时出现是错误的。正确的来说变异,准确的说是能够影响到后代的变异主要产生在生物减数分裂在产生生殖细胞的时候出现的。当是好的变异,自然会更招惹异性喜欢或者是更能够在恶劣的环境下生存,于是这样的基因就有更大可能的被保留,(当然也有生来很帅找不到对象的时候),于是好的变异在逐渐保留,坏的变异在逐渐丢弃。就这样,生物在不断的达到全局最优。
下面说说在计算机上遗传算法是怎么构造和工作的。
模拟自然界,一切进化的基础必然要有其物质,自然就是生物群落。比如说我们研究一堆人,研究一个种群,对于我们解决实际问题来说,自然就是我们想要的即将进化成最优的解们。这些解就是我们的种群。
举一个题来说吧。
我们求解一个函数的最大值:
max f(x1,x2)=21.5+x1*sin(4*pi*x1)+x2*sin(20*pi*x2)
-3.0<=x1<=12.1
4.1<=x2<=5.8
这个函数如果用一般的解法是无法求得最优解的,比如用lingo,一直会保持在一个局部最优解的状态下,而且每次执行的结果都不一样,有兴趣的朋友可以尝试一下。下面就说说用遗传算法的解法。
大家知道,表现型可以转化为基因,基因因为变异而又能影响表现型。因此可以说,最优解的x1,x2即为我们的表现型,而基因型就需要我们来构造。最常见的构造方式即为二进制编码方式。
下面提出两条结论:
表示一个范围[a,b]之间的数的二进制表示法,精度在小数点后五位,需要m个二进制数位,则有以下关系:
2^(m-1)<(b-a)*1E4<=2^(m-1)
即为表现型转化为基因型的个数。将基因型转化为表现型:
x=a+decimal(substring)*((b-a)/(2^m-1))
这里a,b为上述a,b,substing为表示某一范围的二进制串的子串。
我们举个例子,在这个例子中,经计算x1需要18位来表示,x2需要15位来表示。
这样在一个字串中
000001010100101001 101111011111110
前部分子串为x1的基因型,后部分子串为x2的基因型。
由此,我们便能将基因型与表现型进行互换。我们从此可以干上帝的事了。
这样,初始化种群我们就可以做了。我们假设种群为2000个个体。那么我们只要生成18+15=33个位的子串,一共2000个子串就可以了。
顺便还要计算一下当前种群中最优解为多少。我们将基因型转换为表现型,再将表现型代入函数式里计算,求得2000个计算结果,我们再将这2000个计算结果进行挑选,选出是函数值最大的那个,这说明这个基因型最适合生存,我们要尽可能的保留。
之后我们要做的就是杂交了。
所谓杂交也称不上是真正的杂交,可以说仅仅是一种夫妻后产生下一代的过程。在真的生物之中,男性进行减数分裂产生自己一半的基因,女性进行减数分裂随机产生自己的一半基因,然后将两半基因进行拼接,产生下一代的基因型。所为杂交也就是这个过程。
我们先找到两个喜结连理的染色体,然后随机产生一个切点,在这个切点后将两个基因互换。这样就产生了两个新的基因。(我们假设生出来的都是双胞胎,还都互不)
这个过程就算杂交结束了。但是这个世界上并不是每个人都能找到老婆的,所以这个过程需要一定的概率作为支持,这个概率就叫做杂交率,我们这道题里面先选0.3,大家如果觉得有兴趣可以试试其他的概率,这样会对结果产生微妙的影响。
附上matlab函数:
function [ OutputString1,OutputString2 ] = CrossOver( InputString1,InputString2,num )
% CrossOver 用于遗传算法将两个二进制字符串列进行杂交 CopyRight
% InputString1,InputString2为输入的两个基因,num为杂交的起始位数
% 杂交后,起始位之前的两端基因不变,起始位之后及起始位将交换两个基因
(省略)
还有一个过程就是变异。变异的过程即为将一个个体的某个基因进行修改,将原来的1修改为0,将原来的0修改为1,这个过程也需要一定的概率,这里我们暂取0.02。因为要保证一定的生物体稳定性,所以这个概率通常来说取得很小。
附上变异的函数:
function [ OutputString ] = Mutation( InputString,num )
%Mutation 遗传算法的变异 CopyRight
% 将遗传算法的二进制串的某位进行编译,将1变为0,将0变为1
(省略)
之后我们说一说选择,就像刚才我说的,就算是天生一个帅哥也有找不到老婆的情况,所以并不是最优解都会被保留的,但是这种情况是急特殊的,但是我们还有意愿将最好的解保留下来,所以我们采用轮盘赌的形式进行。
轮盘赌也就是好的在轮盘的比例占得大一些,因此获得的概率就大一些,坏的小一些,这样好的坏的都有可能被遗传,但是好的会更可能遗传下去,这样更加符合自然界的规律。
这样循环往复下去,即可得出结论。
附上我的控制台程序:
clear all;
clc;
hold on;
%fid=fopen(’log.txt’,'W’);
%遗传算法演示程序1
%初始化种群数量
PopSize=2000;
LastMax=0;
NowMax=1;
Generation=1;
%变异率
MutationRat=0.01;
%杂交率
CrossOverRat=0.25;
%1、计算所需二进制位数,将表现型转化为基因型
m1=RequiredStringLength(-3.0,12.1);
m2=RequiredStringLength(4.1,5.8);
m=m1+m2;
%1、初始化种群
Genies=”;
for i=1:PopSize;
Genies(i,:)=DeInStringBlank(RandBinStr(m));
end
%while NowMax-LastMax>1e-7
while Generation<1000
Generation=Generation+1;
%2、二进制解码
BinX1=Genies(:,1:m1);
BinX2=Genies(:,m1+1:m);
DecX1=[];
DecX2=[];
for i=1:PopSize;
DecX1(i,:)=GADecode(BinX1(i,:),-3.0,12.1);
DecX2(i,:)=GADecode(BinX2(i,:),4.1,5.8);
end
%3、计算度量函数
Val=GAAim(DecX1,DecX2);
[MaxVal,MaxIndex]=max(Val);
%fprintf(1,’ID=%d,x1=%s,x2=%s,fun=%e。\n’,Generation,BinX1(MaxIndex,:),BinX2(MaxIndex,:),MaxVal);
%fprintf(1,’ID=%d,fun=%e。\n’,Generation,MaxVal)
fprintf(1,’ID=%d,fun=%e\tx1=%e\tx2=%e.\n’,Generation,MaxVal,DecX1(MaxIndex),DecX2(MaxIndex));
plot(Generation,max(Val));
%模拟杂交
for i=1:PopSize;
WhetherCrossOver=CloseRand(0,1,1,1);
if WhetherCrossOver<CrossOverRat
CrossOverMan=fix(CloseRand(1,PopSize,1,1));
CrossOverWoman=fix(CloseRand(1,PopSize,1,1));
CrossBegin=fix(CloseRand(1,m,1,1));
[Genies(CrossOverMan,:),Genies(CrossOverWoman,:)]= CrossOver(Genies(CrossOverMan,:),Genies(CrossOverWoman,:),CrossBegin);
end
end
%4、轮盘赌选择
%4.1、计算total fitness
TF=sum(Val);
%4.2、计算各基因链轮盘赌可能性
p=Val/TF;
%4.4、生成随机数%4.5、轮盘赌选择
NewGenies=”;
for i=1:PopSize;
%4.3、计算累计轮盘赌可能性
q=cumsum(p);
Choose=CloseRand(0,1,1,1);
index=find(q>Choose);
NewGenies(i,:)=Genies(index(1),:);
%模拟变异
MutationChoose=CloseRand(0,1,1,1);
if MutationChoose<MutationRat
MutationCount=fix(CloseRand(1,m,1,1));
Mutation(NewGenies(i,:),MutationCount);
end
end
Genies=NewGenies;
end