遗传算法——从做九转大肠和鼠头鸭脖中得到启发

目录

算法简介

适用赛题

函数优化、组合优化问题。

NP难问题

优点

缺点

0-1背包问题(组合优化)例题与原理讲解

背包问题简介

具体问题描述 

数学建模表达

算法思路

步骤一:种群初始化

步骤二:选择运算

步骤三:交叉运算

步骤四:变异运算

步骤五:迭代循环

代码求解 

初始化

基因编码

轮盘赌法

交叉变异与迭代

算法简介

一、问题定义:
Miking大厨想设计一道新菜,目标是做出口感最好的菜肴。

二、变量定义:
每个菜肴都可以看作是一个个体,主要有以下变量:

  • 原料和分量
  • 烹饪方法
  • 调料选择和量

三、初始化种群:
随机生成多组原料、方法和调料的组合,作为初代种群个体。

四、评估函数:
请多位评委尝试各个菜肴,给出分数,作为个体的适应度。

五、选择:
根据适应度高低,给个体设置被选中的概率。使用轮盘赌法选择优质个体。

六、交叉:
交叉两个个体中评分较好的成分,比如鼠头鸭脖和九转大肠。

七、变异:
一小部分个体随机更改成分,比如把九转大肠变成九转鸭脖。

八、重复:
使用新一代个体重复四五六七步,进化出口感更好的菜肴。

以菜谱为例可以很容易理解和解释遗传算法的基本思路。通过模拟个体进化,寻找最优解。

适用赛题

函数优化、组合优化问题。

适合各种疑难杂症! 不依赖于问题的背景领域 ,使用方便,连续 / 离散、单峰 / 多峰等等各种形式均可。之前讲过的 非线性、多模型、多目标的函数优化问题都可以。

NP难问题

模拟退火算法中讲过的 TSP 问题 背包问题 、图形划分问题等。

优点

全局搜索能力强。
可直接求解离散问题,不依赖梯度。

缺点

局部搜索能力差。耗时更多——可多种算法结合改进(不过新手别用)

0-1背包问题(组合优化)例题与原理讲解

背包问题简介

将一堆物品尽量塞进一个背包里,使得包内物品 总价值最高
假设背包容量足够大,但有 载重上限 ,物品太重会被撑破
物品 无法切割 ,每个物品只能“放”与“不放

具体问题描述 

 例题:现有12份快递需要配送,背包容量350,每份快递的体积不同、收益不同,应该将哪些物品放进背包,使得所总体积不超过背包容量且总收益最大?

数学建模表达

用“ 0 ”和“ 1 ”表示物品的装包状态
一个物品装包状态为 0 代表没有被装进包中, 1 代表被装进包中
例如“ 100111001001 ”代表第 1 4 5 6 9 12 个物品被装进背包
每一个物品的装包状态( 0 1 )作为一个 基因
问题的一个解:一组 12 个数构成的数组为一个 个体(染色体)

算法思路

步骤一:种群初始化

N 个物品 ,则 随机 生成 N 0 1(解分量) 构成一个数组,作为一个 个体(可行解)。
一般生成20-200个个体作为种群。

步骤二:选择运算

求每个个体的 适应度 :把解 𝑥 带入目标函数求函数值 𝑓(𝑥)

 轮盘赌法选个体,使得"适应度越高的个体被选中的概率越大",且"适应度低的个体仍有被选中的可能"

步骤三:交叉运算

先根据交叉概率判断是否执行交叉操作,再随机选择交叉的基因位置

步骤四:变异运算

判断每个个体的每个基因是否进行变异运算。

步骤五:迭代循环

记录本轮迭代的最优个体和最优适应度
重复步骤二、三、四,直至满足终止条件(比如迭代满 100 次)

代码求解 

经典0-1背包问题,属于NP-Hard问题,暴力求解或动态规划在可选物品数量较多时都会带来指数级的时间复杂度,运行时间过长。此时可考虑启发式算法。

初始化

Np = 80;    % 种群个体数                        
N = 12;    % 物品数
V = 350;    % 背包容量  

Pc = 0.85;      % 交叉概率                  
Pv = 0.02;      % 变异概率
Gtime = 150;    % 遗传代数(迭代次数)                        
                           
C = [78,39,90,82,61,52,12,37,49,55,64,8];  % 物品体积
W = [64,37,22,41,76,34,22,21,10,57,32,12]; % 物品价值

alpha = 5;  % 惩罚系数

基因编码


将变量转为二进制0/1的形式,以便于交叉和变异操作
对于1~12号快递,每个快递选中则设为1,不选则设为0,最终得到一组12位的0-1数组。例如[1,1,0,1,0,0,0,0,1,0,1,1],意味着第1、2、4、9、11和12件快递被放入背包;
初始化:生成均匀分布的随机整数,Np行,N列,值为1表示选中该物品,0则不选
每一行就是一种选择方案,种群个体数Np为80,对应80种方案。

