遗传算法基本原理
遗传算法(Genetic Algorithm,GA)作为一种解决复杂问题的优化搜索方法,是由美国密执安大学的John Holland 教授首先提出来的。遗传算法是以达尔文的生物进化论为启发而创建的,是一种基于进化论中优胜劣汰,自然选择,适者生存和物种遗传思想的优化算法。以经广泛应用于人工智能、机器学习、函数优化、自动控制、模式识别等众多领域。
遗传算法的由来
遗传算法的产生受到了自然界生物进化现象的启发,它最初是对自然进化过程的一个简单的模拟。所以遗传算法从生物学理解用了一些术语。“个体” 表示问题的一个可能解;“种群” 表示一组个体的集合。遗传算法是将问题的求解表示成 “染色体”(一般用二进制编码串表示),从而构成一群染色体。将它们置于问题的“环境” 中,根据适者生存的原则,从中选择出适应环境的染色体进行复制,即再生,通过交叉(Crossover) 、变异(Mutation) 两种基因操作产生出新的一代更适应环境的染色体群,这样一代一代的进化,最后收敛到一个最适合环境的个体上,求得问题的最优解。
群体遗传模型的几点假设
- 染色体(基因型)由一固定长度的字符串组成,其中每一位都具有有限数目的等位基因
- 群体由有限数目的基因组成。
- 每一基因型有一相应的适应度(Fitness),表示该基因型生存与复制的能力。适应度为大于零的实数,适应度越大表示生存能力越强。
- 一个小例子:
设字符串长度为 l l l,等位基因数为2,用0和1表示。则其基因型可以表示为
A = a 1 a 2 . . . a n A=a_1a_2...a_n A=a1a2...an
式中, a i ∈ { 0 , 1 } , i = 1 , 2 , . . . , l 。 a_i\in\{0,1\},i=1,2,...,l。 ai∈{0,1},i=1,2,...,l。群体中有n个基因型,用 A j A_j Aj表示第 j 个, j = 1 , 2 , . . . , n 。 j=1,2,...,n。 j=1,2,...,n。各基因型均有大于相应大于零的适应度 f i f_i fi。
遗传算法的基本操作
- 复制
复制(Reproduction),是从一个旧种群中选择生命力强的个体位串产生新种群的过程。或者说,复制是个体位串根据其目标函数(即适应度函数) f f f复制自己的过程。根据位串的适应度值复制位串意味着,具有较高适应度值的位串更有可能在下一代中产生一个或多个后代。显然,这个操作是模仿自然选择现象,将达尔文的适者生存理论应用于位串的复制,适应度值是该位串被复制或淘汰的决定因素。
按 N f i / ∑ f i Nf_i/\sum{f_i} Nfi/∑fi( f i f_i fi是 x i x_i xi的适应度值, ∑ f i \sum{f_i} ∑fi是种群适应度之和, N N N是种群大小)决定第 i i i个个体 x i x_i xi在下一代中应该复制自身的数目。 - 交叉
交叉是在两个基因型之间进行的,是指其中部分内容进行了互换。例如有两个串
A 1 = a 11 a 12 . . . a 1 l A 2 = a 21 a 22 . . . a 2 l A_1=a_{11}a_{12}...a_{1l} \\ A_2=a_{21}a_{22}...a_{2l} A1=a11a12...a1lA2=a21a22...a2l
若在位置 i i i交换,则产生两个新的串
A 1 ′ = a 11 . . . a 1 i ∘ a 2 ( i + 1 ) . . . a 2 l A 2 ′ = a 21 . . . a 2 i ∘ a 1 ( i + 1 ) . . . a 1 l A'_1=a_{11}...a_{1i}\circ a_{2(i+1)}...a_{2l}\\ A'_2=a_{21}...a_{2i}\circ a_{1(i+1)}...a_{1l} A1′=a11...a1i∘a2(i+1)...a2lA2′=a21...a2i∘a1(i+1)...a1l
式中, 1 ≤ i ≤ l − 1 1\leq i\leq l-1 1≤i≤l−1,是随机产生的。交叉是最重要的遗传算子,对搜索过程起决定作用。 - 变异
若基因型中某个或某几个位置上的等位基因从一种状态跳变到另一种状态(0到1或1到0),则称该基因型发生了变异。其中变异的位置也是随机的哟。
例如基因型
A = a 1 a 2 . . . a l A=a_1a_2...a_l A=a1a2...al
中的 a i a_i ai位上变异为 b i b_i bi产生的基因型为
A ′ = a 1 a 2 . . . a i − 1 b i a i + 1 . . . a l A'=a_1a_2...a_{i-1}b_ia_{i+1}...a_l A′=a1a2...ai−1biai+1...al
遗传算法基本步骤
如下图所示是遗传算法的基本步骤
主要包括以下几步
- k = 0 k=0 k=0,随机产生n个串,构成初始种群
- 计算各串的适应度值 f i f_i fi
- 以下步骤产生新的群体,直到新的群体中串的总数达到n:
- 以概率 f i / ∑ f i , f j / ∑ f j f_i/\sum f_i,f_j/\sum f_j fi/∑fi,fj/∑fj从群体中选出两个串 S i , S j S_i,S_j Si,Sj
- 以概率 P c P_c Pc对 S i , S j S_i,S_j Si,Sj进行交换,得到新的串 S i ′ , S j ′ S'_i,S'_j Si′,Sj′
- 以概率 P m P_m Pm使 S i ′ , S j ′ S'_i,S'_j Si′,Sj′的各位产生变异
- k = k + 1 k=k+1 k=k+1返回第 2 步
遗传算法参数确定
- 种群数目
N
N
N:
种群数目影响算法的有效性。 N N N太小,遗传算法会很差或根本找不出问题的解。因为太小的种群数目不能提供足够的采样点; N N N太大,会增加计算量,使收敛时间增长。一般设置在30~160之间比较合适。 - 交换概率
P
c
P_c
Pc:
控制着交换操作的频率, P c P_c Pc太大会导致高适应度的结构很快被破坏掉, P c P_c Pc太小搜索会停滞不前,一般 P c P_c Pc取0.25~0.75. - 变异概率
P
m
P_m
Pm:
是增大种群多样性的第二个因素,起到保持和恢复染色体多样性的作用, P m P_m Pm太小会产生新的基因块, P m P_m Pm太大,会使遗传算法变成随机搜索,一般取0.01~0.2。
关于取值不同的资料说法不一,总之变异概率很小
- 顺便在这里说一下二进制数和十进制数转换:
将一个长度为 n n n(此处取10)的二进制数转化为10进制数
m = 1011101110 x = 1 × 2 9 + 0 × 2 8 + 1 × 2 7 + 1 × 2 6 + 1 × 2 5 + 0 × 2 4 + 1 × 2 3 + 1 × 2 2 + 1 × 2 + 0 × 2 0 m=1011101110\\ x=1\times2^9+0\times2^8+1\times2^7+1\times2^6+1\times2^5+0\times2^4+1\times2^3+1\times2^2+1\times2+0\times2^0 m=1011101110x=1×29+0×28+1×27+1×26+1×25+0×24+1×23+1×22+1×2+0×20
matlab代码实现
%二进制->十进制
n=10;
m=[1 0 1 1 1 0 1 1 1 0];
x=0;
for i=1:n
x=x+m(i)*2^(n-i);
end
%十进制->二进制
x2=500;
for i=1:n
m2(n-i+1)=mod(x2,2);
x2=fix(x2/2);
end
- 将二进制转化的十进制数限定在一定范围
[
a
m
i
n
,
a
m
a
x
]
[a_{min},a_{max}]
[amin,amax]内
a = a m i n + b 2 n − 1 × ( a m a x − a m i n ) a=a_{min}+\frac{b}{2^n-1}\times(a_{max}-a_{min}) a=amin+2n−1b×(amax−amin)
其中, b b b 是转化后的十进制数, n n n 二进制串的长度
一个案例
利用遗传算法求下列函数的极大值:
z
=
x
3
+
(
2
×
y
−
x
)
4
x
∈
[
10
,
20
]
,
y
∈
[
10
,
20
]
z=x^3+(2\times y-x)^4\\ x \in[10,20],y\in[10,20]
z=x3+(2×y−x)4x∈[10,20],y∈[10,20]
首先用matlab看一下它的函数图像
还是附上代码吧
[x,y]=meshgrid(10:0.1:20);
y=x.^3+(2*y-x).^4;
mesh(x,y,z);
参数选择:种群数量 N = 100 N=100 N=100;进化代数 G = 100 G=100 G=100;交换概率 P c = 0.6 P_c=0.6 Pc=0.6;变异概率 P m = 0.015 P_m=0.015 Pm=0.015;串长设置为10
- 代码部分:
- 方式一
MyGA.m
clear all;
clc;
%参数初始化
%适应度函数f=x^3+(2y-x)^4
N=100;%种群数目
G=100;%进化代数
Pc=0.6;%交换概率
Pm=0.015;%变异概率
n=10;%串长,有两个参数每个都是10
x_min=10;
x_max=20;
y_min=10;
y_max=20;
%%
%初始化种群
E=round(rand(N,2*n));%生成一个N行,2n列的0-1内的随机数组,并四舍五入相当于二进制字符串
for k=1:G
time(k)=k;
%%
%计算每个个体的适应度
x=zeros(1,N);
y=zeros(1,N);
for i=1:N
x_temp=0;
y_temp=0;
for j=1:n
x_temp=x_temp+E(i,j)*2^(n-j);
end
x(i)=x_min+x_temp/(2^n-1)*(x_max-x_min);
for j=n+1:2*n
y_temp=y_temp+E(i,j)*2^(2*n-j);
end
y(i)=y_min+y_temp/(2^n-1)*(y_max-y_min);
F(i)=x(i)^3+(2*y(i)-x(i))^4;
end
%%
%找到最优个体
Best_F=max(F);%保存当前的最高适应度
F_temp=F;
[This_F,Index_F]=sort(F_temp);%将这一代个体按照适应度排序This_F是排序后个适应度值,Index_F是对应的个体
Best_Unit=E(Index_F(N),:);%最优个体
Best_Fit(k)=Best_F;
%%
%复制
%规则Nf(i)/sum(f(i))轮盘赌方法
F_sum=sum(F);
P_num=N*This_F/F_sum;
Unit_copynum=floor(P_num);
k=1;
for i=1:N
for j=1:Unit_copynum(i)
temp_E(k,:)=E(Index_F(i),:);
k=k+1;
end
end
for i=k:N
temp_E(i,:)=Best_Unit;
end
%%
%两两进行交叉操作
for i=1:N
Cross_point=round((20-1)*rand+1);%随机产生交叉点
Cross_Unit=round((N-1)*rand+1);%随机选择交叉的个体
temp_Pc=rand;
if Pc>temp_Pc
for j=Cross_point:1:20
temp_E(i,j)=temp_E(Cross_Unit,j);
end
end
end
% temp_E(N,:)=Best_Unit;
%%
%变异
for i=1:N
for j=1:20
temp_Pm=rand;
if Pm>temp_Pm
if temp_E(i,j)==1
temp_E(i,j)=0;
else
temp_E(i,j)=1;
end
end
end
end
E=temp_E;
end
plot(time,Best_Fit);
Best_Unit
2.方式二
每个部分通过函数实现,我自己试试哈
main.m
clear all;
clc;
%参数初始化
%适应度函数f=x^3+(2y-x)^4
N=100;%种群数目
G=100;%进化代数
Pc=0.6;%交换概率
Pm=0.015;%变异概率
n=10;%串长,有两个参数每个都是10
x_min=10;
x_max=20;
y_min=10;
y_max=20;
%%
%种群初始化
E=Population(N,n);
%开始进化
for k=1:G
time(k)=k;
%计算适应度
F=CalFintess(E,x_min,x_max,y_min,y_max);
%寻找当代最优
[Best_F,Best_Unit,Sort_F,Index_F]=FindBest(E,F);
Best_Fit(k)=Best_F;%保存每一代的最优适应度
Best_Single(k,:)=Best_Unit;%保存每一代的最优个体
%选择
[temp_E,mm]=Select(Sort_F,Index_F,E,Best_Unit);
%交叉
temp_E=Cross(temp_E,Pc);
%变异
temp_E=Mutation(temp_E,Pm);
%产生下一代种群
E=temp_E;
end
plot(time,Best_Fit);
Best_Unit
x=Turn(Best_Unit(1:n),x_min,x_max)
y=Turn(Best_Unit(n+1:2*n),y_min,y_max)
Population.m
function E=Population(N,n)
%初始化种群函数,用于初始化种群
%输入量:种群数 N,二进制串长 n
%输出:初始化后的种群
E=round(rand(N,2*n));%生成一个N行,2n列的0-1内的随机数组,并四舍五入相当于二进制字符串
Turn.m
function x=Turn(m,x_min,x_max)
%进制转换函数,用于将二进制数转换为[x_min,x_max]之间的十进制数
%输入量 二进制串 m ;范围 x_min,x_max
%输出量 转换后的数
n=length(m);
x=0;
for i=1:n
x=x+m(i)*2^(n-i);
end
x=x_min+x/(2^n-1)*(x_max-x_min);
CalFintess.m
function F=CalFintess(E,x_min,x_max,y_min,y_max)
%用于计算每个个体的适应度
%输入量:种群 E,以及变量的上下限
%输出量:各个体的适应度
[N,n]=size(E);
n=n/2;
for i=1:N
x(i)=Turn(E(i,1:n),x_min,x_max);
y(i)=Turn(E(i,n+1:2*n),y_min,y_max);
F(i)=x(i)^3+(2*y(i)-x(i))^4;
end
FindBest.m
function [Best_F,Best_Unit,Sort_F,Index_F]=FindBest(E,F)
%用于找到当代的最大适应度和最优个体
%输入量是种群 E ;适应度 F
%输出量是 最大适应度 Best_F,最优个体 Best_Unit,排序后的适应度 Sort_F,以及排序后各适应度对应的下标 Index_F
[N,~]=size(E);
Best_F=max(F);%保存当前的最高适应度
F_temp=F;
[Sort_F,Index_F]=sort(F_temp);%将这一代个体按照适应度排序This_F是排序后个适应度值,Index_F是对应的个体
Best_Unit=E(Index_F(N),:);%最优个体
Select.m
function [temp_E,Unit_copynum]=Select(Sort_F,Index_F,E,Best_Unit)
%用于选择适应度高的个体
%输入:排序后的适应度 Sort_F,对应的序号 Index_F 种群 E,当代最优个体 Best_Unit
[N,~]=size(E);
F_sum=sum(Sort_F);
P_num=N*Sort_F/F_sum;
Unit_copynum=floor(P_num);%用round四舍五入有可能总数超过 N不知道为啥
k=1;
for i=1:N
for j=1:Unit_copynum(i)
temp_E(k,:)=E(Index_F(i),:);
k=k+1;
end
end
[NN,~]=size(temp_E);
if NN<N
for i=NN:N
temp_E(i,:)=Best_Unit;
end
end
Cross.m
function temp_E=Cross(E,Pc)
%执行交叉操作
%输入量:种群 E,交叉概率 Pc
%输出量:交叉后的种群 temp_E
[N,n]=size(E);
n=n/2;
temp_E=E;
for i=1:N
Cross_point=round((2*n-1)*rand+1);%随机产生交叉点
Cross_Unit=round((N-1)*rand+1);%随机选择交叉的个体
temp_Pc=rand;
if Pc>temp_Pc
for j=Cross_point:1:2*n
temp_E(i,j)=temp_E(Cross_Unit,j);
end
end
end
Mutation.m
function temp_E=Mutation(E,Pm)
%执行变异操作
%输入量:种群 E,变异概率 Pm
%输出量:变异后的种群 temp_E
temp_E=E;
[N,n]=size(E);
n=n/2;
for i=1:N
for j=1:2*n
temp_Pm=rand;
if Pm>temp_Pm
if temp_E(i,j)==1
temp_E(i,j)=0;
else
temp_E(i,j)=1;
end
end
end
end
- 运行结果
可以看出在进化到第10代左右时就已经收敛到最大值。