遗传算法-总结

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了遗传算法-总结相关的知识,希望对你有一定的参考价值。

参考技术A 最近在做遗传算法的项目,简单记录一下。
遗传算法是模拟自然界生物进化机制的一种算法,在寻优过程中有用的保留无用的去除。包括3个基本的遗传算子:选择(selection)、交叉(crossover)和变异(mutation)。遗传操作的效果与上述3个遗传算子所取的操作概率、编码方法、群体大小、初始群体,以及适应度函数的设定密切相关。
1、种群初始化
popsize 种群大小,一般为20-100,太小会降低群体的多样性,导致早熟;较大会影响运行效率;迭代次数一般100-500;交叉概率:0.4-0.99,太小会破坏群体的优良模式;变异概率:0.001-0.1,太大搜索趋于随机。编码包括实数编码和二进制编码,可以参考遗传算法的几个经典问题,TSP、背包问题、车间调度问题。
2、选择
目的是把优化个体直接遗传到下一代或通过配对交叉产生新的个体再遗传到下一代,我大部分采用了轮盘赌的方法。具体可参考 http://my.oschina.net/u/1412321/blog/192454 轮盘赌方法各个个体的选择概率和其适应值成比例,个体适应值越大,被选择的概率也越高,反之亦然。在实际问题中,经常需要最小值作为最优解,有以下几种方法进行转换
a、0-1之间的数据,可以用1-该数值,则最小值与最大值互换;
b、 求倒数;
c、求相反数;
以上几种方法均可以将最大值变为最小值,最小值变为最大值,便于利用轮盘赌选择最优个体,根据实际情况来确定。
3、交叉
交叉即将两个父代个体的部分结构加以替换重组而生成新个体的操作,通过交叉,遗传算法的搜索能力得以飞跃提高。根据编码方法的不同,可以有以下的算法:
a、实值重组
离散重组、中间重组、线性重组、扩展线性重组
b、二进制交叉
单点交叉、多点交叉、均匀交叉、洗牌交叉、缩小代理交叉
4、变异
基本步骤:对群中所有个体以事先设定的变异概率判断是否进行变异;对进行变异的个体随机选择变异位进行变异。根据编码表示方法的不同,有实值变异和二进制变异
变异的目的:
a、使遗传算法具有局部的随机搜索能力。当遗传算法通过交叉算子已接近最优解邻域时,利用变异算子的这种局部搜索能力可以加速向最优解收敛。显然该情况下变异概率应取较小值,否则接近最优解的积木块会因为变异遭到破坏。
b、使遗传算法可维持多样性,以防止未成熟收敛现象。此时收敛概率应取较大值。
变异概率一般取0.001-0.1。
5、终止条件
当最优个体的适应度达到给定的阈值,或者最优个体的适应度和群体适应度不再上升时,或者迭代次数达到预设的代数时,算法终止。预设代数一般为100-500。
6、其它
多变量:将多个变量依次连接
多目标:一种方法是转化为单目标,例如按大小进行排序,根据排序和进行选择,可以参考 https://blog.csdn.net/paulfeng20171114/article/details/82454310

基于遗传算法的函数最大值计算(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),以上是关于遗传算法-总结的主要内容,如果未能解决你的问题,请参考以下文章

R语言中的遗传算法详细解析

遗传算法总结(#看了就能懂和用系列#)

遗传算法

遗传算法入门C1

遗传算法(Genetic Algorithm,GA)

遗传算法中适值函数的标定与大变异算法