基于 SPEA2 的多目标优化算法 MATLAB 实现

一、算法核心架构

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    SPEA2 多目标优化算法                   │
├─────────────────────────────────────────────────────────────┤
│  初始化阶段   │  进化循环阶段   │  环境选择阶段   │  输出阶段   │
│               │                 │                 │             │
│  • 生成初始   │  • 适应度计算   │  • 归档集更新   │  • 帕累托   │
│    种群      │  • 繁殖选择     │  • 截断操作     │    前沿     │
│  • 建立归档集 │  • 交叉变异     │  • 多样性保持   │  • 最优解集 │
│  • 定义目标函数│  • 精英保留    │  • 边界处理     │  • 收敛曲线 │
└─────────────────────────────────────────────────────────────┘

二、完整 MATLAB 实现

2.1 主程序 (spea2_main.m)

matlab 复制代码
%% SPEA2 多目标优化算法主程序
% 功能:基于SPEA2的多目标优化,支持任意维度的目标函数和决策变量
% 算法:Strength Pareto Evolutionary Algorithm 2

clear all; close all; clc;

%% 1. 算法参数设置
params = struct();
params.pop_size = 100;           % 种群大小
params.archive_size = 100;       % 归档集大小
params.max_generations = 200;     % 最大进化代数
params.num_variables = 30;        % 决策变量维度
params.num_objectives = 2;        % 目标函数维度
params.var_min = zeros(1, params.num_variables);  % 变量下界
params.var_max = ones(1, params.num_variables);   % 变量上界
params.crossover_prob = 0.9;      % 交叉概率
params.mutation_prob = 0.1;       % 变异概率
params.mutation_rate = 0.05;      % 变异强度
params.k_neighbors = 5;           % k近邻参数(用于密度估计)

fprintf('=== SPEA2 多目标优化算法 ===\n');
fprintf('参数设置:\n');
fprintf('  种群大小: %d\n', params.pop_size);
fprintf('  归档集大小: %d\n', params.archive_size);
fprintf('  最大代数: %d\n', params.max_generations);
fprintf('  决策变量维度: %d\n', params.num_variables);
fprintf('  目标函数维度: %d\n\n', params.num_objectives);

%% 2. 初始化种群和归档集
fprintf('初始化种群...\n');
[population, archive] = spea2_initialize(params);

%% 3. 进化循环
fprintf('开始进化迭代...\n');
convergence_curve = zeros(params.max_generations, 1);

for gen = 1:params.max_generations
    tic;
    
    %% 3.1 计算适应度(包含支配强度、原始适应度、密度估计)
    [population, archive] = spea2_fitness_assignment(population, archive, params);
    
    %% 3.2 环境选择(更新归档集)
    archive = spea2_environmental_selection(population, archive, params);
    
    %% 3.3 繁殖选择(生成下一代种群)
    population = spea2_reproduction(population, archive, params);
    
    %% 3.4 记录收敛信息
    convergence_curve(gen) = mean([archive.fitness]);
    
    %% 3.5 显示进度
    if mod(gen, 20) == 0 || gen == 1
        non_dominated_count = sum([archive.raw_fitness] == 0);
        fprintf('  第 %d 代: 归档集大小=%d, 非支配解=%d, 平均适应度=%.4f, 耗时=%.2fs\n', ...
                gen, length(archive), non_dominated_count, convergence_curve(gen), toc);
    end
    
    %% 3.6 检查收敛条件
    if gen > 50 && std(convergence_curve(gen-49:gen)) < 1e-6
        fprintf('算法在第 %d 代收敛\n', gen);
        break;
    end
end

%% 4. 输出最终结果
fprintf('\n=== 优化完成 ===\n');
final_non_dominated = archive([archive.raw_fitness] == 0);
fprintf('最终非支配解数量: %d\n', length(final_non_dominated));

%% 5. 可视化结果
spea2_visualization(final_non_dominated, convergence_curve, params);

%% 6. 保存结果
save('spea2_results.mat', 'final_non_dominated', 'convergence_curve', 'params');
fprintf('结果已保存到: spea2_results.mat\n');

