提示:笔者是刚刚学习遗传算法的新手,如有错误和值得改进的地方感谢指正!
废话不多说,先上全部代码:
cssj=zeros(22,1,5);
cssj2=zeros(22,1,5);
rand('state',sum(clock));
%初始种群
for k=1:5
cssj(:,1,k)=randi([0,1],22,1); %初始种群,由随机地0和1组成的1*22列向量,第一个为符号位
end
Population=zeros(1,5);%十进制的结果
two=zeros(1,22);%二进制与十进制转换
for k=1:22
two(1,k)=2^(2-k);
end
two(1,1)=0;
%获得初始种群
for TIME=1:200
for k=1:5
Population(1,k)=two(1,:)*cssj(:,1,k);
end
%概率
p=zeros(1,5);
%适应度函数
Population_r=zeros(1,5);
for k=1:5
Population_r(1,k)=Population(1,k)*sin(Population(1,k)*10)+1;
end
for k=1:5%二进制和十进制结果对应排序
for j=k:5
if Population_r(1,k)<Population_r(1,j)
t=Population_r(1,k);
Population_r(1,k)=Population_r(1,j);
Population_r(1,j)=t;
tt=cssj(:,1,k);
cssj(:,1,k)=cssj(:,1,j);
cssj(:,1,j)=tt;
end
end
end
Population_abs=abs(Population_r);%结果排序用真实值,概率排序用绝对值,保证最小值概率最小
sort(Population_abs,'descend');
Population_r(1,1)%不打分号,用于时事输出最大值
for k=1:5
p(1,k)=sum((Population_abs(1:k)))/sum((Population_abs));
end
cssj2=cssj;
%选择算法 利用轮盘赌的方式
for k=2:5
j=rand(1);
for i=1:5
if j<=p(1,i)
cssj2(:,1,k)=cssj(:,1,i);
break;
end
end
end
cssj=cssj2;
%变异算法 变异概率为0.1
for k=randi([2,4],1):5
m1=randi([1,22],1);
m2=randi([1,22],1);
m=[m1,m2];
sort(m);
for j=m(1,1):m(1,2)
rd=rand(1);
if rd<0.1&&cssj(j,1,k)==1
cssj(j,1,k)=0;
elseif rd<0.1&&cssj(j,1,k)==0
cssj(j,1,k)=1;
end
end
end
for kk=1:50
%交叉算法 该部分可以去除!!但保留可以减少总循环次数
cssj2=cssj;
m1=randi([1,22],1);
m2=randi([1,22],1);
m=[m1,m2];
sort(m);
lucky=randi([1,5],1,2);%选取幸运儿交换基因
temp=cssj2(m(1,1):m(1,2),1,lucky(1,1));
cssj2(m(1,1):m(1,2),1,lucky(1,1))=cssj2(m(1,1):m(1,2),1,lucky(1,2));
cssj2(m(1,1):m(1,2),1,lucky(1,2))=temp;
Population2=zeros(1,5);
Population2_r=zeros(1,5);
for k=1:5
if cssj2(1,1,k)==0
cssj2(2,1,k)=0;
end
Population2(1,k)=two(1,:)*cssj2(:,1,k);
if cssj(1,1,k)==0
Population2(1,k)=-Population2(1,k);
end
end
for k=1:5
Population2_r(1,k)=Population2(1,k)*sin(Population2(1,k)*10)+1;
end
sort(Population2_r,'descend');
if Population2_r(1,1)>Population_r(1,1)
cssj=cssj2;
break;
end
end
end
end
个体:一个问题的一个解,或是搜索空间的一个点。
种群:个体组合成的集合,或是搜索空间的一个子集。
染色体:个体的字符串编码形式。
基因:字符串中的字符。
初始种群:随机生成的若干个体。
种群规模:初始种群的个体的数量。
选择运算:从初始种群中,选择的到底一代群体,从父代群体中选取一些个体,得到子一代群体。
交叉运算:两个染色体按某种方式相互交换其部分基因,从而得到两个新的染色体。
变异运算:改变某些编码串中的基因值。
笔者为了逻辑简便,因此将十进制数转化为二进制,用0和1来进行遗传算法的变异和遗传。
由于要求结果至少保留六位小数,因此二进制小数点后保留20位,小数点前有两位,首位为符号位。
如:11.11111111111111111111表示的是+(1.11111111111111111111)2
而00.11111111111111111111表示的是-(0.11111111111111111111)2
但是考虑到xsinx为关于y轴对称,其实可以不用管负数部分的,但是为了增加代码的通用型,还是保留负数部分
生成初始种群:
cssj=zeros(22,1,5);
cssj2=zeros(22,1,5);
rand('state',sum(clock));
%初始种群
for k=1:5
cssj(:,1,k)=randi([0,1],22,1); %初始种群,由随机地0和1组成的1*22列向量,第一个为符号位
end
Population=zeros(1,5);%十进制的结果
two=zeros(1,22);%二进制与十进制转换
for k=1:22
two(1,k)=2^(2-k);
end
two(1,1)=0;
这里涉及到四个变量,既cssj,two,Population
cssj(二进制的种群矩阵,种群规模为5,染色体的基因数为22),
two(二进制转十进制用的行向量,序列为0,1,1/2,1/4,1/8,……1/220)首位为0,是为了不将cssj的符号位做计算
Population(十进制的种群,由行向量two和cssj的每个列向量相乘得到)
cssj2为中间变量
此处将实现第一部分所提到的二进制转换十进制。
for k=1:5
if cssj(1,1,k)==0
cssj(2,1,k)=0;
end
Population(1,k)=two(1,:)*cssj(:,1,k);
if cssj(1,1,k)==0
Population(1,k)=-Population(1,k);
end
end
p为概率,将用于后续的轮盘赌选择,而Population_r就将Population的值带入函数f(x)=xsin(10x)+1
%概率
p=zeros(1,5);
%适应度函数
Population_r=zeros(1,5);
for k=1:5
Population_r(1,k)=Population(1,k)*sin(Population(1,k)*10)+1;%如果想变成其他函数,只需要改这个
end
将f(x)按降序排序,既将Population_r按降序排序。
这里为什么不用sort呢?因为还要将十进制的Population_r和二进制的cssj一一对应,
Population_r调换位置,cssj也对应着调换,给cssj也进行降序排序。
for k=1:5%二进制和十进制结果对应排序
for j=k:5
if Population_r(1,k)<Population_r(1,j)
t=Population_r(1,k);
Population_r(1,k)=Population_r(1,j);
Population_r(1,j)=t;
tt=cssj(:,1,k);
cssj(:,1,k)=cssj(:,1,j);
cssj(:,1,j)=tt;
end
end
end
Population_abs就是将Population_r各项取绝对值,为什么要这么做呢?
看一看概率p的计算就清楚了!p在本文章的含义是(前k项之和)/(五项之和),由于是降序排序,
假如Population_r=[5 4 3 2 1],那么p=[5/15 9/15 12/15 14/15 1],如何进行轮盘赌呢?
只需要使得
for i=1:5
if j<=p(1,i)
cssj2(:,1,k)=cssj(:,1,i);
break;
end
end
j=rand(1)小于[5/15 9/15 12/15 14/15 1]的概率分别为[5/15 4/15 3/15 2/15 1/15],
越大的数字被取到的概率越大。如此,最终可以依概率收敛于最大值。
但是如果Population_r有负数会发生什么?如Population_r=[5 4 2 1 -3];
此时p=[5/9 1 11/9 12/9 1];不仅变得乱序,而且还出现了超过1的数字,这显然不合理。
为了解决此问题,引入Population_abs=abs(Population_r),将所有项取绝对值,再对Population_abs进行排序。
计算概率使用Population_abs,如此一来,在Population_r=[5 4 2 1 -3];时,用Population_abs=[5 4 3 2 1];排序所得到的的p为[5/15 9/15 12/15 14/15 1]。分别对应Population_r=[5 4 2 1 -3];的二进制取值,
如此一来,既使得p必然小于1大于0,也符合了依概率收敛的条件,越大的Population_r,其自变量Population对应的二进制cssj被取到的概率越大。
此时用cssj2作为中间变量暂时保存未经变异和交叉的子代基因。
Population_abs=abs(Population_r);%结果排序用真实值,概率排序用绝对值,保证最小值概率最小
sort(Population_abs,'descend');
Population_r(1,1)%不打分号,用于时事输出最大值
for k=1:5
p(1,k)=sum((Population_abs(1:k)))/sum((Population_abs));
end
cssj2=cssj;%%%%%
%选择算法 利用轮盘赌的方式
for k=2:5
j=rand(1);
for i=1:5
if j<=p(1,i)
cssj2(:,1,k)=cssj(:,1,i);
break;
end
end
end
cssj=cssj2;
最后就是变异算法和交叉算法,变异算法就是最简单的随机选取的思路。最高项不变,其余可能发生变异。
%变异算法 变异概率为0.01
for k=randi([2,4],1):5
m1=randi([1,22],1);
m2=randi([1,22],1);
m=[m1,m2];
sort(m);
for j=m(1,1):m(1,2)
rd=rand(1);
if rd<0.1&&cssj(j,1,k)==1
cssj(j,1,k)=0;
elseif rd<0.1&&cssj(j,1,k)==0
cssj(j,1,k)=1;
end
end
end
交叉算法这里的思路就是,循环五十次(时间复杂度较高待优化),尝试随机联会,如果联会有好处,则保留。(可以去除)
for kk=1:50
%交叉算法
cssj2=cssj;%%%%%
m1=randi([1,22],1);
m2=randi([1,22],1);
m=[m1,m2];
sort(m);
lucky=randi([1,5],1,2);%选取幸运儿交换基因
temp=cssj2(m(1,1):m(1,2),1,lucky(1,1));%%%%%
cssj2(m(1,1):m(1,2),1,lucky(1,1))=cssj2(m(1,1):m(1,2),1,lucky(1,2));%%%%%
cssj2(m(1,1):m(1,2),1,lucky(1,2))=temp;%%%%%
Population2=zeros(1,5);
Population2_r=zeros(1,5);
for k=1:5
if cssj2(1,1,k)==0
cssj2(2,1,k)=0;
end
Population2(1,k)=two(1,:)*cssj2(:,1,k);
if cssj(1,1,k)==0
Population2(1,k)=-Population2(1,k);
end
end
for k=1:5
Population2_r(1,k)=Population2(1,k)*sin(Population2(1,k)*10)+1;
end
sort(Population2_r,'descend');
if Population2_r(1,1)>Population_r(1,1)
cssj=cssj2;
break;
end
end
MATLAB所的结果:
geogebra图像
可见结果符合。
希望能获得大佬们的批评和建议,如果能点个赞就更好了,笔者作为萌新还会继续更新算法相关的学习历程,欢迎关注!