本文旨在使用智能优化算法及其MATLAB实例(第2版) 一书中的例子,来透彻理解遗传算法的本质。
目标: 求解最大化函数 f(x)=x+10sin(5x)+7cos(4x)f(x)=x+10 \sin (5 x)+7 \cos (4 x)f(x)=x+10sin(5x)+7cos(4x) 的 xxx, 其中x∈[0,10]x\in[0,10]x∈[0,10]。
这个问题可以直接通过导数为0求解,但我们选择以此作为遗传算法的简单示例。
遗传算法的本质是优化后的穷搜算法, 事实上几乎所有此类启发式算法,本质都是用不同方式尽可能规避无效搜索后的优化版穷搜算法。
对于这个优化问题,变量xxx为连续变量,可行解有无穷个, 这样甚至连穷搜都不可行。 因此,我们首先要将可行域离散化,使其可行解个数为有限个方能进行搜索。 很简单的一种办法就是遗传算法中所谓的染色体编码: 定义一个编码XXX是一个L×1L\times 1L×1的 0−10-10−1向量, 例如:
X=[0,1,1,0,⋯,1]X=[0, 1, 1, 0, \cdots, 1]X=[0,1,1,0,⋯,1]
那么很显然, 共有2L2^L2L个不同的XXX。 我们可以认为每个XXX都代表一个原问题中的可行xxx, 具体而言,我们可以定义函数:
g(X)=0+m×102L,m=∑i=1L2i−1Xig(X) = 0 + \frac{m\times 10} {2^L}, m = \sum_{i=1}^{L}2^{i-1}X_ig(X)=0+2Lm×10,m=i=1∑L2i−1Xi
那么 g(X)g(X)g(X)就代表了[0,10][0, 10][0,10]之间的一个可行的xxx。
通过均匀量化的方式,我们将无限的可行空间缩小成一个有限的可行空间。 当LLL足够大时,解的精度几乎不会受损。 而这一简单的思想在遗传算法中就被称作听起来更高大上一些的染色体编码。
这样一来,使用穷搜算法的话,就是无脑遍历2L2^L2L个解,选择最好的那个。 接下来介绍遗传算法的搜索方式,以期在搜索数远少于2L2^L2L次的情况下,搜到很好的解。
以上就是遗传算法的具体流程。 虽然看到许多文章会说他各种优点,但笔者看来,本质上就是如果基于一些启发式的(来自于自然的)思想进行更高效的搜索,虽然欠缺扎实的理论分析,但看上去就是个行之有效的方案。 下面则是Matlab实例运行结果:
作者给出的代码中用到了已被淘汰的randint API, 这里笔者进行了修改使得可以直接在新版matlab上运行了,代码如下:
%%%%%%%%%%%%%%%%%%%%标准遗传算法求函数极值%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%初始化参数%%%%%%%%%%%%%%%%%%%%%%%%%%
clear all; %清除所有变量
close all; %清图
clc; %清屏
NP=50; %种群数量
L=20; %二进制数串长度
Pc=0.8; %交叉率
Pm=0.1; %变异率
G=100; %最大遗传代数
Xs=10; %上限
Xx=0; %下限
f=randi(2, NP,L) - 1; %随机获得初始种群
%%%%%%%%%%%%%%%%%%%%%%%%%遗传算法循环%%%%%%%%%%%%%%%%%%%%%%%%
for k=1:G%%%%%%%%%%%%将二进制解码为定义域范围内十进制%%%%%%%%%%%%%%for i=1:NPU=f(i,:);m=0;for j=1:Lm=U(j)*2^(j-1)+m;endx(i)=Xx+m*(Xs-Xx)/(2^L-1);Fit(i)= func1(x(i));endmaxFit=max(Fit); %最大值minFit=min(Fit); %最小值rr=find(Fit==maxFit);fBest=f(rr(1,1),:); %历代最优个体 xBest=x(rr(1,1));Fit=(Fit-minFit)/(maxFit-minFit); %归一化适应度值%%%%%%%%%%%%%%%%%%基于轮盘赌的复制操作%%%%%%%%%%%%%%%%%%%sum_Fit=sum(Fit);fitvalue=Fit./sum_Fit;fitvalue=cumsum(fitvalue);ms=sort(rand(NP,1));fiti=1;newi=1;while newi<=NPif (ms(newi))
不同于轮盘赌方案,君主方案的交叉、变异、选择操作如下:
假设有NNN个城市, 旅行商问题就是解决到这NNN个城市的顺序问题, 也即可行解 XXX 为 [1,2,⋯,N][1,2,\cdots, N][1,2,⋯,N]的任意乱序排列。 因此,我们可以将一个个体定义为一种乱序排列 (也即基因位数仍是323232, 但取值不再是0,10,10,1, 而是1,⋯,N1,\cdots, N1,⋯,N), 也即任意个体就是一个可行解。
为此,我们需要重新定义交叉、变异操作, 以使得创造出的新个体是可行解。
非常启发式的, 交叉操作可以定义为:随机交换两个个体成对的城市索引。 变异操作可定义为: 随机交换一个个体的一对城市索引。
由此我们也可以看出, 用遗传算法解决问题的核心,就在于如何定义交叉、变异操作,使得新个体仍是可行解,即实现有效搜索。