由美国卡耐基梅隆大学的 Baluja 在 1994 年提出,该算法被公认是最早的分布估计算法模型.
经典案例:利用PBIL算法解决二进制编码的优化问题.
问题描述:假设当前种群有20个个体,每个个体中有三个变量,每个变量由一个长度为20的二进制串表示,求解经过1000次进化后每个变量存在的最优二进制串
因为每个变量中每个二进制串都是由0或1组成的,所以二者初始出现出现的概率均为50%,在MATLAB实现中,我们可以使用rand函数先随机生成二进制串。
(1)初始化一些基本常量
Pop_Size=40; %种群中个体数量
Variable_Num=2; %每个个体中种群的变量数
Individual_Len=20;%每个变量的长度(相当于一个二进制数)
Iteration_Times=1000;%进化次数
I=1;%表示第几次进化过程
Learning_Rate=0.01;%学习速率
(2)、随机产生初始种群
说明:1、 zeros 函数用来生成一定维数且值为0的矩阵 例如zeros(2,3,4)生成x=2, y=3, z=4的矩阵
2、for i=1:1:x 与C语言for循环类似,即i从1循环到x
Binary_X=zeros(Pop_Size,Variable_Num,Individual_Len);%生成值为0的种群矩阵Binary_X
for i=1:1:Pop_Size %Pop_Size=40
for j=1:1:Variable_Num %Variable_Num=2
for k=1:1:Individual_Len %Individual_Len=20
Binary_X(i,j,k)=round(rand()); %round函数用于四舍五入随机产生的值 rand()
end %函数随机产生 0到1 之间的小数 round函数将小数四舍五入为0或1
end
end
%zeros是MATLAB内的一个函数。
%其功能是返回一个m×n×p×...的double类零矩阵。注意:m, n, p,...必须是非负整数
%负整数将被当做0看待。
%二维用法:zeros(m,n)或zeros(n)
%功能:zeros(m,n)产生m×n的double类零矩阵,zeros(n)产生n×n的全0方阵。
随机产生后的一个种群大致是这样的(仅是一个样例)
这样,一个初始种群就随机生成了,其信息存储到了Binary_x矩阵中(如上图)
(3)初始化进化过程中需要用到的存储变量
Best_Individual=zeros(1,Iteration_Times); %初始化值为0的优势种群矩阵,用于记录每次进化最好情况
Probability_Vector=zeros(Iteration_Times,Variable_Num,Individual_Len);%初始化概率向量矩阵
traces=zeros(3,Iteration_Times);%追踪每一代的最优值,trace为最优值矩阵,用来记录每一代的最优值
既然要进行选择,我们就要据每个个体的适应值来评估种群适应度进而筛选优势个体,所以第二步又分三小步
(1)将二进制串转化为十进制数
(2)将十进制映射到解空间
(3)通过十进制数来计算个体适应值
(1)、将二进制串转化为十进制数(便于排序筛选)
这一步操作用来计算适应值,由于二进制加减人工模拟不方便,我们将其转化为十进制通过进化过程中的适应值函数统计每个个体适应值来选择最优个体
Decimal_X=zeros(Pop_Size,Variable_Num);%初始化十进制矩阵
for i=1:1:Pop_Size
for j=1:1:Variable_Num
k=Individual_Len; %Individual_Len=20
t=1;
while k>=1%将二进制矩阵转化为十进制矩阵,便于后面进行排序
Decimal_X(i,j)=Decimal_X(i,j)+Binary_X(i,j,k)*2^(t-1);%将一位二进制转化为十进制
k=k-1; %右移一位,切换到下一位转换
t=t+1;
end
end
end
二进制转化为十进制后大致是这样的(仅为一个样例)
(2)将十进制映射到解空间
Solution=zeros(Pop_Size,Variable_Num);%初始化解空间矩阵(二维)
for i=1:1:Pop_Size
for j=1:1:Variable_Num
Solution(i,j)=-2+Decimal_X(i,j)*4/(2^Individual_Len-1);%存储每个个体对应变量的解
end
end
(3)通过十进制数来计算个体适应值
Fitness_Value=zeros(1,Pop_Size);%初始化适应值矩阵(一维表针对每个个体)
for i=1:1:Pop_Size % .*表示矩阵之间元素按位置相乘
Fitness_Value(i)=Solution(i,1).*cos(2*pi*Solution(i,2))+Solution(i,2).*cos(2*pi*Solution(i,1));
end %计算每个个体的适应值,相当于求f(x)的值
通过循环个体计算将每个个体对应的适应
经过一系列适应值函数计算之后得到了每个个体的适应值(仅为一个样例)
(1)对种群优势情况进行排序,并记录原种群信息索引位置,便于之后查找
因为我们已经得出种群每个个体的适应值,然后按照适应值进行排序,适应值越高的其优势越强,选出前一半优势群体为下一步构建概率模型做准备
[FitnessValue,index]=sort(Fitness_Value);%排序适应值(升序),FinnessValue存储排序好的值,index存储对应值在原函数的索引
Best_Individual(I)=Fitness_Value(index(Pop_Size));%存储本次进化最优个体(升序)对应索引位置的值
traces(1,I)=Solution(index(Pop_Size),1);%存储每代最优个体各变量的值及最优值
traces(2,I)=Solution(index(Pop_Size),2);
traces(3,I)=Fitness_Value(index(Pop_Size));
为什么要建立索引?
我们之前转化的十进制数是为了便于排序,但排序时仅仅排序的是十进制数,而对应的二进制串没有排序,所以要给每个十进制数排序号后带上索引,之后在找二进制串时就可以直接通过索引找到了。
通过排序,我们可以达到一个有序适应值的种群和对应原数据的索引(仅为样例)
此时我们就可以知道本次迭代所有个体中最优个体的适应值(仅为样例)
(2)按照排序好的优势情况,根据索引找到对应优势群体信息并存放到新种群中
Superiority_Polution=zeros(Pop_Size/2,Variable_Num,Individual_Len);%只从所有个体选择一半作为优势个体,初始化优势种群矩阵
for i=1:1:Pop_Size/2 %Pop_Size/2决定优势种群个体数
for j=1:1:Variable_Num
for k=1:1:Individual_Len
Superiority_Polution(i,j,k)=Binary_X(index(i+Pop_Size/2),j,k);
end %由于之前是升序排序,且是选择一半优势个体所以优势个体都是从中间开始向后才有的
end %索引在这里起到了很重要的作用,不能使用FitnessValue矩阵因为矩阵内是十进制数
end %而索引在这里可以帮助寻找原矩阵中的二进制数
我么就可以通过索引找到优势个体并放到优势种群中了,这里默认为两个优势群体(仅为样例)
(1)统计概率模型并更新
Ones_Number=zeros(Variable_Num,Individual_Len);
for i=1:1:Pop_Size/2
for j=1:1:Variable_Num
for k=1:1:Individual_Len
if Superiority_Polution(i,j,k)==1%如果优势种群中某个体中某变量中二进制串某一位为1
Ones_Number(j,k)=Ones_Number(j,k)+1;%让对应变量对应位在下次产生1的概率增加1
end
end
end
end
for j=1:1:Variable_Num
for k=1:1:Individual_Len
Probability_Vector(I,j,k)=Ones_Number(j,k)/(Pop_Size/2);%更新每一变量每一位可能出现1概率向量采用百分比形式
end
end
统计出来的概率模型即为下图(仅为样例)
(2)进一步更新概率模型
在进化过程中除了先天的自然选择之外,还有后天自主的学习促使了进化,PBIL算法考虑到了这一点,所以在进化的时候还要加上学习速率,在之前的概率向量中仅更新了自然选择的部分,所以这里要进一步更新,PBIL算法给出了以下公式用于进一步更新概率模型
其中α指的是学习速率,(1-α)则是先天遗传部分,pi(x)则是之前每个变量更新后自然选择概率。至此,第一次进化中的概率模型就构建好了
if I>1 %如果这不是第一次进化
for j=1:1:Variable_Num
for k=1:1:Individual_Len
Probability_Vector(I,j,k)=Learning_Rate.*Binary_X(index(Pop_Size),j,k)+(1-Learning_Rate).*Probability_Vector(I-1,j,k);
end
end
end
通过概率模型我们就可以产生这一次进化的新种群了
for i=1:1:Pop_Size
for j=1:1:Variable_Num
for k=1:1:Individual_Len
r=rand();%随机生成数0到1之间的小数r
if r
根据概率随机产生后,产生下面的一个新种群(仅为样例)
这样子每个变量二进制串每一位在上一次出现概率较高的数,在第二次出现的概率也就会有所提高
对于下一步评估种群适应度可以在迭代开始进行
至此,一次完整的迭代(进化)过程也就结束了!
进化过程是一个漫长的过程,需要可能多代进化才有可能得到最优解,在这里为了保证得到最优解,我们以迭代次数为判断条件,当达到一定的迭代次数后,取得的优解也就相对较稳定了,我们以迭代1000次为判断是否结束迭代
因此,我们可以将之前的迭代过程放入一个循环中,循环条件是判断迭代次数是否小于1000
while(i<=1000)
{
//迭代开始
//
//迭代结束
i=i+1 %迭代次数加1
}
绘制图像观察其变化
可以观察到,起初二进制串中0和1的概率基本持平均为2.5,但随着每一代优势个体的筛选以及概率模型的更新二进制串0或1中适应性出现的概率不断提高,最终保持稳定得到最优解的二进制串(理想情况)
至此,一个完整的PBIL算法实例到这里就演示结束了!
附算法完整代码:
%%%%%%%%%%%%PBIL algorithm
clc
clear
clf
tic %开始计时
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%参数设置%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Pop_Size=40; %种群中个体数量
Variable_Num=2; %每个个体中种群的变量数
Individual_Len=20;%每个变量的长度(相当于一个二进制数)
Iteration_Times=1000;%进化次数
I=1;%表示第几次进化过程
Learning_Rate=0.01;%学习速率
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%产生初始种群%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Binary_X=zeros(Pop_Size,Variable_Num,Individual_Len);%生成值为0的种群矩阵Binary_X
for i=1:1:Pop_Size %Pop_Size=40
for j=1:1:Variable_Num %Variable_Num=2
for k=1:1:Individual_Len %Individual_Len=20
Binary_X(i,j,k)=round(rand()); %round函数用于四舍五入随机产生的值 rand()
end %函数随机产生 0到1 之间的小数 round函数将小数四舍五入为0或1
end
end
%zeros是MATLAB内的一个函数。
%其功能是返回一个m×n×p×...的double类零矩阵。注意:m, n, p,...必须是非负整数
%负整数将被当做0看待。
%二维用法:zeros(m,n)或zeros(n)
%功能:zeros(m,n)产生m×n的double类零矩阵,zeros(n)产生n×n的全0方阵。
Best_Individual=zeros(1,Iteration_Times); %初始化值为0的优势种群矩阵,用于记录每次进化最好情况
Probability_Vector=zeros(Iteration_Times,Variable_Num,Individual_Len);%初始化概率向量矩阵
traces=zeros(3,Iteration_Times);%追踪每一代的最优值,trace为最优值矩阵,用来记录每一代的最优值
%%%%%%%%%%%%%%%%%%%%%%%%%%开始进行迭代%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
while I<=Iteration_Times
%%%%%%%%%%%%%%%%%%%%%%%将采样的值,由二进制转化到十进制%%%%%%%%%%%%%%%%%%%%%%
Decimal_X=zeros(Pop_Size,Variable_Num);%初始化十进制矩阵
for i=1:1:Pop_Size
for j=1:1:Variable_Num
k=Individual_Len; %Individual_Len=20
t=1;
while k>=1%将二进制矩阵转化为十进制矩阵,便于后面进行排序
Decimal_X(i,j)=Decimal_X(i,j)+Binary_X(i,j,k)*2^(t-1);%将一位二进制转化为十进制
k=k-1; %右移一位,切换到下一位转换
t=t+1;
end
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%将十进制映射到解空间中%%%%%%%%%%%%%%%%%%%%%%%%%%%
Solution=zeros(Pop_Size,Variable_Num);%初始化解空间矩阵(二维)
for i=1:1:Pop_Size
for j=1:1:Variable_Num
Solution(i,j)=-2+Decimal_X(i,j)*4/(2^Individual_Len-1);%存储每个个体对应变量的解
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%计算适应值%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Fitness_Value=zeros(1,Pop_Size);%初始化适应值矩阵(一维表针对每个个体)
for i=1:1:Pop_Size % .*表示矩阵之间元素按位置相乘
Fitness_Value(i)=Solution(i,1).*cos(2*pi*Solution(i,2))+Solution(i,2).*cos(2*pi*Solution(i,1));
end %计算每个个体的适应值,相当于求f(x)的值
%%%%%%%%%%%%%%%%将适应值按照从小到大的顺序排序,并选出最优个体%%%%%%%%%%%%%%%
[FitnessValue,index]=sort(Fitness_Value);%排序适应值(升序),FinnessValue存储排序好的值,index存储对应值在原函数的索引
Best_Individual(I)=Fitness_Value(index(Pop_Size));%存储本次进化最优个体(升序)对应索引位置的值
traces(1,I)=Solution(index(Pop_Size),1);%存储每代最优个体各变量的值及最优值
traces(2,I)=Solution(index(Pop_Size),2);
traces(3,I)=Fitness_Value(index(Pop_Size));
%%%%%%%%%%%%%%%%%%%%%%%%%%%%选出优势群体%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
Superiority_Polution=zeros(Pop_Size/2,Variable_Num,Individual_Len);%只从所有个体选择一半作为优势个体,初始化优势种群矩阵
for i=1:1:Pop_Size/2 %Pop_Size/2决定优势种群个体数
for j=1:1:Variable_Num
for k=1:1:Individual_Len
Superiority_Polution(i,j,k)=Binary_X(index(i+Pop_Size/2),j,k);
end %由于之前是升序排序,且是选择一半优势个体所以优势个体都是从中间开始向后才有的
end %索引在这里起到了很重要的作用,不能使用FitnessValue矩阵因为矩阵内是十进制数
end %而索引在这里可以帮助寻找原矩阵中的二进制数
%%%%%%%%%%%%%%%%从优势群体中统计基因位的值,来更新概率向量%%%%%%%%%%%%%%%%%%%
Ones_Number=zeros(Variable_Num,Individual_Len);
for i=1:1:Pop_Size/2
for j=1:1:Variable_Num
for k=1:1:Individual_Len
if Superiority_Polution(i,j,k)==1%如果优势种群中某个体中某变量中二进制串某一位为1
Ones_Number(j,k)=Ones_Number(j,k)+1;%让对应变量对应位在下次产生1的概率增加1
end
end
end
end
for j=1:1:Variable_Num
for k=1:1:Individual_Len
Probability_Vector(I,j,k)=Ones_Number(j,k)/(Pop_Size/2);%更新每一变量每一位可能出现1概率向量采用百分比形式
end
end
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%更新概率向量%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
if I>1 %如果这不是第一次进化
for j=1:1:Variable_Num
for k=1:1:Individual_Len
Probability_Vector(I,j,k)=Learning_Rate.*Binary_X(index(Pop_Size),j,k)+(1-Learning_Rate).*Probability_Vector(I-1,j,k);
end
end
end
%%%%%%%%%%%%%%%%%%%%%%%%根据概率向量对解空间采样%%%%%%%%%%%%%%%%%%%%%%%%%%%%
for i=1:1:Pop_Size
for j=1:1:Variable_Num
for k=1:1:Individual_Len
r=rand();%随机生成数0到1之间的小数r
if r