一、前言
遗传算法是一种群优化算法,我在各个网站上找到的遗传算法相关的文章大多是对一个参数的优化,本文将使用遗传算法解决多个参数的优化问题(以两个参数为例),本文以应用为主,不会去详细讲解原理,因为CSDN、知乎上已经有许多讲的非常好的文章了。
二、优化问题
求解多峰函数Ackley函数的全局最小
f
(
x
,
y
)
=
−
20
e
−
0.2
0.5
(
x
2
+
y
2
)
−
e
0.5
(
cos
2
π
x
+
cos
2
π
y
)
+
e
+
20
−
5
≤
x
,
y
≤
5
f\left( x,y \right) =-20\text{e}^{-0.2\sqrt{0.5\left( x^2+y^2 \right)}}-\text{e}^{0.5\left( \cos 2\pi x+\cos 2\pi y \right)}+\text{e}+20\\ -5 \leq x,y \leq 5
f(x,y)=−20e−0.20.5(x2+y2)−e0.5(cos2πx+cos2πy)+e+20−5≤x,y≤5
函数图像:
易知,函数的全局极小值为:
f
(
0
,
0
)
=
0
f(0,0)=0
f(0,0)=0
三、编程思路
-
首先要明确使用遗传算法进行优化时的几个要素:
(1)待优化参数个数。在这个问题中,自变量 x 、 y x、y x、y就是待优化参数。
(2)参数的取值范围。
(3)目标函数。即优化参数与优化目标之间的函数关系。
(4)适应度函数。这里需要区分一下适应度函数和目标函数的区别,如果优化目的是求目标函数的最大值,则适应度函数和目标函数可以等同看待;如果是求最小值,则需要对目标函数进行转换,最常见的方式是对目标函数取反或者取倒数。因为在进行优化时,我们设计的算法往往是求最大值的。
-
求二元函数的极小值问题可以理解成含有两个参数的优化问题,目标函数是 f ( x , y ) f(x,y) f(x,y)。优化参数的取值范围为-5到5。
-
编码: 使用二进制编码。设精度为0.001,即将优化参数、优化结果值保留到小数点后三位。因此二进制的编码需要编码的数目为:
M = [ 5 − ( − 5 ) ] ⋅ 1 0.001 = 10000 M=[5-(-5)]· \frac{1}{0.001}=10000 M=[5−(−5)]⋅0.0011=10000
由于
2 13 ≤ M ≤ 2 14 2^{13} \leq M \leq 2^{14} 213≤M≤214
因此,对于每一个优化参数,取编码长度为14,则编码精度为
L = 10000 2 14 L=\frac{10000}{2^{14}} L=21410000 -
上面的每一个14个二进制数组成的一个编码就是一个不完整的染色体,为什么说是不完整呢?因为对于多参数优化,一个完整的染色体应该是将各个不完整的编码串联到一起,组合成一个完整的编码,才能成为一个完整的染色体。所以在这个题中,一个染色体应该包含28个二进制数,在进行解码时将前十四个、后十四个分别解码即可。
-
适应度函数: 由于原函数是求极小值,所以将目标函数取倒数作为适应度函数,适应度函数最大时,目标函数取得最小值。
-
其他步骤不再详细讲解。
四、注意
- 本题中 x 、 y x、y x、y的取值范围相同,所以解码时划分染色体是均匀划分的,如果取值范围不同,代码需要稍作修改,这个有时间了再做,想尝试的话可以在源码的第四部分中对变量DevidePoint入手。
- 如果你有其他方法非常非常欢迎交流,因为我总觉得这个方法对于参数特别多的情况不太友好。
- 如果发现本文有错误的地方欢迎交流指正。
- 本文内容包括代码在内均为原创,如果转载需要标明出处。
- 如果对代码内容有任何疑问可以私信或者留言,看到就会第一时间回复。
- 祝在看的你学业有成!!!
五、源码
- 绘图
clear;clc;close all;
x = -5:0.01:5;
y = -5:0.01:5;
[X,Y] = meshgrid(x,y);
f = -20*exp(-0.2*sqrt(0.5*(X.^2+Y.^2)))-...
exp(0.5*(cos(2*pi*X)+cos(2*pi*Y)))+exp(1)+20;
mesh(X,Y,f);
xlabel('x');
ylabel('y');
zlabel('z');
title('f(x,y)函数图像')
hold on
- 主函数
clear;clc;close all;
%%%%%%%%% 运行前设定 %%%%%%%%%
UnknowNum = 2; % 待优化参数数量
popsize = 20; % 种群大小
N = 1000; % 最大迭代次数(遗传代数)
CrossRate = 0.6; % 交叉概率
MutateRate = 0.01; % 变异概率
LowBound = -5; % 待优化参数下界
UpBound = 5; % 待优化参数上界
Precision = 0.001; % 优化精度,保留到小数点后三位
Individual = cell(1,N); %最优个体、最优适应度值计算
BestFit = zeros(1,N);
%%%%%%%%%%%%%%%%%%% 初始化处理 %%%%%%%%%%%%%%%%%%%
ChromLen = CodeLength(LowBound,UpBound,Precision);% 一个参数对应的染色体长
ChromTotal = UnknowNum*ChromLen; % 染色体总长
Population = round(rand(popsize,ChromTotal)); % 种群初始化
IntialFitVal = FitFunction(Population,UnknowNum,LowBound,UpBound); % 初始适应度计算
%%%%%%%%%%%%%%%%%%% 遗传 %%%%%%%%%%%%%%%%%%%
for i = 1:N
if i==1
FitVal = IntialFitVal;
else
Population = MutatePop;
end
SelectPop = Selection(Population,FitVal);
CrossPop = CrossOver(SelectPop,CrossRate,UnknowNum);
MutatePop = Mulation(CrossPop,MutateRate,UnknowNum);
FitVal = FitFunction(MutatePop,UnknowNum,LowBound,UpBound); % 新一代适应度
[BestFit(i),Index] = max(FitVal);
value = 1/BestFit(i);
Individual0 = MutatePop(Index,:);
Individual1 = BinToDec(Individual0,UnknowNum,LowBound,UpBound);
Individual{i} = Individual1;
end
%%%%%%%%%%%%%%%%%%% 对适应度值处理 %%%%%%%%%%%%%%%%%%%
[MaxFit,Index] = max(BestFit);
MinVal = 1/MaxFit;
BestIndividual = cell2mat(Individual(Index));
disp('函数极小值为:')
disp(MinVal);
disp('x、y的取值分别为:')
disp(BestIndividual);
% 验证
f = -20*exp(-0.2*sqrt(0.5*(BestIndividual(:,1).^2+BestIndividual(:,2).^2)))-...
exp(0.5*(cos(2*pi*BestIndividual(:,1))+cos(2*pi*BestIndividual(:,2))))+exp(1)+20;
- 编码长度计算
% 编码长度即单个染色体长度
function Len = CodeLength(lb,ub,p)
% lb:参数下界
% ub:参数上界
% p:参数精度
Scope = (ub-lb)/p; % 对取值范围的表示
Len = log2(Scope);
Len = ceil(Len); % 向上取整函数
end
- 二进制转为十进制(解码)
% Population: 种群
% UnknowNum: 优化参数数量
function Val = BinToDec(Population,UnknowNum,lb,ub)
[m,n] = size(Population);
DevidePoint = n/UnknowNum; % 分割点:分割不同参数
NewPop = zeros(m,UnknowNum);
for j=1:UnknowNum
for i=1+(j-1)*DevidePoint:j*DevidePoint
Population(:,i) = Population(:,i)*2^(i-1-(j-1)*DevidePoint);
end
NewPop(:,j) = sum(Population(:,1+(j-1)*DevidePoint:j*DevidePoint),2); % 解码为十进制
end
deta = (ub-lb)/(2^(n/UnknowNum)-1);
%%%%%%%%%%%%%%%%%%% 根据自变量范围修改 %%%%%%%%%%%%%%%%%%%
NewPop = NewPop*deta-5; % 解码为十进制
Val = NewPop;
end
- 适应度函数
% 若是优化求最大值,返回值即为目标函数值;
% 若是优化求最小值,返回值为目标函数值取倒数或取反
% Population:初始化后的种群,本质是二进制矩阵
% UnknowNum:待优化参数数量
function FitVal = FitFunction(Population,UnknowNum,lb,ub)
NewPop = BinToDec(Population,UnknowNum,lb,ub);
f = -20*exp(-0.2*sqrt(0.5*(NewPop(:,1).^2+NewPop(:,2).^2)))-...
exp(0.5*(cos(2*pi*NewPop(:,1))+cos(2*pi*NewPop(:,2))))+exp(1)+20;
FitVal = 1./f;
end
- 选择
function NewPop = Selection(Population,FitVal)
[m,n] = size(Population);
NewPop = zeros(m,n); % 初始化新种群
FitTotal = sum(FitVal);
SelectedP = FitVal/FitTotal; % 单个个体被选中的概率
SelectedP = cumsum(SelectedP); % 概率求和排序
RandP = sort(rand(m,1)); % 随机概率排序,轮盘赌法
fitIndex = 1;
newIndex = 1;
while newIndex <= m
if RandP(newIndex)<SelectedP(fitIndex)
NewPop(newIndex,:) = Population(fitIndex,:);
newIndex = newIndex+1;
else
fitIndex=fitIndex+1;
end
end
end
- 交叉
% 交叉
% 生成随机概率,小于交叉概率时进行交叉,相邻两个个体交叉
% Population: 种群
% CrossRate: 交叉概率
function Newpop = CrossOver(Population,CrossRate,UnknowNum)
[m,n] = size(Population);
Newpop = zeros(m,n);
DevidePoint = n/UnknowNum;
for j = 1:UnknowNum
StartPoint = (j-1)*DevidePoint+1;
EndPoint = j*DevidePoint;
for i = 1:2:m-1
P = rand;
if P<CrossRate
CrossPoint = StartPoint+round(rand*(DevidePoint-1)); % 交叉点
Newpop(i,StartPoint:EndPoint) = [Population(i,StartPoint:CrossPoint),...
Population(i+1,CrossPoint+1:EndPoint)];
Newpop(i+1,StartPoint:EndPoint) = [Population(i+1,StartPoint:CrossPoint),...
Population(i,CrossPoint+1:EndPoint)];
else
Newpop(i,StartPoint:EndPoint) = Population(i,StartPoint:EndPoint);
Newpop(i+1,StartPoint:EndPoint) = Population(i+1,StartPoint:EndPoint);
end
end
end
end
- 变异
% 变异
% 将其中一个某一个染色位取反
% Population: 种群
% CrossRate: 交叉概率
function NewPop = Mulation(Population,MutateRate,UnknowNum)
[m,n] = size(Population);
NewPop = zeros(m,n);
DevidePoint = n/UnknowNum;
for j = 1:UnknowNum
StartPoint = (j-1)*DevidePoint+1;
EndPoint = j*DevidePoint;
for i = 1:m
P = rand;
if P<MutateRate
MPoint = StartPoint+round(rand*(DevidePoint-1)); % 变异点
NewPop(i,StartPoint:EndPoint) = Population(i,StartPoint:EndPoint);
NewPop(i,MPoint) = ~NewPop(i,MPoint);
else
NewPop(i,StartPoint:EndPoint) = Population(i,StartPoint:EndPoint);
end
end
end
end