一个Demo助你快速入门进化算法

525 阅读4分钟

进化算法是一种优化算法,其受自然进化理论所启发,模拟自然选择的繁殖的过程,迭代求问题的最优解。

遗传算法可以为涉及搜索,优化和学习的各种问题提供高质量的解决方案。同时,它们类似于自然进化,因此可以克服传统搜索和优化算法遇到的一些障碍,尤其是对于具有大量参数和复杂数学表示形式的问题。

本文从计算机的角度出发,先介绍进化算法的基本概念,并用matlab语言实现一个进化算法的Demo(完整代码下载(访问密码:7892)),适合新手快速入门。

基本概念

染色体,在遗传算法中,每个个体都由代表基因集合的染色体组成,一条染色体可以表示为一个二进制串,其中每一位代表一个基因。 image.png

适应度函数(Fitness function),在种群每次迭代中,使用适应度函数对个体进行评估。适应度函数通常代表了某个试图求解的问题,适应度得分代表解的好坏。

选择(Selection),得出每个个体的适应度后,选择哪些个体将用于繁殖并生成下一代,适应度更高的个人更容易被选中(遗传优质基因给后代),但不排除有机会选择适应度低的个体。

交叉(Crossover),从当前代中选择双亲样本,将他们的部分染色体互换(交叉),以创建两个新的染色体,这两个染色体代表后代。 image.png

突变(Mutation),随机改变一个或多个染色体值,实现基因突变的效果。 image.png

进化算法步骤

image.png

Matlab 实现案例

以下代码均依据进化算法的步骤按顺序编写,代码注释详细并附带完整代码下载链接。

完整代码下载(访问密码:7892)

初始化种群 main.m

nVar = 100; % 染色体的长度
nPop = 30; % 初始化种群大小

% 定义种群结构
template.x = []; % 存染色体
template.y = []; % 存适应度值
Parent = repmat(template, nPop, 1); % 父母种群

% 初始化种群
for i = 1 : nPop
    Parent(i).x = randi([0,1], 1, nVar); % 产生0~1范围内,1行nVar列的整数
    Parent(i).y = fun(Parent(i).x); % 计算适应度
end

适应度函数 fun.m

function y = fun(x)
    y = sum(x);
end

选择操作 selectPop.m

function p = selectPop(Parent) % 选择个体
    % 锦标赛算法
    n = numel(Parent); % 返回数组个数
    index = randperm(n); % 随机返回1~n范围内n个下标
    p1 = Parent(index(1));
    p2 = Parent(index(2));
    if p1.y <= p2.y % 返回适应值更小的个体
        p = p1;
    else
        p = p2;
    end
end

交叉操作 crossPop.m

function [y1, y2] = crossPop(x1, x2)
    % 单点交叉
    n = numel(x1);
    s = randi([1,n-1]); % 设置交叉点,交叉点不能为开头也不能为最后一个!
    
    y1 = [x1(1:s) x2(s+1:end)];
    y2 = [x2(1:s) x1(s+1:end)];
end

变异操作 mutatePop.m

function p = mutatePop(x, mu)
    % 单点变异,取反
    if rand <= mu % mu为变异概率,成立则变异,rand值为0-1的double
        n = numel(x);
        s = randi([1, n]); % 取变异位(1位)
        if x(s) == 0 % 取反
            x(s) = 1;
        else
            x(s) = 0;
        end
    end
    p = x;
end

产生下一代种群 main.m

nPc = 0.8; % 子代的比例
nC = round(nPop * nPc /2) * 2; % 子代规模的大小,round规定一定为整数,并偶数化

Offspring = repmat(template, nC, 1); % 定义子代种群

每次种群迭代 main.m

maxIt = 2000; % 最大迭代次数
for It = 1 : maxIt
    Offspring = repmat(template, nC, 1); % 每次迭代都需更新子代
    
    % 求出子代
    for j = 1:2:nC % nC 需确保为偶数
        % 选择操作
        p1 = selectPop(Parent);
        p2 = selectPop(Parent);
        % 交叉操作
        [Offspring(j).x, Offspring(j+1).x] = crossPop(p1.x, p2.x);
    end
    
    % 变异操作
    for k = 1 : nC
       Offspring(k).x = mutatePop(Offspring(k).x, nMu);
       Offspring(k).y = fun(Offspring(k).x);
    end
    
    % 筛选数据,防止数据越来越多
    newPop = [Parent; Offspring]; % 拼接数据
    [~, so] = sort([newPop.y], 'ascend'); % 排序,从小到大,so是下标索引
    newPop = newPop(so); % newPop此时已排序
    Parent = newPop(1 : nPop); % 取前 1~nPop个
    
    disp(['迭代次数:', num2str(It), ',最小值为:', num2str(Parent(1).y)]);
    
end

运行效果截图 image.png

可以看到需要迭代近1k4次才能取到最小值,这里的迭代次数很大程度上受选择、交叉、变异操作的影响,采用其他算法或能得到更好的收敛效果。

Reference

遗传算法(Genetic Algorithm)详解与实现 - 掘金 (juejin.cn)

遗传算法,有手就行_哔哩哔哩_bilibili