population = randi([0,1],Np,N);     %随机获得初始种群
%randi: 这是MATLAB的一个函数,用于生成随机整数。
%[0,1]: 这是randi函数生成整数的范围。在这里,它将生成0或1。
%Np, N: 这些是输出矩阵的维度。输出将是一个有个体数量Np行和解决方案个数N列的矩阵。
% 开始迭代
for t = 1:Gtime
    % 求每个个体的适应度
    for i = 1:Np
         fit(i) = fitness(population(i,:),C,W,V,alpha);
         %population(i,:): 这选取了population矩阵中的第i行。这表示种群中的第i个个体或染色体。
         %这个fitness函数的目的是评估当前个体的适应度。
    end
    maxfit = max(fit);  % 适应度最大值(本轮迭代最优解的函数值)
    minfit = min(fit);
    
    % find函数找到满足等式的元素(最优个体)下标(在群体里的序号是几),可能有多个
    findex = find(fit == maxfit);
    fBest = population(findex(1,1),:);  % 根据序号选中最优个体

轮盘赌法


累积概率表示每个个体之前所有个体的选择概率之和,相当于概率论中的概率分布函数F(x)。
轮盘赌法使得"适应度越高的个体被选中的概率越大",且"适应度低的个体仍有被选中的可能"。

    fitvalue = fit ./ sum(fit);     % 被选中的概率,适应度越高的概率越大,累加为1

    fitvalue = cumsum(fitvalue);    % 累计概率。第i个元素,是原第1到i个元素之和。cumsum用于计算数组的累积和。
    %如果fitvalue是[0.1, 0.2, 0.3],那么累积和就是[0.1, 0.3, 0.6]。
    bet = sort(rand(Np,1));         % 随机生成Np个(种群个体数)0到1的数并排序
    fit_i = 1;
    new_i = 1;
    while new_i <= Np
        % 选择的核心逻辑排名第fit_i的个体,相应的累积概率大于轮盘中第new_i大的随机数时被选中
        if (fitvalue(fit_i)) > (bet(new_i))   
            %fprintf("选中第%d个个体,",fit_i)
            choosef(new_i,:) = population(fit_i,:);     % 该个体选中,赋值给choosef
            new_i = new_i + 1;  
            %fprintf("轮盘随机数变为%d;",new_i)
        else
            %fprintf("第%d个个体未被选中,",fit_i)
            fit_i = fit_i + 1;
            %fprintf("准备查看第%d个个体",fit_i)
        end
    end

思考:会不会出现fit_i大于80(数组population的元素个数)而带来语法错误?
轮盘赌法中的while循环,如果一直不满足if,随着fit_i不断增加,超过数组population的元素数量不就导致了语法错误么?如本题中fitvalue里有80个元素,如果fit_i增加到81怎么办?


解答:不会出现这种错误。因为fitvalue是累积概率,最后一项fitvalue(Np)=1;而bet里的元素都是大于0且小于1的随机数,因此fitvalue(80)=1>bet(80);
所以while循环到最后,一定是new_i先增大到大于Np(本题Np=80)从而满足new_i <= Np,使得while循环结束,因此不会出现“fit_i超过80导致超出population数组下标的上限而报错”的情况。

交叉变异与迭代

    % 交叉操作:第i个个体和第i+1个个体,进行第j位的基因互换
    for i = 1:2:Np  % 第1个和第2个个体,第3和第4个个体……两两交叉
        p = rand;   % 随机生成一个0到1之间的数,满足均匀分布
        if p < Pc   % 交叉概率Pc=0.85,及有85%的概率满足if语句
            q = randi([0,1],1,N);  % 生成1行Nt列的矩阵,其中元素随机是0或1
            for j = 1:N            % 对于12个物品基因(0或1),随机选中某些基因来进行交叉
                if q(j) == 1        % 随机生成的q中该位置元素为1,意味着第j个物品基因被选中进行交叉
                    temp = choosef(i + 1,j);            % 第i+1个个体的第j个基因赋值给临时变量
                    choosef(i + 1,j) = choosef(i,j);    % 第i个个体的第j个基因赋值给第i+1个个体
                    choosef(i,j) = temp;                % 同样,第i+1个个体的第j个基因复制给第i个个体,完成交叉
                end
            end
        end
    end

    % 变异操作
    for n = 1:Np        % 每个个体
        for m = 1:N     % 每个基因
            variation = rand(1,1);      % 生成一个大于0小于1的随机数
            if variation < Pv           % Pv=0.02,则有2%的概率满足if
                choosef(n,m) = ~choosef(n,m);   % 符号"~"是取反,0变1,1变0
            end
        end
    end
    population = choosef;       % 交叉和变异后的群体
    population(1,:) = fBest;    % 把交叉变异前的最优个体保留在群体首位
    trace(t) = maxfit;          % 本轮迭代的最优适应度
end
fBest;    % 迭代之后的最优个体
figure
plot(trace)
xlabel('迭代次数')
ylabel('目标函数值')
title('遗传算法适应度迭代')
function result = fitness(f,C,W,V,alpha)
fit = sum(f.*W);    % 当前方案总价值
TotalV = sum(f.*C); % 当前方案总体积
if TotalV > V
    fit = fit - alpha * (TotalV - V);   % 该方案所选的物品超过了背包总体积
end
result = fit;
end
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值