遗传算法详解及matlab代码

一、理论基础

       在理论方面有着生物学的基础,就是“物竞天择,适者生存”,有过初中生物或者学过中国近代史的都知道这个道理。其理论基础为模式定理与积木块假设,其实就是在论证这个遗传算法是具有全局最优搜索的能力的。

       其实无论是当下的遗传算法还是后面要讲解的其他算法都是一种更新策略,其实就用贪心算法也能够进行最优值的搜索,但是加上更新策略其性能就会有所差异。

二、遗传算法专业术语

术语及其解释
群体所有的可行解的集合
个体可行解
染色体其实也是可行解,都是可以看出是解的微观形式
基因你的参数变量
基因形式用什么方法对变量进行表示
适应度你的目标函数
选择挑选个体进行后面的操作
交叉不同染色体基因进行交换
变异在相应的基因节点进行变异

        对于遗传编码有着二进制编码,当变量少的时候不易进行交叉的操作,所以要用二进制来表示十进制数,但是其在后面的代码中可以看出其功能不止于此,利用其0,1操作可以进行许多意想不到的操作。还有一种方式就是十进制编码方式,就是一个基因点对应一个参数。

        对于适应度,这个就是我们评价个体好不好的一个标准,是整个种群进化的方向,其作用类似于于深度学习的损失函数,只不过优化的机理不太一样,深度学习是通过梯度下降,而优化算法是通过更新策略。

三、遗传操作

      要进行算法的运行,其更新机制怎么去实现呢?首先就是交叉和变异,那么就需要选择个体进行遗传,所以并不是所有个体都能够把自己的基因给传下去的,生存能力强的个体机会才大,那么怎么评价生存能力?用适应度函数。通过轮盘赌的方法随机选取一定量的个体进行交叉和变异,其适应度好的个体选择的次数越多,其实这个过程可以理解为宏观的复制。

        对于交叉,个体两两配,通过交叉概率看其是否要进行交叉,然后随机选择交叉的点,其有2种策略,其一就是每个位置都有50%的概率交叉;其二是先随机选择一个开始交叉的点,然后用一个值来决定结束交叉的位置或者说是交叉的长度,从而形成了一个新的个体。

        之后在以一定的概率进行一个变异操作,对于选出来的种群已经进行了交叉,还差一个变异才能完成一个微观的复制,首先对于种群所有个体按照事先设定的变异概率判断是否进行变异然后,对进行变异个个体选择变异为进行变异。

        至此就已经完成一次更新,可最终最优个体的性能来得到收敛曲线。

四、Matlab代码

         首先是对于一个二维函数一维变量的寻优问题,其变量只有一个所以采用二进制编码,其过程可见代码注释。

%这个是优化一个一维变量,为了方便变异和交叉所以采用二进制编码方式
%其许多命令可以通过matlab内置函数实现,但是为了便于理解并未使用
%下面对自变量从0到10内进行寻找全局最优点
clear all;
close all;
clc;
NP=50;% 种群的数量
L=20;%二进制串的个数(必须要把自变量的范围包括进去)
Pc=0.8;%交叉率
Pm=0.1;%变异率(不能太高否则收敛不了)
G=100;%迭代次数
Xs=10;%自变量的上界
Xx=0;%自变量的下界
func1=@(x)x+10*sin(5*x)+7*cos(4*x);%这里函数值就是适应度
f = randi([0,1],NP,L);%随机生成一个50*20的矩阵,及初始化种群
for k=1:G%迭代次数
    for i = 1:NP%提取每个个体
        U=f(i,:);
        m=0;
        for j=1:L%对每个个体进行解码
            m=U(j)*2^(j-1)+m;
        end
        x(i)=Xx+m*(Xs-Xx)/(2^L-1);
        Fit(i)=func1(x(i));%适应度
    end
    maxFit=max(Fit);
    minFit=min(Fit);
    rr=find(Fit==maxFit);
    fBest=f(rr(1,1),:);
    xBest=x(rr(1,1));
    Fit = (Fit-minFit)/(maxFit-minFit);%归一化
    
    sum_Fit=sum(Fit);%轮盘赌概率准备
    fitvalue=Fit./sum_Fit;
    fitvalue=cumsum(fitvalue);
    ms=sort(rand(NP,1));
    fiti=1;
    newi=1;
    while newi<=NP%轮盘赌进行选择
        if (ms(newi))<fitvalue(fiti)
            nf(newi,:)=f(fiti,:);
            newi=newi+1;
        else
            fiti=fiti+1;
        end
    end
   for i = 1:2:NP%两两之间进行交叉
       p=rand;
       if p<Pc
          q=randi([0,1],1,L);
          for j = 1:L
              if q(j)==1
                  temp=nf(i+1,j);
                  nf(i+1,j)=nf(i,j);
                  nf(i,j)=temp;
              end
          end
       end
   end
   
   i=1;
   while i<=round(NP*Pm)%以一定的概率种群挑选个体进行变异
       h=randi([1,NP],1,1);
       for j=1:round(L*Pm)%个体的基因以一定的概率进行变异
           g=randi([1,L],1,1);
           nf(h,g)=~nf(h,g);
       end
       i=i+1;
   end
   f=nf;
   f(i,:)=fBest;%把最好的给拿进去
   trace(k)=maxFit;
end
%下面就是对于进化过程的一个描述了
for i = 1:NP
    U=f(i,:);
    m=0;
    for j=1:L
        m=U(j)*2^(j-1)+m;
    end
    x(i)=Xx+m*(Xs-Xx)/(2^L-1);
    Fit(i)=func1(x(i));
end
disp(xBest);
figure
plot(trace);
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')
figure
x1=0:0.01:10;%生成变量
y=x1+10*sin(5*x1)+7*cos(4*x1);
plot(x1,y)
hold on
plot(xBest,func1(xBest),'*')
plot(x,Fit,'o')
axis([0,10,-20,28])
xlabel('x')
ylabel('y')
title('f(x)=x+10sin(5x)+7cos(4x)')


    

其运行结果如下

  下面这一个是求一个10维变量的平方和最小值,变量多可以采用实数编码。

%对于一个多维问题就可以采用实数编程了
close all;
clear all;
clc

D=10;%基因个数(自变量的维数)
NP=100;%种群数量
Xs=20;%自变量上限
Xx=-20;%自变量下界
G=1000;%迭代次数
f = zeros(D,NP);%初始种群空间矩阵
nf = zeros(D,NP);
Pc=0.8;%交叉率
Pm=0.1;%变异率
f = rand(D,NP)*(Xs-Xx)+Xx;

for np=1:NP
    FIT(np)=func2(f(:,np));%计算初始值的适应度
end
[SortFIT,Index]=sort(FIT);%降序排列
Sortf=f(:,Index);%把种群按照函数值从小到大进行排列
for gen = 1:G
  %for i = 1:2:NP%两两之间进行交叉,在此之前还需要用轮盘赌来选择个体,这里就不敲了
    %p=rand;
    %if p<=Pc %以一定的概率发生交叉
        %NoPoint=round(D*Pc);%每次交叉的基因点数
        %PoPoint=randi([1,D],NoPoint,1);%交叉的位置
        %for k = 1:NoPoint%交叉后产生的新的2个个体
            %temp=nf(PoPoint(k,i),i+1);%这里就是随机进行交配了
            %nf(PoPoint(k,i),i+1)=nf(PoPoint(k,i),i);
            %nf(PoPoint(k,i),i)=temp;
        %end
    Emper = Sortf(:,1);%选出最优的个体,选举为君主,在下面与每个在偶数位数的个体进行交叉
    NoPoint=round(D*Pc);%每次交叉的基因点数
    PoPoint=randi([1,D],NoPoint,NP/2);
    nf = Sortf;
    for i=1:NP/2%与偶数为进行交叉
        nf(:,2*i-1)=Emper;
        nf(:,2*i)=Sortf(:,2*i);
        for k = 1:NoPoint%交叉后产生的新的2个个体
            nf(PoPoint(k,i),2*i-1)=nf(PoPoint(k,i),2*i);
            nf(PoPoint(k,i),2*i)=Emper(PoPoint(k,i));
        end
    end
    for m = 1:NP%其每个个体的基因以一定的概率进行变异(全部个体均变异)
        %q=rand;
        %if q<=Pc这个可以表达个体是否变异
        for n = 1:D
            r = rand(1,1);
            if r <Pm
                nf(n,m)=rand(1,1)*(Xs-Xx)+Xx;
            end
        end
    end
    
    for np=1:NP%计算新个体的适应度
        NFIT(np)=func2(nf(:,np));
    end
    [NSortFIT,Index]=sort(NFIT);
    NSortf=nf(:,Index);%和初始化个体一样进行一次排序
    f1=[Sortf,NSortf];%将新老个体进行一次拼接
    FIT1 = [SortFIT,NSortFIT];%将适应度值也进行拼接(按照列拼接)
    [SortFIT1,Index]=sort(FIT1);%排序
    Sortf1=f1(:,Index);%按照适应度大小进行排序
    SortFIT=SortFIT1(1:NP);%选取前面的NP个个体的适应度
    Sortf=Sortf1(:,1:NP);%选取前NP个个体的值
    trace(gen)=SortFIT(1);
