用遗传算法求解非线性问题是常见的求解算法之一,求解的过程就是随机生成解,计算适应度,然后选择,交叉,变异,更新种群,不断迭代,这样,每个个体都会向每代中最佳的个体学习并靠拢,这是局部最优解;而变异操作是为了在靠近当前最优解的同时还有机会变异出更佳的基因,从而跳出局部最优解而达到全局最优解。
而有时候,面对一个很复杂的非线性函数,或者是根本无法用确定的表达式描述的离散非线性函数,在计算适应度时就会产生很大的问题,比如计算时间过长,解出式子需要半个小时;比如无法计算,只有贴近的离散点。这样,传统的遗传算法无法达到我们期望的速度和要求,我们就需要引入其他辅助遗传算法的内容。
这里,我们引入了BP神经网络:有导师学习的误差前馈神经网络。BP神经网络随机初始化权值与阈值,并通过已有的训练数据和训练期望,将计算出来的误差前馈给神经网络,也就是归结为权值和阈值的“过错”。这样,权值和阈值不断得到修改,最终形成逼近训练数据和期望的模型。
所以,我们在遗传算法的主过程中,先迭代一部分的次数,得到一部分种群个体和适应度值,用来训练BP神经网络。接下来的迭代中,将一部分的种群个体的适应度直接用BP模型求出来,而另一部分的种群个体适应度仍然用原函数求出(或者原来的预测曲线),将这一部分的种群个体和适应度再次带入BP神经网络中训练网络,使网络越来越精准。神经网络的求值是很快的,最后时间复杂度将会大大降低。
在下面的例子中,有一个很简单的例子:。这是一个很简单的非线性函数,我们用上面的思路来模拟一遍解法,最后得出最优解和一个逼近此函数的BP网络模型。
首先,遗传算法的基本函数如下
1.选择函数,以每个个体的适应度为概率选择优秀个体,更新种群select.m
function ret=select(individuals,sizepop)
% 本函数对每一代种群中的染色体进行选择,以进行后面的交叉和变异
% individuals input : 种群信息
% sizepop input : 种群规模
% opts input : 选择方法的选择
% ret output : 经过选择后的种群
individuals.fitness= 1./(individuals.fitness);
sumfitness=sum(individuals.fitness);
sumf=individuals.fitness./sumfitness;
index=[];
for i=1:sizepop %转sizepop次轮盘
pick=rand;
while pick==0
pick=rand;
end
for j=1:sizepop
pick=pick-sumf(j);
if pick<0
index=[index j];
break; %寻找落入的区间,此次转轮盘选中了染色体i,注意:在转sizepop次轮盘的过程中,有可能会重复选择某些染色体
end
end
end
individuals.chrom=individuals.chrom(index,:);
individuals.fitness=individuals.fitness(index);
ret=individuals
2.交叉函数:Cross.m
function ret=Cross(pcross,lenchrom,chrom,sizepop,bound)
%本函数完成交叉操作
% pcorss input : 交叉概率
% lenchrom input : 染色体的长度
% chrom input : 染色体群
% sizepop input : 种群规模
% ret output : 交叉后的染色体
for i=1:sizepop
% 随机选择两个染色体进行交叉
pick=rand(1,2);
while prod(pick)==0
pick=rand(1,2);
end
index=ceil(pick.*sizepop);
% 交叉概率决定是否进行交叉
pick=rand;
while pick==0
pick=rand;
end
if pick>pcross
continue;
end
flag=0;
while flag==0
% 随机选择交叉位置
pick=rand;
while pick==0
pick=rand;
end
pos=ceil(pick.*sum(lenchrom)); %随机选择进行交叉的位置,即选择第几个变量进行交叉,注意:两个染色体交叉的位置相同
pick=rand; %交叉开始
v1=chrom(index(1),pos);
v2=chrom(index(2),pos);
chrom(index(1),pos)=pick*v2+(1-pick)*v1;
chrom(index(2),pos)=pick*v1+(1-pick)*v2; %交叉结束
flag1=test(lenchrom,bound,chrom(index(1),:)); %检验染色体1的可行性
flag2=test(lenchrom,bound,chrom(index(2),:)); %检验染色体2的可行性
if flag1*flag2==0
flag=0;
else flag=1;
end %如果两个染色体不是都可行,则重新交叉
end
end
ret=chrom;
其中test函数为test.m,判断是否合法
function flag=test(lenchrom,bound,code)
%判断是否合法
% lenchrom input : 染色体长度
% bound input : 变量的取值范围
% code output: 染色体的编码值
flag=1;
[n,m]=size(code);
for i=1:n
if code(i)<bound(i,1) || code(i)>bound(i,2)
flag=0;
end
end
3.变异函数:Mutation.m
function ret=Mutation(pmutation,lenchrom,chrom,sizepop,pop,bound)
% 本函数完成变异操作
% pcorss input : 变异概率
% lenchrom input : 染色体长度
% chrom input : 染色体群
% sizepop input : 种群规模
% pop input : 当前种群的进化代数和最大的进化代数信息
% ret output : 变异后的染色体
for i=1:sizepop
% 随机选择一个染色体进行变异
pick=rand;
while pick==0
pick=rand;
end
index=ceil(pick*sizepop);
% 变异概率决定该轮循环是否进行变异
pick=rand;
if pick>pmutation
continue;
end
flag=0;
while flag==0
% 变异位置
pick=rand;
while pick==0
pick=rand;
end
pos=ceil(pick*sum(lenchrom)); %随机选择了染色体变异的位置,即选择了第pos个变量进行变异
v=chrom(i,pos);
v1=v-bound(pos,1);
v2=bound(pos,2)-v;
pick=rand; %变异开始
if pick>0.5
delta=v2*(1-pick^((1-pop(1)/pop(2))^2));
chrom(i,pos)=v+delta;
else
delta=v1*(1-pick^((1-pop(1)/pop(2))^2));
chrom(i,pos)=v-delta;
end %变异结束
flag=test(lenchrom,bound,chrom(i,:)); %检验染色体的可行性
end
end
ret=chrom;
4.main函数,就是我们的主过程,
先给出其中Code.m的代码:
function ret=Code(lenchrom,bound)
%本函数将变量编码成染色体,用于随机初始化一个种群
% lenchrom input : 染色体长度
% bound input : 变量的取值范围
% ret output: 染色体的编码值
flag=0;
while flag==0
pick=rand(1,length(lenchrom));
ret=bound(:,1)'+(bound(:,2)-bound(:,1))'.*pick; %线性插值
flag=test(lenchrom,bound,ret); %检验染色体的可行性
end
然后,整个过程就出来了,还是很简单的
main.m
clc
clear all
close all
%参数
maxgen=100; %迭代次数
sizepop=100; %种群规模
pcross=[0.6]; %交叉概率
pmutation=[0.01];
lenchrom=[1 1 1 1 1]; %个体长度
bound=[0 0.9*pi;0 0.9*pi;0 0.9*pi;0 0.9*pi;0 0.9*pi];%五个参数都是一样的域
%个体结构体
individuals=struct('fitness',zeros(1,sizepop),'chrom',[]);
avgfitness=[]; %平均适应度
bestfitness=[]; %最佳适应度
bestchrom=[]; %最佳种群个体
Testfitness=[];
Testchrom=[];
%初始化种群
for i=1:sizepop
%随机产生种群
individuals.chrom(i,:)=Code(lenchrom,bound);
x=individuals.chrom(i,:);
individuals.fitness(i)=fun(x);
end
%找最好的染色体
[n_bestfitness,bestindex]=min(individuals.fitness);
%最好的染色体
bestchrom=individuals.chrom(bestindex,:);
avgfitness=sum(individuals.fitness)/sizepop; %平均适应度
trace=[];%每一代最好的适应度
%先进化二十代,选出20*100个个体和适应度,并训练神经网络
for i=1:20
%选择
individuals=select(individuals,sizepop);
avgfitness=sum(individuals.fitness)/sizepop;
%交叉
individuals.chrom=Cross(pcross,lenchrom,individuals.chrom,sizepop,bound);
%变异
individuals.chrom=Mutation(pmutation,lenchrom,individuals.chrom,sizepop,[i maxgen],bound);
%计算适应度
for j=1:sizepop
x=individuals.chrom(j,:);%一个个体
individuals.fitness(j)=fun(x);
Testfitness=[Testfitness;fun(x)]; %将每个个体都加入训练矩阵中,扩大训练样本
Testchrom=[Testchrom;x];
end
%找到最好的染色体和他们在种群的位置
[newbestfitness newbestindex]=min(individuals.fitness);
[worestfitness,worestindex]=max(individuals.fitness);
if n_bestfitness>newbestfitness
bestindex=newbestindex;
bestchrom=individuals.chrom(bestindex,:);
n_bestfitness=individuals.fitness(bestindex);
end
individuals.chrom(worestindex,:)=bestchrom;
individuals.fitness(worestindex)=n_bestfitness;
avgfitness=sum(individuals.fitness)/sizepop;
trace=[trace;avgfitness n_bestfitness];
end
%%新建bp神经网络
P=Testchrom';
T=Testfitness';
inputnum=size(P,1);
outputnum=size(T,1);
hiddennum=11;
net=newff(Testchrom',Testfitness',hiddennum);
%设置训练参数
net.trainParam.epochs=1000;
net.trainParam.goal=0.01;
LP.lr=0.1;
net.trainParam.show=NaN;
%训练bp网络
net=train(net,P,T);
%训练好之后,继续进行遗传的迭代,不过这次计算适应度一半用遗传,一半用bp,并且遗传的一部分继续用于训练bp
for i=21:maxgen
%选择
individuals=select(individuals,sizepop);
avgfitness=sum(individuals.fitness)/sizepop;
%交叉
individuals.chrom=Cross(pcross,lenchrom,individuals.chrom,sizepop,bound);
%变异
individuals.chrom=Mutation(pmutation,lenchrom,individuals.chrom,sizepop,[i maxgen],bound);
%计算适应度
is_bpfun=[];
mid=50; %可以自动设置比例,决定哪一半用BP计算
for j=1:sizepop
if j<mid
%前mid个个体用直接计算的方法
x=individuals.chrom(j,:);
individuals.fitness(j)=fun(x);
Testfitness=[Testfitness;individuals.fitness(j)];
is_bpfun(:,j)=0;
Testchrom=[Testchrom;x];
else
if j==mid
%第mid代就可以训练网络
x=individuals.chrom(j,:);
individuals.fitness(j)=fun(x);
is_bpfun(:,j)=0;
Testfitness=[Testfitness;individuals.fitness(j)];
Testchrom=[Testchrom;x];
net.train(net,Testchrom',Testfitness');
else
%大于mid的用神经网络直接输出结果
x=individuals.chrom(j,:);
individuals.fitness(j)=sim(net,x');
is_bpfun(:,j)=1;
end
end
end
[newbestfitness,newbestindex]=min(individuals.fitness);
[worestfitness,worestindex]=max(individuals.fitness);
if n_bestfitness>newbestfitness
bestindex=newbestindex;
bestchrom=individuals.chrom(bestindex,:);
n_bestfitness=individuals.fitness(bestindex);
end
%如果是神经网络算出的结果,重算
while is_bpfun(bestindex)==1
x=individuals.chrom(bestindex,:);
individuals.fitness(bestindex)=fun(x);
if individuals.fitness(bestindex)>n_bestfitness
is_bpfun(bestindex)=0;
n_bestfitness=individuals.fitness(bestindex);
else
[newbestfitness newbestindex]=min(individuals.fitness);
bestindex=newbestindex;
n_bestfitness=newbestfitness;
end
end
individuals.chrom(worestindex,:)=bestchrom;
individuals.fitness(worestindex)=n_bestfitness;
avgfitness=sum(individuals.fitness)/sizepop;
trace=[trace;avgfitness n_bestfitness];
end
%% 结果显示
[r c]=size(trace);
figure
plot([1:r]',trace(:,1),'r-',[1:r]',trace(:,2),'b--');
title(['函数值曲线 ' '终止代数=' num2str(maxgen)],'fontsize',12);
xlabel('进化代数','fontsize',12);ylabel('函数值','fontsize',12);
legend('各代平均值','全局最佳值','fontsize',12);
disp('函数值 变量');
ylim([1.5 8])
%xlim([1,size(trace,1)])
grid on
% 窗口显示
disp([n_bestfitness x]);
testchrom=[];
T_test=[];
%随机生成测试数据
for i=1:sizepop
testchrom(i,:)=Code(lenchrom,bound);
x=testchrom(i,:);
T_test(i,:)=fun(x);
end
P_test=sim(net,testchrom');
P_test2=P_test';
N=0.9*pi;
x=0:N/20:N;
figure
plot([T_test P_test2]);
legend('真实值','BP预测值');
xlabel('自变量值');
ylabel('因变量值');
string={'真实值与预测值对比'};
title(string);
最后,运行程序,得到的结果如下图