2.2 初始化模块 (spea2_initialize.m)

matlab 复制代码
function [population, archive] = spea2_initialize(params)
    % SPEA2 初始化函数
    % 输入:params - 算法参数结构体
    % 输出:population - 初始种群,archive - 初始归档集
    
    num_vars = params.num_variables;
    pop_size = params.pop_size;
    
    % 1. 随机生成初始种群
    population = struct();
    for i = 1:pop_size
        % 随机生成决策变量
        vars = params.var_min + rand(1, num_vars) .* (params.var_max - params.var_min);
        
        % 计算目标函数值
        objectives = evaluate_objectives(vars, params.num_objectives);
        
        % 存储个体信息
        population(i).variables = vars;
        population(i).objectives = objectives;
        population(i).strength = 0;      % 支配强度
        population(i).raw_fitness = 0;  % 原始适应度
        population(i).density = 0;       % 密度估计
        population(i).fitness = 0;      % 最终适应度
    end
    
    % 2. 初始化空归档集
    archive = struct();
    fprintf('种群初始化完成,共 %d 个个体\n', length(population));
end

2.3 适应度分配模块 (spea2_fitness_assignment.m)

matlab 复制代码
function [population, archive] = spea2_fitness_assignment(population, archive, params)
    % SPEA2 适应度分配函数
    % 计算支配强度、原始适应度、密度估计和最终适应度
    
    % 合并种群和归档集
    combined = [population(:); archive(:)];
    num_combined = length(combined);
    
    %% 1. 计算支配强度 S(i)
    for i = 1:num_combined
        strength = 0;
        for j = 1:num_combined
            if i ~= j
                if dominates(combined(i).objectives, combined(j).objectives)
                    strength = strength + 1;
                end
            end
        end
        combined(i).strength = strength;
    end
    
    %% 2. 计算原始适应度 R(i)(被支配程度)
    for i = 1:num_combined
        raw_fitness = 0;
        for j = 1:num_combined
            if i ~= j
                if dominates(combined(j).objectives, combined(i).objectives)
                    raw_fitness = raw_fitness + combined(j).strength;
                end
            end
        end
        combined(i).raw_fitness = raw_fitness;
    end
    
    %% 3. 密度估计 D(i)(基于k近邻)
    % 提取目标函数值矩阵
    obj_matrix = zeros(num_combined, params.num_objectives);
    for i = 1:num_combined
        obj_matrix(i, :) = combined(i).objectives;
    end
    
    % 标准化目标函数值
    obj_normalized = normalize_objectives(obj_matrix);
    
    % 计算k近邻距离
    for i = 1:num_combined
        distances = zeros(num_combined, 1);
        for j = 1:num_combined
            if i ~= j
                distances(j) = sqrt(sum((obj_normalized(i, :) - obj_normalized(j, :)).^2));
            else
                distances(j) = inf;
            end
        end
        
        % 找到第k个最近邻的距离
        sorted_distances = sort(distances);
        kth_distance = sorted_distances(params.k_neighbors);
        
        % 密度估计(距离越大,密度越小,适应度越好)
        if kth_distance > 0
            combined(i).density = 1 / (kth_distance + 2);  % +2避免除零
        else
            combined(i).density = 0;
        end
    end
    
    %% 4. 计算最终适应度 F(i) = R(i) + D(i)
    for i = 1:num_combined
        combined(i).fitness = combined(i).raw_fitness + combined(i).density;
    end
    
    % 分离种群和归档集
    population = combined(1:length(population));
    archive = combined(length(population)+1:end);
end

%% 支配关系判断函数
function is_dominant = dominates(obj1, obj2)
    % 判断obj1是否支配obj2
    % obj1支配obj2当且仅当:obj1的所有目标都不比obj2差,且至少有一个更好
    better_in_all = all(obj1 <= obj2);
    strictly_better_in_one = any(obj1 < obj2);
    is_dominant = better_in_all && strictly_better_in_one;
end

%% 目标函数标准化
function normalized = normalize_objectives(obj_matrix)
    % 标准化目标函数值到[0,1]区间
    [num_individuals, num_objectives] = size(obj_matrix);
    normalized = zeros(num_individuals, num_objectives);
    
    for i = 1:num_objectives
        obj_range = max(obj_matrix(:, i)) - min(obj_matrix(:, i));
        if obj_range > 0
            normalized(:, i) = (obj_matrix(:, i) - min(obj_matrix(:, i))) / obj_range;
        else
            normalized(:, i) = obj_matrix(:, i);
        end
    end
end

2.4 环境选择模块 (spea2_environmental_selection.m)

matlab 复制代码
function archive = spea2_environmental_selection(population, archive, params)
    % SPEA2 环境选择函数
    % 更新归档集,保持多样性
    
    % 合并种群和归档集
    combined = [population(:); archive(:)];
    
    % 筛选非支配解(raw_fitness = 0)
    non_dominated_indices = find([combined.raw_fitness] == 0);
    non_dominated = combined(non_dominated_indices);
    
    % 如果非支配解数量超过归档集大小,进行截断
    if length(non_dominated) > params.archive_size
        % 使用截断操作保持多样性
        archive = spea2_truncation(non_dominated, params.archive_size);
    else
        % 如果非支配解数量不足,用次优解补充
        archive = non_dominated;
        remaining_slots = params.archive_size - length(archive);
        
        if remaining_slots > 0
            % 按适应度排序选择剩余个体
            [~, sorted_indices] = sort([combined.fitness]);
            for i = 1:length(sorted_indices)
                candidate = combined(sorted_indices(i));
                if ~ismember(candidate, archive)
                    archive(end+1) = candidate;
                    remaining_slots = remaining_slots - 1;
                    if remaining_slots == 0
                        break;
                    end
                end
            end
        end
    end
end

%% 截断操作(保持多样性)
function truncated_set = spea2_truncation(set, target_size)
    % 截断操作:移除拥挤的个体,保持分布均匀
    
    current_size = length(set);
    truncated_set = set;
    
    while current_size > target_size
        % 计算所有个体间的距离矩阵
        distances = zeros(current_size, current_size);
        for i = 1:current_size
            for j = 1:current_size
                if i ~= j
                    distances(i, j) = sqrt(sum((set(i).objectives - set(j).objectives).^2));
                else
                    distances(i, j) = inf;
                end
            end
        end
        
        % 找到距离最近的一对个体
        min_distance = inf;
        remove_index = -1;
        
        for i = 1:current_size
            % 找到第k个最近邻
            sorted_dists = sort(distances(i, :));
            kth_distance = sorted_dists(2);  % 跳过自己
            
            if kth_distance < min_distance
                min_distance = kth_distance;
                remove_index = i;
            end
        end
        
        % 移除距离最近的个体
        truncated_set(remove_index) = [];
        current_size = current_size - 1;
    end
end

2.5 繁殖选择模块 (spea2_reproduction.m)

matlab 复制代码
function population = spea2_reproduction(population, archive, params)
    % SPEA2 繁殖选择函数
    % 使用二元锦标赛选择生成下一代种群
    
    new_population = struct();
    pop_size = params.pop_size;
    
    for i = 1:pop_size
        % 二元锦标赛选择
        parent1 = spea2_binary_tournament(archive);
        parent2 = spea2_binary_tournament(archive);
        
        % 交叉操作
        if rand() < params.crossover_prob
            [child1_vars, child2_vars] = spea2_crossover(parent1.variables, parent2.variables, params);
        else
            child1_vars = parent1.variables;
            child2_vars = parent2.variables;
        end
        
        % 变异操作
        if rand() < params.mutation_prob
            child1_vars = spea2_mutation(child1_vars, params);
        end
        if rand() < params.mutation_prob
            child2_vars = spea2_mutation(child2_vars, params);
        end
        
        % 边界处理
        child1_vars = bound_variables(child1_vars, params.var_min, params.var_max);
        child2_vars = bound_variables(child2_vars, params.var_min, params.var_max);
        
        % 计算子代目标函数
        child1_obj = evaluate_objectives(child1_vars, params.num_objectives);
        child2_obj = evaluate_objectives(child2_vars, params.num_objectives);
        
        % 存储子代
        new_population(i*2-1).variables = child1_vars;
        new_population(i*2-1).objectives = child1_obj;
        new_population(i*2).variables = child2_vars;
        new_population(i*2).objectives = child2_obj;
    end
    
    % 如果种群大小是奇数,移除最后一个
    if length(new_population) > pop_size
        new_population(end) = [];
    end
    
    population = new_population;
end

%% 二元锦标赛选择
function selected = spea2_binary_tournament(population)
    % 随机选择两个个体,选择适应度更好的一个
    indices = randperm(length(population), 2);
    candidate1 = population(indices(1));
    candidate2 = population(indices(2));
    
    if candidate1.fitness <= candidate2.fitness
        selected = candidate1;
    else
        selected = candidate2;
    end
end

%% 交叉操作(模拟二进制交叉 SBX)
function [child1, child2] = spea2_crossover(parent1, parent2, params)
    eta_c = 20;  % 交叉分布指数
    child1 = parent1;
    child2 = parent2;
    
    for i = 1:length(parent1)
        if rand() < 0.5
            % 计算beta
            beta = 1 + (2 / abs(parent1(i) - parent2(i)));
            alpha = 2 - beta^(-eta_c);
            
            if rand() <= 1/alpha
                beta_q = (rand() * alpha)^(1/(eta_c+1));
            else
                beta_q = (1/(alpha * (1 - rand())))^(1/(eta_c+1));
            end
            
            % 生成子代
            child1(i) = 0.5 * ((1 + beta_q) * parent1(i) + (1 - beta_q) * parent2(i));
            child2(i) = 0.5 * ((1 - beta_q) * parent1(i) + (1 + beta_q) * parent2(i));
        end
    end
end

%% 变异操作(多项式变异)
function mutated = spea2_mutation(variables, params)
    eta_m = 20;  % 变异分布指数
    mutated = variables;
    
    for i = 1:length(variables)
        if rand() < params.mutation_rate
            delta = min(variables(i) - params.var_min(i), params.var_max(i) - variables(i)) / ...
                    (params.var_max(i) - params.var_min(i));
            
            if rand() < 0.5
                delta_q = (2 * rand() + (1 - 2 * rand()) * (1 - delta)^(eta_m + 1))^(1/(eta_m + 1)) - 1;
            else
                delta_q = 1 - (2 * (1 - rand()) + 2 * rand() * delta^(eta_m + 1))^(1/(eta_m + 1));
            end
            
            mutated(i) = variables(i) + delta_q * (params.var_max(i) - params.var_min(i));
        end
    end
end

%% 边界处理
function bounded_vars = bound_variables(variables, var_min, var_max)
    bounded_vars = variables;
    for i = 1:length(variables)
        bounded_vars(i) = max(var_min(i), min(var_max(i), variables(i)));
    end
end

2.6 目标函数定义 (evaluate_objectives.m)

matlab 复制代码
function objectives = evaluate_objectives(variables, num_objectives)
    % 目标函数评估函数
    % 这里使用经典的ZDT测试函数作为示例
    % 可以根据实际问题修改此函数
    
    if num_objectives == 2
        % ZDT1 测试函数
        f1 = variables(1);
        g = 1 + 9 * mean(variables(2:end));
        h = 1 - sqrt(f1 / g);
        f2 = g * h;
        objectives = [f1, f2];
        
    elseif num_objectives == 3
        % DTLZ1 测试函数(简化版)
        f1 = variables(1);
        f2 = variables(2);
        f3 = 1 - variables(1) - variables(2);
        objectives = [f1, f2, f3];
        
    else
        % 通用多目标函数
        objectives = zeros(1, num_objectives);
        for i = 1:num_objectives
            objectives(i) = sum(variables.^i) / length(variables);
        end
    end
end

2.7 可视化模块 (spea2_visualization.m)

matlab 复制代码
function spea2_visualization(non_dominated, convergence_curve, params)
    % SPEA2 结果可视化
    
    figure('Position', [100, 100, 1200, 800]);
    
    % 1. 帕累托前沿
    subplot(2, 3, 1);
    if params.num_objectives == 2
        scatter([non_dominated.objectives], [non_dominated.objectives]);
        xlabel('Objective 1');
        ylabel('Objective 2');
        title('帕累托前沿 (2D)');
        grid on;
    elseif params.num_objectives == 3
        scatter3([non_dominated.objectives], ...
                 [non_dominated.objectives], ...
                 [non_dominated.objectives]);
        xlabel('Objective 1');
        ylabel('Objective 2');
        zlabel('Objective 3');
        title('帕累托前沿 (3D)');
        grid on;
    end
    
    % 2. 收敛曲线
    subplot(2, 3, 2);
    plot(convergence_curve, 'b-', 'LineWidth', 2);
    xlabel('Generation');
    ylabel('Average Fitness');
    title('收敛曲线');
    grid on;
    
    % 3. 目标函数值分布
    subplot(2, 3, 3);
    if params.num_objectives == 2
        obj_matrix = zeros(length(non_dominated), 2);
        for i = 1:length(non_dominated)
            obj_matrix(i, :) = non_dominated(i).objectives;
        end
        histogram(obj_matrix(:, 1), 20, 'FaceColor', 'r', 'EdgeColor', 'black');
        xlabel('Objective 1');
        ylabel('Frequency');
        title('目标函数1分布');
    end
    
    % 4. 决策变量分布
    subplot(2, 3, 4);
    var_matrix = zeros(length(non_dominated), min(5, params.num_variables));
    for i = 1:length(non_dominated)
        var_matrix(i, :) = non_dominated(i).variables(1:min(5, params.num_variables));
    end
    boxplot(var_matrix);
    xlabel('Decision Variables');
    ylabel('Variable Values');
    title('决策变量分布');
    
    % 5. 适应度分布
    subplot(2, 3, 5);
    fitness_values = [non_dominated.fitness];
    histogram(fitness_values, 20, 'FaceColor', 'g', 'EdgeColor', 'black');
    xlabel('Fitness Value');
    ylabel('Frequency');
    title('适应度分布');
    
    % 6. 算法性能统计
    subplot(2, 3, 6);
    axis off;
    stats_text = sprintf(['SPEA2 算法性能统计\n\n', ...
                          '非支配解数量: %d\n', ...
                          '目标函数维度: %d\n', ...
                          '决策变量维度: %d\n', ...
                          '收敛代数: %d\n', ...
                          '最终平均适应度: %.4f\n', ...
                          '最小适应度: %.4f\n', ...
                          '最大适应度: %.4f'],
                         length(non_dominated),
                         params.num_objectives,
                         params.num_variables,
                         length(convergence_curve),
                         mean([non_dominated.fitness]),
                         min([non_dominated.fitness]),
                         max([non_dominated.fitness]));
    text(0.1, 0.5, stats_text, 'FontSize', 12, 'FontWeight', 'bold');
    
    sgtitle('SPEA2 多目标优化结果可视化');
end

三、测试脚本 (test_spea2.m)

matlab 复制代码
%% SPEA2 算法测试脚本
clear all; close all; clc;

fprintf('=== SPEA2 多目标优化算法测试 ===\n\n');

%% 测试1:ZDT1 测试函数
fprintf('测试1:ZDT1 测试函数 (2目标,30变量)\n');
params1 = struct();
params1.pop_size = 100;
params1.archive_size = 100;
params1.max_generations = 150;
params1.num_variables = 30;
params1.num_objectives = 2;
params1.var_min = zeros(1, 30);
params1.var_max = ones(1, 30);
params1.crossover_prob = 0.9;
params1.mutation_prob = 0.1;
params1.mutation_rate = 0.05;
params1.k_neighbors = 5;

tic;
[population, archive] = spea2_initialize(params1);
for gen = 1:params1.max_generations
    [population, archive] = spea2_fitness_assignment(population, archive, params1);
    archive = spea2_environmental_selection(population, archive, params1);
    population = spea2_reproduction(population, archive, params1);
    
    if mod(gen, 30) == 0
        fprintf('  第 %d 代完成\n', gen);
    end
end
time1 = toc;
fprintf('ZDT1 测试完成,耗时: %.2f 秒\n\n', time1);

%% 测试2:不同参数的影响
fprintf('测试2:参数敏感性分析\n');
param_sets = {
    struct('pop_size', 50, 'archive_size', 50, 'mutation_rate', 0.01),
    struct('pop_size', 100, 'archive_size', 100, 'mutation_rate', 0.05),
    struct('pop_size', 200, 'archive_size', 200, 'mutation_rate', 0.1)
};

figure('Position', [100, 100, 1200, 400]);
for i = 1:length(param_sets)
    params2 = struct();
    params2.pop_size = param_sets{i}.pop_size;
    params2.archive_size = param_sets{i}.archive_size;
    params2.max_generations = 100;
    params2.num_variables = 20;
    params2.num_objectives = 2;
    params2.var_min = zeros(1, 20);
    params2.var_max = ones(1, 20);
    params2.crossover_prob = 0.9;
    params2.mutation_prob = 0.1;
    params2.mutation_rate = param_sets{i}.mutation_rate;
    params2.k_neighbors = 5;
    
    [population, archive] = spea2_initialize(params2);
    convergence = zeros(params2.max_generations, 1);
    
    for gen = 1:params2.max_generations
        [population, archive] = spea2_fitness_assignment(population, archive, params2);
        archive = spea2_environmental_selection(population, archive, params2);
        population = spea2_reproduction(population, archive, params2);
        convergence(gen) = mean([archive.fitness]);
    end
    
    subplot(1, 3, i);
    plot(convergence, 'LineWidth', 2);
    xlabel('Generation');
    ylabel('Average Fitness');
    title(sprintf('Pop=%d, Mut=%g', param_sets{i}.pop_size, param_sets{i}.mutation_rate));
    grid on;
end

fprintf('所有测试完成!\n');

参考代码 基于SPEAII的多目标优化算法 www.youwenfan.com/contentcsu/63221.html

四、算法特点与应用

4.1 算法优势

特性 优势
精英保留 通过归档集确保优秀解不会丢失
多样性保持 k近邻密度估计防止过早收敛
收敛速度快 强度帕累托机制加速收敛
参数鲁棒性 对参数设置不敏感

4.2 适用场景

  1. 工程设计优化:多目标权衡设计
  2. 机器学习:多目标特征选择、模型压缩
  3. 能源系统:微电网容量配置、调度优化
  4. 路径规划:多目标路径优化

4.3 参数调优建议

参数 建议值 说明
pop_size 50-200 种群越大,搜索越充分
archive_size 50-200 通常与种群大小相当
mutation_rate 0.01-0.1 根据问题复杂度调整
k_neighbors 3-10 影响多样性保持强度
相关推荐
沪漂阿龙1 小时前
AI大模型面试题:支持向量机是什么?间隔最大化、软间隔、核函数、LinearSVC 全面拆解
人工智能·算法·支持向量机
赏金术士1 小时前
Kotlin 习题集 · 高级篇
android·开发语言·kotlin
little~钰1 小时前
倍增算法和ST表
算法
楼兰公子2 小时前
buildroot 在编译rust时裁剪平台类型数量的方法
开发语言·后端·rust
知识领航员2 小时前
蘑兔AI音乐深度实测:功能拆解、实测表现与适用场景
java·c语言·c++·人工智能·python·算法·github
薛定e的猫咪2 小时前
因果推理研究方向综述笔记
人工智能·笔记·深度学习·算法
吴声子夜歌2 小时前
Go——并发编程
开发语言·后端·golang
ooseabiscuit3 小时前
Laravel4.x:现代PHP框架的奠基之作
java·开发语言·php