end
Bestf=Sortf(:,1)
trace(end);
plot(trace)
           
    
function result = func2(x)
summ=sum(x.^2);
result = summ;

       

       下面是一个经典的旅行商的问题,其由于题目的约束,导致其变异和交叉过程由随机过程实现,但是实际上也可以由变异率和交叉率来实现,只不过要复杂一点点,这里只是了解过程,就不具体展开。

clear all;
close all;
clc;
%下面来处理一个典型的旅行商的问题
C = [1304 2312;3639 1315;4177 2244;3712 1399;3488 1535;3326 1556;3238 1229;4196 1004;4312 790;4386 570;
    3007 1970;2562 1756;2788 1491; 2381 1676;1332 695;3715 1678;3918 2179;4061 2370;3780 2212;3676 2578;
    4029 2838;4263 2931;3429 1908;3507 2367;3394 2643;3439 3201;2935 3240;3140 3550;2545 2357;2778 2826;2370 2975];
N=size(C,1);%城市数目
D = zeros(N);%任意2个矩阵的间隔矩阵
for i= 1:N
    for j = 1:N
        D(i,j)= ((C(i,1)-C(j,1))^2+(C(i,2)-C(j,2))^2)^0.5;
    end
end
NP=200;%种群数量
G=1000;%最大遗传代数
f = zeros(NP,N);%开辟种群的矩阵
F=[];
Rlenth=zeros(1,G);
for i=1:NP%初始化矩阵
    f(i,:)=randperm(N);
end
R = f(1,:);%用来存储最优解
len = zeros(NP,1);%存储路径长度
fitness=zeros(NP,1);%存储归一化适应值
for k= 1:G
    for i = 1:NP%计算距离
        len(i,1)=D(f(i,N),f(i,1));
        for j = 1:(N-1)
            len(i,1)=len(i,1)+D(f(i,j),f(i,j+1));
        end
    end
    maxlen=max(len);
    minlen=min(len);%归一化
    Rlenth(k)=minlen;
    rr = find(len==minlen);
   R=f(rr(1,1),:);%最优解
   for i=1:length(len)
       fitness(i,1)=(1-((len(i,1)-minlen)/(maxlen-minlen+0.001)));
   end
   nn=0;
   for i=1:NP
       if fitness(i,1)>=rand
           nn=nn+1;
           F(nn,:)=f(i,:);
       end
   end
   [aa,bb]=size(F);
   while aa<NP
       nnper = randperm(nn);%随机进行交叉
       A=F(nnper(1),:);
       B=F(nnper(2),:);
       W = ceil(N/10);%交叉点的个数,向正无穷进行取整
       p=unidrnd(N-W+1);%选择开始交叉的点
       for i = 1:W
           x = find(A==B(p+i-1));
           y = find(B==A(p+i-1));
           temp = A(p+i-1);
           A(p+i-1)=B(p+i-1);
           B(p+i-1)=temp;
           temp=A(x);
           A(x)=B(y);
           B(y)=temp;
       end%到此交叉完毕
       p1=floor(1+N*rand());
       p2=floor(1+N*rand());%向负方向取整
       while p1==p2
          p1=floor(1+N*rand());
          p2=floor(1+N*rand());%向负方向取整 
       end
       tmp = A(p1);
       A(p1)=A(p2);
       A(p2)=tmp;
       tmp=B(p1);
       B(p1)=B(p2);
       B(p2)=tmp;
       F = [F;A;B];
       [aa,bb]=size(F);
   end
   if aa>NP
       F=F(1:NP,:);
   end
   f =F;
   f(1,:)=R;%保留每带最优个体
   clear F;
  
