进化算法介绍及实现(遗传算法)
进化算法也可以称为遗传算法。进化算法的求解过程是模拟大自然生物进化的过程,通过“适者生存,劣者 淘汰”的规则不断进化,直到找到最优解或者达到终止条件。详细的介绍可以百度或者查看论文在这不做过多的介绍
进化算法的实现可以想象为“上帝”,通过控制种群的选择,杂交,变异策略,不断优化种群的过程。
文章目录
一、进化计算的简介
进化计算的详细介绍大家可以百度或者查看知网论文了解,这个只大致介绍。以下为进化算法的基本框架的流程图:
二、进化计算的基本要素
进化计算的结构尽管比较简单,但每一部分的不同处理方式,都会影响算法 的效率和最终结果,进化算法有以下 7 个主要的要素。
1. 个体的编码
常见的编码方式有二进制编码和实数编码,本文的采用的就是实数编码。
2.适应值函数
在进化算法中,适应函数是用于评价种群中的每个个体,是进行父代种群选 择的依据,也是选择新一代种群的依据,适应度函数会影响种群多样性和算法的
3.父代选择策略
父代选择的目的是希望较好的父代个体进行杂交和变异操作后,期望能够得 到更好的后代。不同的父代选择方式会影响到算法的收敛速度;若是每次都选择 最好的个体作为父代,算法容易陷入局部最优。而如果选择压力过小,即个体较 好的较少被选择成为父代,虽然可以保持种群的多样性,增加算法收敛到最优的 概率,但算法的收敛速度会缓慢,可能在达到终止条件时也求不出最优解[7]。 所以保持一个适当的选择父代的压力是一个重要问题。一般来说,算法的初 始阶段宜采用较低的选择压力,这有利于扩展搜索空间,而在算法的后期,宜采 用较大的选择压力,这有利于算法找到更好的解。 常见的父代选择方式有轮盘赌选择,锦标赛选择,基于排名的选择方式等等。
4.变化算子
变化算子主要有:杂交算子和变异算子。
(1)杂交算子 杂交算子是将两个个体的信息进行组合,产生一个或者多个后代。杂交算子 是期望通过重组较好的父代的信息得到更好的后代。一般是通过两个父代之间进 行杂交产生两个新的个体。杂交的方式有多种,采用不同的杂交方式对算法的收 敛和算法的效率也同样有很大的影响。
(2)变异算子 变异算子是改变一个个体的某个染色体的信息,得到一个新的个体,其目的 是通过引入一个新的信息增加种群的多样性,扩大搜索空间,避免让算法陷入局 部最优。
5.新一代的种群选择
新一代种群选择的作用是从当代的种群和父代所产生的后代中选择出一组 个体以形成新一代种群。一般新一代种群选择是通过适应值比较,按照当代种群 中所有个体的适应值排序,然后选取最好的个体形成下一代。
6.种群的初始化
种群的初始化可以在可行域的空间中随机生成,或者通过佳点集策略在可行 域中生成均匀分布的种群,也可以使用启发式方法初始化种群,使算法可以更快 的得到相对较优的解。
7.终止条件
一般算法的终止条件有以下几种[7]:
(1) 算法的执行时间达到预先指定的最大运行时间。
(2) 算法计算个体的适应值的总数达到预先指定的最大次数。
(3) 算法的进化次数达到预先指定的最大代数。
(4) 在算法连续若干代以后,个体适应值没有明显的变化。
(5) 种群的多样性降到某个预先指定的阈值。
算法的实际终止条件可能是上述的若干条件的析取,但最常用是预先指定一 个最大的代数。
三、进化算法的实现
这里进化算法的实现采用MATLAB,测试函数采用Hedar库的测试函数——求解函数最小值。实现方法比较简单,可以当做一个参考内容,并没有考虑太多的优化效果。
Hedar test set中的测试函数,参见
链接: http://www-optima.amp.i.kyoto-u.ac.jp/member/student/hedar/Hedar_files/TestGO_files/Page364.htm.
这里放两张函数的三维模型看看:
1. Matlab代码实现框架
因为这里是求解函数的最小值,编码规则就选择实数编码。
1. 种群初始化
简单点,这里选择随机初始化种群的方法。
%初始化种群大小
% 种群大小popsize:设置种群大小
% 维度dim:个体的维度
% 取值范围bounds:每个个体每一维的取值范围
function pop=initpop(popsize,dim,bounds)
p = rand(popsize,dim); %随机生成popsize*dim的0-1矩阵
for i = 1:dim
p(:,i) = p(:,i)*(bounds(i,2)-bounds(i,1))+bounds(i,1);
end
pop = p;
end
2. 计算个体适应值
%计算函数适应值,先对函数值进行求倒,为了防止函数值为0的情况,先加上0.001,最后对进行归一化处理
function [fitvalue] = calfitvalue(objvalue)
value = 1./(objvalue+0.001);
Max = max(value);
Min = min(value);
fitvalue = (value-Min)/(Max-Min);
end
3. 父代个体的选择
这里采用最简单的方式选择父代个体,选择适应值最大的前N个个体为父代种群,进行后续的杂交和变异。这样做的一个优点是可以加快种群的进化速度,缺点是容易陷入局部最优,要做到一个更好的优化效果建议采用其他策略。
function [newpop] = selection(pop,fitvalue)
%pop:种群
%fitvalue:种群每个个体对应的适应值
[~,index]= sort(fitvalue,'descend');
[x,y] = size(pop);
newpop = zeros(x,y);
for i = 1:ceil(x/1.2)
newpop(i,:) = pop(index(i),:);
end
end
4. 个体杂交方法
个体杂交方式也采用比较简单的方案,在父代种群中随机选择两个个体进行杂交,根据杂交概率是否进行杂交。生成一个杂交因子n,父代1乘以n加上父代2乘以(1-n);第二个子代交换顺序相乘。最后将父代和子代合并称为一个新的种群,进行下一步的变异。
%交叉变换
% pop:种群
% pc:杂交概率,用于决定所选的两个个体是否进行交叉变换--杂交
function [newpop] = crossover(pop,pc)
[px,~] = size(pop);
newpop = pop;
childpop = [];
for i = 1:px
x = ceil(rand(1,2)).*px; %生成两个随机数选择个体进行交换生成新个体
if(rand<pc)
n = rand;
newpop1 = pop(x(1),:).*n+pop(x(2),:)*(1-n);
newpop2 = pop(x(1),:).*(n-1)+pop(x(2),:)*n;
childpop = [childpop ;newpop1; newpop2];
end
end
newpop = [newpop;childpop];%和并父代种群和子代个体
end
5. 个体变异方法
遍历每一个个体,遍历个体的的每一个基因(维度),根据变异概率决定该基因是否变异。
%变异算子
% pop:种群
% pc:变异概率,个体单个基因(维度)变异的概率
function [newpop] = mutation(pop,pm,bounds)
[px,py] = size(pop);
newpop = pop;
for i = 1:px
for j = 1:py
if(rand<pm) % 小于pm,变异,变异为取值范围内的随机值
newpop(i,j) = rand * (bounds(j,2)-bounds(j,1))+bounds(j,1);
end
end
end
end
6. 新一代种群选择
在当前种群中根据适应值排序选择新一代种群
function [newpop,newfvalue] = updatepop(pop,popsize,fitvalue)
[px,py] = size(pop);
if px<=popsize
newpop = pop;
newfvalue = fitvalue;
return;
end
newpop = zeros(popsize,py);
newfvalue = zeros(popsize,1);
[~,index]= sort(fitvalue,'descend');
for i = 1:popsize
newpop(i,:) = pop(index(1),:);
newfvalue(i,:) = fitvalue(index(1),:);
end
end
7. 计算函数值
在这里就不计算所有的Hedar库的所有函数了,Hedar共有27个函数,有些函数有多个不同维度可以计算。在这里就随便几个函数和对应的维度测试。
function y = calfun(x)
global fvals nfev nprob %用于保存函数值历史和计算次数
switch nprob
case 1
y = ackley(x); %2d
case 2
y = ackley(x); %5d
case 3
y = ackley(x); %10d
case 4
y = ackley(x); %20d
case 5
y = beale(x);
case 6
y = bh1(x);
case 7
y = bh2(x);
case 8
y = bh3(x);
case 9
y = booth(x); %
case 10
y = branin(x);
end
nfev = nfev + 1;%保存计算次数
8. 最佳个体计算函数
function [bestindividual,bestfit] = best(pop,fitvalue,bestindividual,bestfit)
[px,~] = size(pop);
if isempty(bestindividual) % 初始种群
bestindividual = pop(1,:);
bestfit = fitvalue(1);
end
for i = 1:px
if fitvalue(i)<bestfit % 最小化问题
bestindividual = pop(i,:);
bestfit = fitvalue(i);
end
end
end
8. 主函数
function bestfit = myGA_2(bounds,popsize,pc,pm)
% bounds 搜索区域
% popsize 种群大小
% pc 杂交概率
% pm 变异概率
global nfev maxnfev % 函数值计算次数、最大函数值计算次数
if nargin<2
popsize = 200; % 种群大小
end
if nargin<3
pc = 0.8;%杂交概率
end
if nargin<4
pm = 0.05;%变异概率
end
[dim,~] = size(bounds);
pop = initpop(popsize,dim,bounds);%初始种群
objvalue = zeros(popsize,1);
bestindividual = [];
bestfit = [];
while nfev<maxnfev
for j=1:size(pop,1)
objvalue(j) = calfun(pop(j,:));% 评估种群适应值
end
fvalue = calfitvalue(objvalue);
%寻找最优解
[bestindividual,bestfit] = best(pop,objvalue,bestindividual,bestfit);
%更新种群
[newpop,newfvalue] = updatepop(pop,popsize,fvalue);
%选择操作
pop = selection(newpop,newfvalue);
%交叉操作
newpop = crossover(pop,pc);
%变异操作
pop = mutation(newpop,pm,bounds);
end
end
9. 测试函数
function [H] = testMyGA()
% H : 算法求解Hedar库的测试结果,只保存最佳结果。如果有需要保存其他数据,请自行添加,改造函数
% N : 问题维数向量
% c: 描述要比较的算法编号
global nfev nprob maxnfev %用于计算次数、问题编号、最大函数值计算次数
maxnfev = 50000; % 最大函数值计算次数
nfev = 0; % 函数值计算次数初值
maxnp = 10; % 测试函数个数
maxrun = 2; % 运行多少次算法,适用于随机算法;随机算法,每次计算的结果都不一致。
H = ones(maxrun ,maxnp);
for nprob=1:maxnp
fprintf('Begin to test the %3d th problem\n',nprob);
bounds = getbounds(nprob);
popsize = 100;
pc = 0.6;
pm = 0.05;
for r = 1:maxrun
bestfit = myGA_2(bounds,popsize,pc,pm);
nfev = 0;
H(r,nprob) = bestfit;
end
end
clear global
end
%每个问题的取值范围
function [bounds] = getbounds(nprob)
switch nprob
case 1
bounds = [-15*ones(2,1) 30*ones(2,1)];
case 2
bounds = [-15*ones(5,1) 30*ones(5,1)];
case 3
bounds = [-15*ones(10,1) 30*ones(10,1)];
case 4
bounds = [-15*ones(20,1) 30*ones(20,1)];
case 5
bounds = [-4.5*ones(2,1) 4.5*ones(2,1)];
case 6
bounds = [-80*ones(2,1) 125*ones(2,1)]; % 与hedar的界不同,左边乘以0.8,右边除以0.8
case 7
bounds = [-80*ones(2,1) 125*ones(2,1)]; % 与hedar的界不同,左边乘以0.8,右边除以0.8
case 8
bounds = [-80*ones(2,1) 125*ones(2,1)]; % 与hedar的界不同,左边乘以0.8,右边除以0.8
case 9
bounds = [-10*ones(2,1) 10*ones(2,1)];
case 10
bounds = [-5 10;0 15];
end
end
2. 测试结果
这个测试结果是函数计算次数才5W次,运行一次所得的结果,由于结果我只保存小数点后四位,基本上只有第5,9,10函数与最优解有一定的差距,想要缩短差距,可以设置更大函数计算次数,计算多次以及改进进化策略。
函数名 | 维度 | 最优解 | 测试结果 |
---|---|---|---|
ackley | 2 | 0 | 0 |
ackley | 5 | 0 | 0 |
ackley | 10 | 0 | 0 |
ackley | 20 | 0 | 0 |
beale | 2 | 0 | 0.0060 |
bh1 | 2 | 0 | 0 |
bh2 | 2 | 0 | 0 |
bh3 | 2 | 0 | 0 |
booth | 2 | 0 | 0.0066 |
branin | 2 | 0.397887 | 0.3991 |