Matlab应用笔记--遗传算法

注:本篇随笔依据《Matlab在数学建模上的应用》中第4章介绍来写,主要介绍简单遗传算法的思想及其Matlab实现
(博客以及Matlab小白,若有不当欢迎指出)

遗传算法(GA)简介

模拟达尔文生物进化论的自然选择和孟德尔遗传学机理的生物进化过程的计算模型,一种通过模拟自然进化过程搜索最优解的方法。
遗传算法本质是启发式随机搜索算法,通过遗传算法得到的解多是全局最优解。
简单的遗传算法注重模拟竞争关系,而自然界还有其它作用(协作、寄生关系等),当问题的优化异常复杂难以处理时,传统的简单遗传算法无能为力,这时引入协同关系的“协同进化遗传算法”就发挥作用了。

遗传算法的实现

(1)编码

遗传算法编码主要有浮点编码和二进制编码两种,这里只介绍二进制编码。

设某一参数的取值范围是(L,U),精度是p,则有效取值长度为length=ULp
现在,要用一个二进制数串能表示所有可能数值,则该数串的长度k应该满足
2k11<length2k1 (数串0代表L,2k1代表U)
(要保证二进制数串表示的数精度不得低于p,即δ=length2k11
若待编码的数值为m,则mL间的长度为length=mLp
对应的“二进制编码大小”为“mL间的偏移长度”除以“二进制数的精度”,
val=length÷δ=length÷length2k1=length×2k1length
最后对直接对val进行转换即可(普通的十进制转二进制)

例如:取值范围为(3,34),精度是0.01,则length=3430.01=3100
k=12,则满足21211<31002121
若待编码的数为17,则173间的偏移长度为length=1730.01=1400
所以val=1400÷31002121=1400×212131001849
直接对1849转成二进制数串:011100111001

(2)解码

解码是编码的逆过程。

设某一参数的取值范围是(L,U),精度是p,则有效取值长度为length=ULp
二进制数串的长度直接读出为k
二进制数的精度是δ=length2k1
若待解码的数串为bkbk1bk2...b3b2b1,先将其直接转为二进制编码大小val
则“待求十进制数mL间的偏移长度”为“二进制编码大小”乘以“二进制数的精度”
length=val×δ=val×length2k1
最后L加上“偏移长度乘以十进制精度”即为待求十进制数:m=L+lenght×p

例如:取值范围为(3,34),精度是0.01,则length=3430.01=3100
待解码数串为011100111001
长度k=12,二进制数的精度是δ=31002121
转成十进制数:val=1849
偏移长度:length=1849×310021211400
待求十进制数:m=3+1400×0.01=17

(3)个体适应度评估

根据一定的筛选机制算出染色体的适应度,以便后继的复制和选择操作。
通常求目标函数最大值的问题可以直接把目标函数作为检测个体适应度大小的函数。

先将染色体串解码为十进制真值m
根据真值m评价目标函数f(m)
将目标函数值转为适应度eval(U)=G(f(m))
(对于极大值问题,目标函数值可直接作为适应度)

例如:给定染色体串011100111001
解码后:m=17
设目标函数:f(x)=ex2sin(x),代入得f(17)1437
若对于求极大值问题,令G(x)=xeval(U)=G((f(17))=G(1437)=1437
即染色体011100111001在当前环境的适应度为1437

(4)染色体复制

复制运算是根据个体适应度大小决定下代遗传的可能性。
适应度越高(低),复制的几率越大(小),遗传基因就会在种群中扩散(淘汰)。

设种群中个体总数为N,个体i的适应度为fi,则个体i被选取的几率为
Pi=fik=1Nfk
个体复制的几率决定后,再产生[0,1]区间的均匀随机数决定哪些个体参加复制

例如:种群中个体总数为100,第29个个体的适应度为f29=5140
所有个体的适应度之和为k=1100fk=114514
则第29个个体被选取的几率为P29=51401145144.5
将所有个体的复制几率按序号依次填充到区间[0,1]
假设第29个个体的覆盖区间是3337.5
再产生[0,1]区间的均匀随机数,若该随机数在3337.5内,则第29号个体被选中进行复制。

(4)染色体交叉(配)

交叉运算是使用单点或多点进行交叉的算子。
首先用随机数产生一个或多个交配点位置,然后两个个体在交配点位置互换部分基因码,形成两个子个体。

设染色体总量为N,交配概率为Pc,则参与交配的染色体数量大概为n=[N×Pc](取整)
产生在[0,1]区间内的N个随机数R1R2...RN,挑选出最低的n个随机数作为交配对象
(或者相邻序号为一对,从1号开始到N号依次产生N2对,每一对内产生一随机数R,若R<Pc则交配,否则不交配)
交配时以两个染色体位单位进行,随机产生一个[0,k1]区间内的数t(k是染色体长度)
然后将两个染色体的第t个位点右边的位段([t+1,k1]部分)进行互换,得到两个新的子辈染色体。

例如:染色体总量为100,交配概率为25,则参与交配的染色体数量大概为n=25
以相邻序号为一对,从1号开始到100号依次产生50对,每一对内产生一随机数
假设第7对(第13、14号染色体)产生的随机数0.17<0.25,它们进行交配
假设染色体位长为12,再在[0,11]区间内产生一随机数t=5为交配位点
假设原来的13、14号染色体分别为
U13=011000110111
U14=101011011001
将它们的[6,11]位段进行互换,得子代染色体分别为
U13=011000011001
U14=101011110111

(5)染色体变异(基因突变)

依照突变运算规则,种群内所有的基因(每个二进制位)都有概率进行突变(取反)。

设染色体总量为N,每个染色体的长度为k,突变概率为Pm,则每一代突变的基因数大概为n=[N×k×Pm](取整)
产生N×k个位于[0,1]区间内的随机数,随机数从1开始到N×k依次编号
若某号num随机数小于突变概率Pm,则对应染色体内的某个二进制位取反
numk号染色体上的numk号基因)

例如:设染色体总量为10,每个染色体的长度为12,突变概率为Pm=0.01,则每一代突变的基因数大概为n=1(取整)
产生120个位于[0,1]区间内的随机数,随机数从1开始到120依次编号
假设第75号随机数num=0.005<0.01
对应第7512=6染色体上的7512=3号基因位进行突变,即
U6=101101100001 (原6号染色体)
U6=100101100001 (基因突变后6号染色体)

(6)染色体倒位

一般简单的问题不用考虑染色体倒位,当问题比较复杂时则可能需要倒位运算。
这里只简单介绍倒位的效果,具体实现不细展开(可自行实现)。

U=1101010010110001(倒位前)
U=1100011010010101(选择[4,14]位段进行180°翻转)

遗传算法的Matlab实现

(1)Matlab遗传算法工具箱

本篇随笔只基于Matlab7.0 Version版本的工具箱讨论,其他版本的工具箱可自行尝试或找其它方法。
Matlab的遗传算法工具箱(GA Toolbox)对常用的遗传运算进行集成,用户能方便调用,但是过度的封装也让该工具箱缺乏灵活性,某些问题最好还是编写算法源程序去解决。
Matlab7.0 Version的遗传算法工具箱有自带的遗传算法与直接搜索工具箱。

使用方法:直接在命令窗口中输入optimtool,在弹出的界面上填写相关参数和函数句柄或使用M文件进行程序设计。(这里只细说程序设计部分,工具箱的其它具体使用部分略)
遗传算法与直接搜索工具箱有ga和gaoptimset两个核心函数。

函数ga的一般语法格式

[x,fval,reason] = ga(@fitnessfun,nvars,options)

x为遗传进化后自变量最佳染色体返回值
fval为最佳染色体的适应度
reason为算法停止的原因
@fitnessfun为适应度句柄函数
nvars为目标函数自变量的个数
options为算法的属性设置(通过函数gaoptimset赋予)

函数gaoptimset的语法格式

options = gaoptimset('PropertyName1',PropertyValue1',
'PropertyName2',PropertyValue2','PropertyName3',PropertyValue3',...,...)

通过不同的“属性名”和“属性值”设置遗传算法的参数,返回句柄options用于函数ga中

(函数gaoptimset属性)

一般程序设计

function f = fitnessfun(x)
%适应性函数设计
if(...) %符合约束条件
f = f(x); %目标函数的表达式
else %不符合约束条件
f = ...; %相应处理(比如要求最大值时可以令f = -inf)
end
%主函数
clc;
clear;
close all;
options = gaoptimset(...); %设置好属性
[x,fval,reason] = ga(@fitnessfun,nvars,options) %根据设置好的属性和目标函数求解

若采用接力进化,则主函数还可以这样写

%主函数
clc;
clear;
close all;
%第一轮遗传运算
options = gaoptimset(...);
[x,fval,reason,output,finnal_pop] = ga(@fitnessfun,nvars,options);
%第二轮遗传运算,初始化种群为第一趟结束时的种群
options = gaoptimset('InitialPopulation',finnal_pop,...);
[x,fval,reason,output,finnal_pop2] = ga(@fitnessfun,nvars,finnal_pop);
%若需要,可以往后继续接力

(2)Matlab遗传算法的源程序模版

工具箱并非万能,很多时候工具箱不够灵活,需要我们自己写出源程序以针对复杂的多约束非线性规划问题。

一般遗传算法的设计(伪代码)

t = 0; %遗传代数
初始化P(t); %初始化种群
whlie(不满足停止准则)
begin
t = t+1;
从P(t-1)中选择P(t); %选择
重组P(t); %交叉和变异
计算P(t)的适应值;
end

一个简单遗传算法的详细程序设计模版

主函数

%主函数
clc;
clear;
close all;
%全局变量
global BitLength %染色体位长
global boundsbegin %变量取值范围中的下限
global boundsend %变量取值范围中的上限
bounds = [-2 2]; %一维自变量取值范围S
precision = 0.0001; %运算精度
boundsbegin = bounds(:,1);
boundsend = bounds(:,2);
BitLength = ceil(log2((boundsend - boundsbegin)'./precision));
popsize = 50; %种群大小
Generationmax = 12; %遗传代数
pcrossover = 0.90; %交配概率
pmutation = 0.09; %变异概率
%产生初始种群
population = round(rand(popsize,BitLength));
%计算适应度,返回适应度Fitvalue和累积概率cumsump
[Fitvalue,cumsump] = fitnessfun(population);
%开始迭代模拟遗传
Generation = 1;
xmax(1:Generationmax) = 0;
ymax(1:Generationmax) = 0;
ymean(1:Generationmax) = 0;
while Generation <= Generationmax
for j = 1:2:popsize
%复制操作略,可添加
seln = selection(cumsump);%选择
scro = crossover(population,seln,pcrossover);%交配
%变异
smnew(j,:) = mutation(scro(1,:),pmutation);
smnew(j+1,:) = mutation(scro(2,:),pmutation);
end
%产生新种群
population = smnew;
%计算新种群的适应度
[Fitvalue,cumsump] = fitnessfun(population);
%记录当前代最好的适应度和平均适应度
[fmax,nmax] = max(Fitvalue);
ymax(Generation) = fmax;
ymean(Generation) = mean(Fitvalue);
%记录当前代的最佳染色体个体
x = transform2to10(population(nmax,:));%译码
xx = boundsbegin + ... %转到区间范围内对应的值
x*(boundsend-boundsbegin)/(power(2,BitLength)-1);
xmax(Generation) = xx;
Generation = Generation + 1;
end
%记录所有代中最好的适应度及其对应的染色体
Generation = Generation - 1;
[fmax,nmax] = max((ymax'));
Bestpopulation = xmax(nmax)
Besttargetfunvalue = targetfun(Bestpopulation)
%绘制曲线
%若发现平均适应度与最大适应度在曲线上有趋同的形态,则表明算法收敛进行顺利
%最大适应度个体连续若干代都没有发生进化表明种群已经成熟
figure(1);
hand1 = plot(1:Generation,ymax);
set(hand1,'linestyle','-','linewidth',1.8,'marker','*',...
'markersize',6)
hold on;
hand2 = plot(1:Generation,ymean);
set(hand2,'color','r','linestyle','-','linewidth',1.8,...
'marker','h','markersize',6)
xlabel('进化代数');ylabel('最大/平均适应度');xlim([1 Generationmax]);
legend('最大适应度','平均适应度');
box off;
hold off;

计算适应度的函数

function [Fitvalue,cumsump] = fitnessfun(population)
%计算适应度的函数
global BitLength
global boundsbegin
global boundsend
popsize = size(population,1); %求个体数
Fitvalue(1:popsize) = 0; %初始化
cumsump(1:popsize) = 0; %初始化
%计算适应值
for i = 1:popsize
x = transform2to10(population(i,:)); %译码
xx = boundsbegin + ... %转到区间范围内对应的值
x*(boundsend-boundsbegin)/(power(2,BitLength)-1);
Fitvalue(i) = targetfun(xx); %计算适应度
end
Fitvalue = Fitvalue' + 230; %保证适应值为正数
%计算选择概率
fsum = sum(Fitvalue);
Pperpopulation = Fitvalue/fsum;
%计算累积概率
cumsump(1) = Pperpopulation(1);
for i = 2:popsize
cumsump(i) = cumsump(i-1)+Pperpopulation(i);
end
cumsump = cumsump';

新种群交叉操作

function scro = crossover(population,seln,pc)
%新种群交叉操作
global BitLength
pcc = IfCroIfMut(pc); %根据交叉概率决定是否交叉
if pcc == 1
chb = round(rand*(BitLength-2))+1; %随机产生交叉位
scro(1,:) = [population(seln(1),1:chb) population(seln(2),chb+1:BitLength)];
scro(2,:) = [population(seln(2),1:chb) population(seln(1),chb+1:BitLength)];
else
scro(1,:) = population(seln(1),:);
scro(2,:) = population(seln(2),:);
end

新种群变异操作

function snnew = mutation(snew,pmutation)
%新种群变异操作
global BitLength
snnew = snew;
pmm = IfCroIfMut(pmutation); %根据变异概率决定是否变异
if pmm == 1
chb = round(rand*(BitLength-1))+1; %随机产生交叉位
snnew(chb) = abs(snew(chb)-1);
end

判断遗传运算是否需要进行交叉或变异

function pcc = IfCroIfMut(mutORcro)
%判断遗传运算是否需要进行交叉或变异
%设立长度为100的池,在里面抽选
test(1:100) = 0;
length = round(100*mutORcro);
test(1:length) = 1;
n = round(rand*99)+1;
pcc = test(n);

从种群中选中两个个体

function seln = selection(cumsump)
%从种群中选中两个个体
seln(1:2) = 0;
for i = 1:2
r = rand; %产生一个随机数
j = 1;
while cumsump(j)<r
j = j+1;
end
seln(i) = j; %选中个体的序号
end

将二进制数转换为十进制数

function x = transform2to10(Population)
%将二进制数转换为十进制数
global BitLength
x = Population(BitLength);
for i = 1:BitLength-1
x = x+Population(BitLength-i)*power(2,i);
end

目标函数

function y = targetfun(x)
%目标函数
y = 200*exp(-0.05*x).*sin(x);

书上例题以及实机操作结果:

例1


结果

例2


遗传500代

遗传100代

遗传36代

遗传12代

原解决算法中的变异概率设置为0.09,若改为0.01,则在多代遗传的效果会更好

例3


获得的解不稳定
(某次跑的结果)

(另一次跑的结果)

posted @   kksk43  阅读(1305)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
特效
黑夜
侧边栏隐藏
点击右上角即可分享
微信分享提示