遗传算法的原理
遗传算法的科学严谨的原理都在百度百科(遗传算法地址)中由详细解释,在这里我只表达我所理解的遗传算法原理。
之所以叫遗传算法,原因是该算法是根据大自然中生物体进化规律而设计提出的,通过计算机的方法模拟自然进化过程搜索最优解。
遗传在百度百科中的解释:是指亲代表达相应性状的基因通过无性繁殖或有性繁殖传递给后代,从而使后代获得其父母遗传信息的现象。基因就是染色体,计算机方法也就是通过模拟染色体的行为来求得最优解。遗传算法对染色体的模拟主要是交叉和变异。
交叉
在染色体中就是染色体片段的互换,可能是与其他个体交叉,也可能是本身的染色体交叉互换,每次交叉互换就会得到两个新的个体,从而得到两个个新的群体。
变异
在这里的变异并不是缺失、重复、倒位、易位,而是在群体中的个体串的某些基因座上的基因值作变动,如下图所示:
将原群体进行变异操作,又得到一个新的群体。
计算适应度
完成以上染色体的行为的步骤后,就产生了新的三个群体。然后进行发育,就是用获得的基因,通过一定的计算得到相对应的结果,在生物的角度看,就是从受精卵发育成一个完整个体。在遗传算法中也就是计算适应度。
选择操作
现在我们就有四个群体,交叉得到的两个群体,变异得到的一个群体,和原来的群体。计算适应度后,择优选择。
不断重复以上操作,最终得到最优秀的群体。
遗传算法的案例
以
f
(
x
)
=
x
sin
(
x
)
cos
(
x
)
f(x)=x\sin(x)\cos(x)
f(x)=xsin(x)cos(x)为例,下图就是
f
(
x
)
,
(
x
∈
[
0
,
2
π
]
)
f(x),(x\in[0,2\pi])
f(x),(x∈[0,2π])的图例:
我们最终的目标是得到的该段函数的最大值,由图可以很容易看出来这个最大值的横坐标就是
x
=
4
x=4
x=4附近,如下图所示:
虽然我们可以直观的看出来最大值,可是是用一般算法可能会陷入局部最优,也就是最后的结果可能是横坐标
x
=
1
x=1
x=1的点取值。因此,我们使用遗传算法来求解这个问题。
初始化种群
如上图所示我们需要先初始化种群,也就是产生第一代个体,第一代个体的产生是随机的,在这里,我使用的是十进制编码去模拟基因,也可以用二进制编码,但是二进制编码的精度感觉没有十进制高。模拟的基因长度是5,随机产生多个基因,如下表随机产生的基因:
第一代的个体 | 9 3 4 6 8 | 7 6 9 0 7 | 3 0 8 2 6 | … |
---|
现在可能有的同学有些疑问,为啥这一串数字就是基因呢?
从基因的角度上说基因其实就是由携带了一定信息的DNA构成的,而这串数字也携带了一些信息。但是这个可能还是有点抽象,这是因为就算直接看真正的基因也是看不出什么东西来的,只有长成个体后,才知道这段基因决定了那些性状,就像高中课本中果蝇的不同表现型一样。现在我们就需要让这串基因“长大”,看看这串基因代表什么。
由于我们使用的是十进制编码,所以,我们需要用到十进制编码器
[
10000
;
1000
;
100
;
10
;
1
]
[10000; 1000; 100; 10 ;1]
[10000;1000;100;10;1],以第一个个体基因9 3 4 6 8为例:
计算自适应度
首先需要解码,就是将9 3 4 6 8与解码器
[
10000
;
1000
;
100
;
10
;
1
]
[10000; 1000; 100; 10 ;1]
[10000;1000;100;10;1]相乘相加,再相除对应的数:
9
×
10000
+
3
×
1000
+
4
×
100
+
6
×
10
+
8
×
1
=
93468
9\times10000+3\times1000+4\times100+6\times10+8\times1=93468
9×10000+3×1000+4×100+6×10+8×1=93468
x
1
=
93468
÷
(
9999
×
2
×
π
)
=
5.8728
x_1= 93468\div(9999\times2\times\pi)=5.8728
x1=93468÷(9999×2×π)=5.8728
之所以除9999,是因为十位编码范围为
[
0
,
9999
]
[0,9999]
[0,9999],再除
2
π
2\pi
2π,是因为这是
x
x
x的取值范围。再将
x
1
=
5.8728
x_1=5.8728
x1=5.8728带入
f
(
x
)
f(x)
f(x),得
f
(
x
1
)
=
−
2.1484
f(x_1)=-2.1484
f(x1)=−2.1484,也就是说9 3 4 6 8这串基因最后的表现型是
−
2.1484
-2.1484
−2.1484。如下图所示:
我们生成20个个体,如下图所示:
以上的步骤实际上就是计算自适应度(我也不知道为啥叫自适应度,可能还是知识浅薄😂)。
选择操作
选择操作就是选择优秀的个体,在本案例中,说白了就是比大小,就拿刚刚算出的数
f
(
x
1
)
=
−
2.1484
f(x_1)=-2.1484
f(x1)=−2.1484来看,这个数明显比较小,所以说明这个个体不咋滴,要排除。我们还看到有两个点已经很接近最大值了,那么说明这两个个体就比较优秀,于是就需要选择这两个个体。但在实际操作中也不是这样直接选择,因为可能会陷入局部最优,就比如都在
x
=
1
x=1
x=1附近。因此,为了避免局部最优的情况发生,一般选用两种方法,一是轮盘赌法,二是排名法。
轮盘法是通过每个个体与总体的适应度占比来衡量其优劣,比值越大越不容易被淘汰,就是将所有的个体的适应度累加,除以各个个体的适应度
P
i
=
R
i
∑
R
i
P_i={R_i \over\sum{R}_i}
Pi=∑RiRi,其中
R
i
R_i
Ri是各个个体的适应度。
排名法是通过每个个体的适应度排名来看的,排名越靠前越不容易被淘汰,排名法的计算法复杂度相对较低,假设
f
(
x
1
)
=
−
2.1484
f(x_1)=-2.1484
f(x1)=−2.1484是第15名,那么
p
1
=
15
20
=
0.75
p_1={15\over20}=0.75
p1=2015=0.75,就是有
75
%
75\%
75%的概率被移除。
第一代种群生成后一般不进行选择操作。
交叉操作
交叉操作就是基因片段的交换,设
D
1
=
93468
,
D
2
=
76907
D_1=9 3 4 6 8,D_2=7 6 9 0 7
D1=93468,D2=76907,他们进行交叉,就是基因片段的互换,如下图所示:
除了和其他个体交叉,也可能自己与自己交叉。交叉的对象都是随机的,这样做的目的都是为了让群体具有多样性,避免局部最优。
变异操作
还是以
D
1
=
93468
D_1=9 3 4 6 8
D1=93468为例,每个数字可能会变成0~9,的任意一个数:
93460
,
93478
,
.
.
.
93460,93478,...
93460,93478,...当然在生物界的实际情况也可知变异的概率较小,且变异的长度较短。
通过以上操作,产生四个群体,再经选择操作,选出最优秀的20个。
MATLAB
clear,clc;
syms x
f = @(x) x.*sin(x).*cos(x)
ezplot(f, [0, 2*pi])
xlabel('x')
ylabel('y')
N = 20; % 种群上限
ger = 100; % 迭代次数
L = 5; % 基因长度
pc = 0.8; % 交叉概率
pm = 0.1; % 变异概率
dco = [10000; 1000; 100; 10 ;1]; % 解码器
dna = randi([0, 9], [N, L]); % 基因
hold on
x = dna * dco / 99999 * 2 * pi; % 对初始种群解码
plot(x, f(x),'ko','linewidth',3) % 画出初始解的位置
x1 = zeros(N, L); % 初始化子代基因,提速用
x2 = x1; % 同上
x3 = x1; % 同上
fi = zeros(N, 1); % 初始化适应度,提速
for epoch = 1: ger % 进化代数为100
for i = 1: N % 交叉操作
if rand < pc
d = randi(N); % 确定另一个交叉的个体
m = dna(d,:); % 确定另一个交叉的个体
d = randi(L-1); % 确定交叉断点
x1(i,:) = [dna(i,1:d), m(d+1:L)]; % 新个体 1
x2(i,:) = [m(1:d), dna(i,d+1:L)]; % 新个体 2
end
end
x3 = dna;
for i = 1: N % 变异操作
if rand < pm
x3(i,randi(L)) = randi([0, 9]);
end
end
dna = [dna; x1; x2; x3]; % 合并新旧基因
fi = f(dna * dco / 99999 * 2 * pi); % 计算适应度,容易理解
dna = [dna, fi];
dna = flipud(sortrows(dna, L + 1)); % 对适应度进行排名
while size(dna, 1) > N % 自然选择
d = randi(size(dna, 1)); % 排名法
if rand < (d - 1) / size(dna, 1)
dna(d,:) = [];
fi(d, :) = [];
end
end
dna = dna(:, 1:L);
end
x = dna * dco / 99999 * 2 * pi; % 对最终种群解码
plot(x, f(x),'ro','linewidth',3) % 画出最终解的位置
disp(['最优解为x=',num2str(x(1))]);
disp(['最优值为y=',num2str(fi(1))]);
此代码不是我自己编的,原文链接:https://blog.csdn.net/nightmare_dimple/article/details/74355510
结果如下: