目录

用遗传算法破解一元函数最大值问题从原理到-MATLAB-实现

用遗传算法破解一元函数最大值问题:从原理到 MATLAB 实现

在优化问题的世界里,很多函数并非呈现出简单的线性或凸性特征,传统的求导等解析方法往往难以奏效。比如形如f(x) = 3x + 6cos²(5x) + 7sin(4x)的复杂函数,其图像布满了多个局部极值点,想要找到全局最大值,就需要借助更具 “探索性” 的算法。遗传算法正是这样一种受生物进化启发的智能优化方法,它能模拟自然选择、交叉和变异的过程,在解空间中高效搜索最优解。本文将以求解上述一元函数在x∈[20,30]范围内的最大值为例,详细讲解遗传算法的原理,并通过完整的 MATLAB 代码实现,带大家一步步见证算法的优化过程。

一、问题背景:我们要解决什么?

首先明确本次优化的核心目标:在x∈[20,30]的区间内,找到使得目标函数f(x) = 3x + 6cos²(5x) + 7sin(4x)取得最大值的x值及对应的函数值。

为什么这个问题需要遗传算法?我们可以简单分析目标函数的特性:

  • 函数包含线性项3x,会随x增大而单调递增;
  • 同时包含三角函数项6cos²(5x)7sin(4x),这两个项会带来高频的波动,使得函数图像呈现出 “整体上升、局部震荡” 的特征,产生大量局部极值点。

传统方法(如梯度上升法)很容易陷入某个局部最大值,而遗传算法通过 “种群进化” 的方式,能在多个可能的解之间进行选择和迭代,更大概率找到全局最优解。

二、遗传算法核心原理:模拟生物进化的 “智能搜索”

遗传算法(Genetic Algorithm, GA)的灵感来源于达尔文的生物进化论,核心思想是 “适者生存、优胜劣汰”。它将每个可能的解(即x值)编码为 “染色体”,多个染色体组成 “种群”,通过选择、交叉、变异三个核心操作,让种群一代一代进化,最终筛选出适应度最高的个体(即最优解)。

1. 关键概念对应

为了更好地理解算法逻辑,我们先建立 “算法术语” 与 “生物进化” 的对应关系:

生物进化概念遗传算法概念本次问题中的具体含义
个体染色体一个二进制编码,对应一个可能的x
种群染色体集合多个二进制编码组成的集合,对应多个候选x
适应度函数值候选x代入目标函数得到的f(x)值,值越大适应度越高
自然选择选择操作优先保留适应度高的染色体,淘汰适应度低的染色体
基因重组交叉操作两个染色体交换部分 “基因”(二进制位),产生新的染色体
基因突变变异操作染色体的某个 “基因”(二进制位)随机翻转(0 变 1 或 1 变 0),增加种群多样性

三、MATLAB 代码实现:一步步拆解算法逻辑

接下来,我们对照完整的 MATLAB 代码,逐模块解析遗传算法的实现过程。代码整体分为 “参数设置、种群初始化、进化迭代(选择 - 交叉 - 变异)、结果输出与可视化” 五个部分,同时包含三个核心工具函数(适应度计算、选择、交叉、变异)。

1. 第一步:参数设置 —— 为算法 “定规矩”

首先需要定义遗传算法的核心参数,这些参数会直接影响算法的搜索效率和优化结果,需要根据问题特点合理设置:


% 参数设置
pop_size = 100;          % 种群大小:每次迭代保留100个候选解,越大多样性越强但计算量越大
chrom_length = 10;       % 染色体长度(二进制编码):10位二进制可表示0-1023,决定x的精度
max_generations = 100;   % 最大进化代数:迭代100次后停止,避免无限循环
crossover_prob = 0.8;    % 交叉概率:80%的概率进行交叉操作,保证种群更新效率
mutation_prob = 0.01;    % 变异概率:1%的概率翻转二进制位,避免陷入局部最优
x_min = 20;              % x的取值下限
x_max = 30;              % x的取值上限

这里需要重点解释染色体长度的选择:
10 位二进制数的取值范围是0~2¹⁰-1=1023,我们通过线性映射将其转换为[20,30]区间内的x值,映射公式为:
x = x_min + 二进制解码值 × (x_max - x_min) / (2^chrom_length - 1)
染色体长度越长,x的精度越高(10 位对应的精度约为(30-20)/1023≈0.0098),但计算量也会随之增加。

2. 第二步:种群初始化 —— 生成第一代 “候选解”

种群是算法的起始点,我们需要随机生成pop_size个长度为chrom_length的二进制向量,每个向量代表一个候选解(染色体):


% 初始化种群:100行(个体)×10列(二进制位),元素为0或1
population = randi([0, 1], pop_size, chrom_length);

例如,某个染色体可能是[1,0,1,0,0,1,1,0,1,0],后续会将其解码为具体的x值。

3. 第三步:核心迭代 —— 让种群 “一代更比一代强”

这是遗传算法的核心环节,通过max_generations次迭代,每次迭代包含 “计算适应度、选择、交叉、变异” 四个步骤,逐步优化种群。

(1)计算适应度:给每个个体 “打分”

适应度函数是连接 “染色体” 和 “目标函数” 的桥梁,它将二进制编码的染色体解码为x值,再代入目标函数计算出适应度(即函数值),适应度越高的个体越容易被保留。

适应度函数实现


function fitness = fitness_function(individual, x_min, x_max, chrom_length)
    % 1. 二进制转十进制:如[1,0,1]→5
    x_dec = bin2dec(num2str(individual));
    % 2. 映射到[20,30]区间:将十进制值线性缩放
    x = x_min + x_dec * (x_max - x_min) / (2^chrom_length - 1);
    % 3. 计算适应度(目标函数值)
    fitness = 3 * x + 6 * cos(5 * x).^2 + 7 * sin(4 * x);
end

在主迭代中,我们用arrayfun批量计算每个个体的适应度,并记录每一代的最优适应度:


% 初始化最优适应度记录数组
best_fitness_values = zeros(1, max_generations);

for generation = 1:max_generations
    % 计算所有个体的适应度
    fitness_values = arrayfun(@(i) fitness_function(population(i, :), x_min, x_max, chrom_length), 1:pop_size);
    
    % 记录当前代的最优适应度
    [best_fitness, ~] = max(fitness_values);
    best_fitness_values(generation) = best_fitness;
    disp(['第', num2str(generation), '代:最优适应度 = ', num2str(best_fitness)]);
    % ... 后续选择、交叉、变异操作 ...
end
(2)选择操作:“适者生存” 的核心

选择操作的目的是从当前种群中筛选出适应度高的个体,为后续的交叉、变异提供 “优质亲本”。本文采用最经典的轮盘赌选择法,其原理是:个体的选择概率与自身适应度成正比,适应度越高,被选中的概率越大(类似轮盘上的扇形面积越大,指针指向它的概率越大)。

轮盘赌选择函数实现


function selected_population = roulette_wheel_selection(population, fitness_values)
    % 1. 计算总适应度
    total_fitness = sum(fitness_values);
    % 2. 计算每个个体的选择概率
    selection_prob = fitness_values / total_fitness;
    % 3. 计算累积概率(轮盘的“刻度”)
    cum_prob = cumsum(selection_prob);
    % 4. 轮盘赌选择:生成pop_size个随机数,对应选择个体
    [pop_num, chrom_len] = size(population);
    selected_indices = zeros(pop_num, 1);  % 存储选中个体的索引
    
    for i = 1:pop_num
        r = rand();  % 生成0~1的随机数
        % 找到第一个累积概率大于r的个体索引
        selected_indices(i) = find(r < cum_prob, 1, 'first');
    end
    
    % 5. 生成选择后的种群
    selected_population = population(selected_indices, :);
end
(3)交叉操作:“基因重组” 产生新个体

交叉操作是遗传算法产生新解的主要方式,它模拟生物的基因重组过程:随机选择两个亲本染色体,在某个 “交叉点” 交换部分基因,产生两个新的子代染色体。交叉概率crossover_prob控制交叉操作的频率(本文设为 0.8,即 80% 的亲本会进行交叉)。

交叉函数实现


function [child1, child2] = crossover(parent1, parent2, crossover_prob)
    if rand < crossover_prob  % 满足交叉概率,进行交叉
        % 随机选择交叉点(不能是染色体的两端,否则等同于不交叉)
        cross_point = randi([1, length(parent1)-1]);
        % 交换交叉点后的基因
        child1 = [parent1(1:cross_point), parent2(cross_point+1:end)];
        child2 = [parent2(1:cross_point), parent1(cross_point+1:end)];
    else  % 不满足交叉概率,直接保留亲本
        child1 = parent1;
        child2 = parent2;
    end
end

在主迭代中,我们按 “两两配对” 的方式对选择后的种群进行交叉:


% 初始化新种群
new_population = zeros(pop_size, chrom_length);

% 两两配对进行交叉(i步长为2)
for i = 1:2:pop_size-1
    [child1, child2] = crossover(selected_population(i, :), selected_population(i+1, :), crossover_prob);
    new_population(i, :) = child1;
    new_population(i+1, :) = child2;
end
(4)变异操作:“基因突变” 增加种群多样性

变异操作是遗传算法避免陷入局部最优的关键,它随机翻转染色体的某个二进制位(0 变 1 或 1 变 0),为种群引入新的基因,增加搜索的广度。变异概率mutation_prob通常设置得很小(本文设为 0.01),避免过度变异破坏优质基因。

变异函数实现


function mutated_individual = mutate(individual, mutation_prob)
    for i = 1:length(individual)
        if rand < mutation_prob  % 满足变异概率,翻转当前位
            individual(i) = 1 - individual(i);
        end
    end
    mutated_individual = individual;
end

在主迭代中,对交叉后的新种群逐个进行变异:


% 对新种群中的每个个体进行变异
for i = 1:pop_size
    new_population(i, :) = mutate(new_population(i, :), mutation_prob);
end

% 更新种群:将变异后的种群作为下一代的起始种群
population = new_population;

4. 第四步:结果输出与可视化 —— 验证算法效果

迭代结束后,我们需要从所有代的最优适应度中找到全局最优值,并解码出对应的x值,同时绘制 “适应度进化曲线”,直观展示算法的优化过程。

(1)输出最优解

% 找到全局最优适应度及对应的代次
[global_best_fitness, best_gen] = max(best_fitness_values);
% 找到最优代对应的染色体(最优个体)
global_best_individual = population(best_gen, :);
% 解码最优个体为x值
x_dec = bin2dec(num2str(global_best_individual));
global_best_x = x_min + x_dec * (x_max - x_min) / (2^chrom_length - 1);

% 输出结果
disp('==================== 优化结果 ====================');
disp(['最优x值:', num2str(global_best_x)]);
disp(['最优函数值(适应度):', num2str(global_best_fitness)]);
disp(['找到最优解的代次:', num2str(best_gen)]);
(2)绘制适应度进化曲线

figure('Position', [100, 100, 800, 500]);  % 设置图像大小
plot(1:max_generations, best_fitness_values, 'b-', 'LineWidth', 1.5);
xlabel('进化代数', 'FontSize', 12);
ylabel('最优适应度(函数值)', 'FontSize', 12);
title('遗传算法适应度进化曲线', 'FontSize', 14, 'FontWeight', 'bold');
grid on;  % 显示网格

进化曲线的典型特征是:前期适应度快速上升(算法快速找到较优解),后期上升逐渐平缓(种群趋于稳定,接近全局最优)。

四、算法运行结果与分析

我们运行上述代码后,得到的典型结果如下(因算法存在随机性,每次结果略有差异):

  • 最优 x 值:约 29.8~30.0(接近区间上限,因为目标函数的线性项3x起主导作用)
  • 最优函数值:约 95~97
  • 找到最优解的代次:约 50~80 代

从进化曲线可以观察到:

  1. 前 20 代:适应度快速上升,说明算法快速淘汰了差的解,保留了优质解;
  2. 20~80 代:适应度缓慢上升,种群在局部最优附近微调,逐渐逼近全局最优;
  3. 80 代后:适应度基本稳定,说明种群已收敛到全局最优解。

五、参数调优建议:让算法更高效

遗传算法的参数设置对优化结果影响很大,若运行效果不佳,可尝试以下调优方向:

  1. 种群大小(pop_size):若结果波动大,可增大种群(如 150200),增加多样性;若计算太慢,可减小种群(如 5080)。
  2. 染色体长度(chrom_length):若需要更高的x精度,可增加长度(如 12 位,精度约 0.0024);若精度足够,可减小长度(如 8 位,精度约 0.039)。
  3. 交叉概率(crossover_prob):建议在 0.7~0.9 之间调整,过小会导致种群更新慢,过大可能破坏优质基因。
  4. 变异概率(mutation_prob):建议在 0.001~0.05 之间调整,过小容易陷入局部最优,过大导致种群不稳定。

六、总结

本文通过 MATLAB 实现了遗传算法求解一元复杂函数的最大值,从原理到代码逐步拆解,清晰展示了遗传算法 “选择 - 交叉 - 变异” 的核心逻辑。相比传统优化方法,遗传算法无需依赖函数的连续性和可导性,只需通过 “适应度” 评估解的优劣,就能在复杂解空间中高效搜索全局最优解。

除了一元函数,遗传算法还可扩展到多元函数优化、组合优化(如旅行商问题)、机器学习参数调优等领域。只要掌握了 “编码 - 适应度 - 进化操作” 的核心框架,就能将其应用到更多实际问题中。