基于遗传算法的函数最大值计算(MATLAB新手向)


前言

利用遗传算法来计算xsin(10x)+1在[-1,2]内的最大值(至少小数点后保留六位)

提示:笔者是刚刚学习遗传算法的新手,如有错误和值得改进的地方感谢指正!

一、遗传算法是什么?

遗传算法(GA)是一种基于自然选择远离和自然遗传机制的搜索(寻优)算法,它是模拟自然界中生命的进化机制,在人工系统中实现特定目标的优化。

二、代码与详解

0.题目

转自华南理工大学MOOC

1.整体代码

废话不多说,先上全部代码:

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); %初始种群,由随机地01组成的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

2.基本概念

个体:一个问题的一个解,或是搜索空间的一个点。
种群:个体组合成的集合,或是搜索空间的一个子集。
染色体:个体的字符串编码形式。
基因:字符串中的字符。
初始种群:随机生成的若干个体。
种群规模:初始种群的个体的数量。
选择运算:从初始种群中,选择的到底一代群体,从父代群体中选取一些个体,得到子一代群体。
交叉运算:两个染色体按某种方式相互交换其部分基因,从而得到两个新的染色体。
变异运算:改变某些编码串中的基因值。

3.代码分析

第一部分

笔者为了逻辑简便,因此将十进制数转化为二进制,用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); %初始种群,由随机地01组成的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图像
https://www.geogebra.org/calculator
可见结果符合。

希望能获得大佬们的批评和建议,如果能点个赞就更好了,笔者作为萌新还会继续更新算法相关的学习历程,欢迎关注!

  • 20
    点赞
  • 125
    收藏
    觉得还不错? 一键收藏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值