end
figure
for i = 1:N-1
    plot([C(R(i),1),C(R(i+1),1)],[C(R(i),2),C(R(i+1),2)],'bo-');
    hold on;
end
plot([C(R(N),1),C(R(1),1)],[C(R(N),2),C(R(1),2)],'bo-');
title(['优化最短距离:',num2str(minlen)]);
figure
plot(Rlenth)
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')
    







       

下面是一个背包问题,其二进制是代表是否选取该物品,但如何控制背包容量的问题呢?这里可以通过有一个惩罚因素来控制背包容量,同时如果还要控制物品件数的话也可以加入对于物件数的惩罚因子。

%下面来叙述一下一个关于背包的问题
%一个背包容量为300,物品的数量为10,有着不同的一个容积和价值
%怎么样才能在容积的范围内是的价值最大
%选择二进制编码
clear all;
close all;
clc;
NP=50;%种群的数量
L=10;%10种物品用长度为10的来表示状态
Pc=0.8;%交叉概率
Pm=0.05;%变异概率
G=100;%迭代次数
V=300;%背包容积
C=[95,75,23,73,50,22,6,57,89,98];%物品体积
W=[89,59,19,43,100,72,44,16,7,64];%无物品价值
afa=2;%体积惩罚函数限制
f=randi([0,1],NP,L);%初始化种群数量
for k= 1:G
    for i = 1:NP
        Fit(i)=func4(f(i,:),C,W,V,afa);
    end
    maxFit=max(Fit);
    minFit=min(Fit);
    rr=find(Fit==maxFit);%知道这个历史最优的个体位置
    fBest=f(rr(1,1),:);%找到这个最优个体的值
    Fit=(Fit-minFit)/(maxFit-minFit);
    sum_Fit=sum(Fit);
    fitvalue=Fit./sum_Fit;
    fitvalue=cumsum(fitvalue);
    ms=sort(rand(NP,1));
    fiti=1;
    newi=1;
    while newi<=NP%轮盘赌进行选择
        if (ms(newi))<fitvalue(fiti)
            nf(newi,:)=f(fiti,:);
            newi=newi+1;
        else
            fiti=fiti+1;
        end
    end
    for i = 1:2:NP%两两之间进行交叉
       p=rand;
       if p<Pc
          q=randi([0,1],1,L);
          for j = 1:L
              if q(j)==1
                  temp=nf(i+1,j);
                  nf(i+1,j)=nf(i,j);
                  nf(i,j)=temp;
              end
          end
       end
    end
   for m=1:NP
       for n=1:L
           r = rand(1,1);
           if r<Pm
               nf(m,n)=~nf(m,n);
           end
       end
   end
   f=nf;
   f(i,:)=fBest;%把最好的给拿进去
   trace(k)=maxFit;
end
disp(fBest);
figure
plot(trace);
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')   


function result=func4(f,C,W,V,afa)
fit=sum(f.*W);
TotalSize=sum(f.*C)
if TotalSize<=V
    fit=fit;
else
    fit = fit-afa*(TotalSize-V);
end
result=fit;

五、总结

       通过以上介绍可以看出,遗传算法最为一个参数优化算法本身就存在许多参数,那么这些参数怎么控制,同时其遗传算法对于选择、交叉和变异的过程不同的问题实现方式也不一致,为此怎么去确定这些不确定约束呢,现在还没有合理的方法,不然就出来不了那么多改进遗传算法了。

       同时我们也会发现这一整套机制首当其冲的问题就是编码,每个基因点的具体含义是由自己定义的,因此如何内化一个问题是很关键的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值