我很大程度上是理解了这些代码的意思。
首先是画出函数图
figure(1);
hold on;
t = 0:0.01:10;
y = t + 10*sin(5*t) + 7*cos(4*t);
plot(t, y)
上面这个代码的意思是,首先建立图像1,t的取值范围是在0~10之间,步伐是0.01 。
接下来是定义我们要求的目标函数,plot把图画出来。
接下来第一步骤,定义遗传算法的参数
NP=50; %种群数量
L=20; %二进制位串长度
Pc=0.8; %交叉率
Pm=0.1; %变异率
G=100; %最大遗传代数
Xs=10; %在区间[0,10]求函数的极值
Xx=0; %定义域下限
f=randi([0,1],NP,L); %随机产生第一代随机种群
trace=zeros(1,G); %创建数组,用于记录每一代,适应度最好的函数值
for k=1:G%开始进行一代一代的循环
上面这段代码的意思是,首先定义参数,并且给它们赋值。
用到了语法randi,它的意思是随机生成0或者1,50行,20列。
50行呢,每行代表一个个体
20列呢,每列代表一个基因。
然后语法zeros的意思是,X = zeros(sz1,...,szN)
返回由零组成的 sz1
×...×szN
数组,其中 sz1,...,szN
指示每个维度的大小。例如,zeros(2,3)
将返回一个 2×3 矩阵。
上面代码的意思就是,创建1X100列的矩阵,每一列代表那一代的适应度最高的函数值。
然后是优化,第二步骤,计算适应度
%%%%%%第2步,计算染色体适应度%%%%,
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
plot(t,y,'b-',x,func1(x),'ro')
pause(0.4)
%求适应度最优%
maxFit=max(Fit); %目标函数最大值
minFit=min(Fit); %目标函数最小值
rr=find(Fit==maxFit);
%最大值在Fit数组中的位置,返回一个数组,因为可能有几个相同的最大值
%这点要特别注意,所以下面不能直接用rr
fBest=f(rr(1,1),:); %最优适应度,有多个相同值时只取第一个
xBest=x(rr(1,1)); %最优适应度对应的染色体
Fit=(Fit-minFit)/(maxFit-minFit); %归一化适应度值
上面这段代码开始有点东西了。
首先,外层循环是 for i=1:NP
,表示对种群中的每个染色体进行循环操作,其中 NP
是种群的大小。在循环内部,首先通过 U=f(i,:)
获取当前染色体的二进制编码,这行代码的作用是取出种群矩阵 f
中的第 i
行,即第 i
个染色体。
然后,通过一个内层循环 for j=1:L
,对该染色体的每个基因进行遍历。L
是染色体的长度,即基因的数量。在内层循环中,通过 m=U(j)*2^(j-1)+m
将二进制编码转换为十进制的过程。这行代码的作用是将二进制编码中的每个基因乘以相应的权重并求和,以得到一个十进制数值 m
。
接下来,通过 x(i)=Xx+m*(Xs-Xx)/(2^L-1)
将染色体解码到函数定义域,其中 Xx
和 Xs
是函数定义域的上下界。这行代码的作用是将十进制数值 m
映射到函数定义域内的实数值,并将结果存储在向量 x
中的第 i
个位置。
最后,通过 Fit(i)=func1(x(i))
计算解码后的染色体的适应度,即目标函数的值。func1
是一个自定义的函数,用于计算染色体解码后的实数值在目标函数上的表现。
总结起来,这段代码是对种群中的每个染色体进行遍历,将其二进制编码转换为十进制数值,并解码到函数定义域内,然后计算解码后染色体的适应度。
第三步骤染色体复制
%%%%%第3步,复制染色体%%%%
sum_Fit=sum(Fit);
fitvalue=Fit./sum_Fit; %可以看作概率密度f
fitvalue=cumsum(fitvalue); %可以看作概率累计F
ms=sort(rand(NP,1)); %随机生成(0,1)的有序概率密度NP大小向量
fiti=1;
newi=1;
%由ms可以看出这是一种随机的复制方式,但总趋势是将适应度比较大的遗传下去
while newi <=NP
if (ms(newi)<fitvalue(fiti))
nf(newi,:)=f(fiti,:);
newi=newi+1;
else
fiti=fiti+1;
end
end
上面这段代码呢,用的是轮盘赌方法。
首先,先把它们加起来,然后每个适应度值去除以总和,就是占比的概率,概率越大表示被选中的概率越高。
然后,通过 fitvalue=cumsum(fitvalue)
对概率密度向量进行累积求和操作,得到一个类似于概率累计分布的向量之后随机生成0,1的向量。这个向量表示了每个染色体被选中的累积概率,值越大表示累积概率越高。然后,通过 ms=sort(rand(NP,1))
随机生成一个大小为 NP
的有序概率密度向量 ms
。这个向量的元素是在区间 (0,1) 内均匀分布的随机数,通过排序使得它们有序。
接下来,通过一个循环 while newi <=NP
对新的种群 nf
进行构建。在循环内部,通过判断 ms(newi)
是否小于 fitvalue(fiti)
来决定是否选择染色体进行复制。如果是,则将第 fiti
行的染色体 f(fiti,:)
复制到新的种群 nf
的第 newi
行。
然后,将 newi
自增1,表示已经选择了一个染色体进行复制。如果 ms(newi)
不小于 fitvalue(fiti)
,则将 fiti
自增1,表示尝试选择下一个染色体进行复制。
重复这个过程,直到新的种群 nf
中的所有染色体都被复制完成。
综上所述,这段代码实现了一种随机的选择方式,但整体趋势是将适应度较大的染色体更有可能被选择和遗传到下一代种群中。
第四步染色体交叉操作
%%%%%%%%第4步,基于概率的交叉操作%%%%%%%%
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 i=1:2:NP
循环,从种群中选择相邻的两条染色体进行交叉操作(一次选取2个染色体进行交叉,就像父母)。这里使用步长为2,即每次选择相邻的两个染色体进行交叉。在每次循环中,首先生成一个随机数 p=rand
,用于确定是否进行交叉操作。
如果 p
小于交叉概率阈值 Pc
,则进行交叉操作;否则,跳过本次循环,不进行交叉操作。
如果确定进行交叉操作,则通过 q=randi([0,1],1,L)
随机生成一个大小为L的二进制序列 q
,用于确定哪些基因位置要进行交叉。接着,通过一个循环 for j=1:L
遍历染色体的每个基因位置。
在循环内部,通过判断 q(j)
是否等于1,即确定是否对该基因位置进行交叉。如果 q(j)
等于1,则进行交叉操作。首先,将第 i+1
行染色体的第 j
个基因值存储到临时变量 temp
中。然后,将第 i
行染色体的第 j
个基因值替换为第 i+1
行染色体的第 j
个基因值,将第 i+1
行染色体的第 j
个基因值替换为 temp
。
这样,交叉操作就完成了。
总结起来,这段代码的循环逻辑是从种群中选择相邻的两条染色体,根据交叉概率和随机生成的二进制序列,对这两条染色体的指定基因位置进行交叉操作。循环的步长为2,确保选择的染色体是相邻的。
第五步骤变异
%%%%%%%%%第五步,基于概率的变异操作%%%%%%%%%%%%
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(1,:)=fBest; %保留最优个体在新种群中
trace(k)=maxFit; %历代最优是应当由,即最大函数值
end
上面这段代码是遗传算法中的变异操作。
首先,定义一个变量 i
,初始值为1。通过 while i<=round(NP*Pm)
循环,控制变异染色体的总数。NP
是种群大小,Pm
是变异概率。
在每次循环中,首先通过 h=randi([1,NP],1,1)
随机选择一个染色体进行变异。这里使用 randi
函数生成一个随机整数,范围是从1到种群大小 NP
。
接着,通过 for j=1:round(L*Pm)
循环控制染色体上进行变异的基因总数。L
是染色体的长度,Pm
是变异概率。
在循环内部,通过 g=randi([1,L],1,1)
随机选择一个基因进行变异。这里使用 randi
函数生成一个随机整数,范围是从1到染色体长度 L
。
然后,通过 nf(h,g)=~nf(h,g)
对选定的染色体的指定基因进行变异操作。使用 ~
运算符对基因取反,即将基因的值由0变为1,或由1变为0。
完成所有变异操作后,将新的一代种群存储到变量 f
中,即 f=nf
。同时,保留最优个体在新种群中,即将最优个体的染色体赋值给新种群的第一行,即 f(1,:)=fBest
。这样可以确保最优个体在新一代种群中得到保留。
最后,将历代最优的适应度值(目标函数最大值)赋给 trace(k)
,用于记录历代最优的变化过程。
总结起来,这段代码的循环逻辑是根据变异概率和随机选择的染色体和基因位置,对染色体进行变异操作。每次循环选择一个染色体进行变异,并随机选择若干个基因进行变异。完成所有变异操作后,更新新一代种群,并保留最优个体。循环结束后,记录历代最优的适应度值。
最后输出最优解
disp('最优解');
disp(xBest); %最优个体,也就是最优解
figure
plot(trace)
xlabel('迭代次数')
ylabel('目标函数值')
title('适应度进化曲线')
function fit=func1(x)
fit=x+10*sin(5*x)+7*cos(4*x);
end
在代码的主体部分,首先通过 disp('最优解')
显示一条消息,表明接下来将显示最优个体(最优解)。
然后,通过 disp(xBest)
显示变量 xBest
,它存储了最优个体的值,即最优解。
接下来,通过 figure
创建一个新的图形窗口。
通过 plot(trace)
绘制适应度进化曲线,其中 trace
存储了历代最优的适应度值。横坐标表示迭代次数,纵坐标表示目标函数的值。
最后,通过 xlabel
、ylabel
和 title
分别设置图形的 x 轴标签、y 轴标签和标题,以便更好地描述图形的含义。
定义了一个函数 func1(x)
,用于计算目标函数的值。目标函数的表达式为 x+10*sin(5*x)+7*cos(4*x)
。
函数 func1(x)
的输入参数是变量 x
,代表问题的自变量。在这里,它表示染色体解码后得到的实数值。
函数体内的表达式 fit=x+10*sin(5*x)+7*cos(4*x)
计算了目标函数的值。其中,x
代表自变量的值。
总的来说,这段代码定义了一个目标函数 func1(x)
,并展示了最优解和适应度进化曲线。目标函数用于评估染色体的适应度,而适应度进化曲线则反映了遗传算法在每一代迭代中适应度的变化情况。