前言
提醒:
文章内容为方便作者自己后日复习与查阅而进行的书写与发布,其中引用内容都会使用链接表明出处(如有侵权问题,请及时联系)。
其中内容多为一次书写,缺少检查与订正,如有问题或其他拓展及意见建议,欢迎评论区讨论交流。
内容由AI辅助生成,仅经笔者审核整理,请甄别食用。
文章目录
matlab代码
function PAES_ZDT1_Complete()
% PAES算法求解ZDT1问题 - 完整实现
% Pareto Archived Evolution Strategy for ZDT1 Problem
clc; clear; close all;
fprintf('=== PAES算法求解ZDT1问题 ===\n');
fprintf('开始优化...\n\n');
% ==================== 算法参数设置 ====================
params.max_iterations = 3*10000; % 最大迭代次数
params.archive_size = 100; % 档案最大容量
params.grid_divisions = 10; % 网格划分数
params.mutation_rate = 0.1; % 变异率
params.dimension = 30; % 决策变量维数
params.num_objectives = 2; % 目标函数数量
% ==================== 初始化 ====================
% 随机生成初始解
current_solution = rand(1, params.dimension); % ZDT1变量范围[0,1]
archive = current_solution;
% 计算初始解的目标函数值
current_obj = ZDT1_objective(current_solution);
archive_obj = current_obj;
% ==================== 主优化循环 ====================
tic;
for iter = 1:params.max_iterations
% 1. 变异产生候选解
candidate = mutate(current_solution, params.mutation_rate);
candidate_obj = ZDT1_objective(candidate);
% 2. 评估候选解
[accept_candidate, new_current, archive, archive_obj] = ...
evaluate_candidate(current_solution, current_obj, ...
candidate, candidate_obj, ...
archive, archive_obj, params);
% 3. 更新当前解
if accept_candidate
current_solution = new_current;
current_obj = ZDT1_objective(current_solution);
end
% 4. 维护档案大小
if size(archive, 1) > params.archive_size
[archive, archive_obj] = maintain_archive(archive, archive_obj, params);
end
% 5. 显示进度
if mod(iter, 2000) == 0
fprintf('迭代 %d/%d: 档案大小 = %d\n', iter, params.max_iterations, size(archive, 1));
end
end
elapsed_time = toc;
% ==================== 结果输出和可视化 ====================
fprintf('\n=== 优化完成 ===\n');
fprintf('总耗时: %.2f秒\n', elapsed_time);
fprintf('最终档案大小: %d\n', size(archive, 1));
fprintf('f1范围: [%.4f, %.4f]\n', min(archive_obj(:, 1)), max(archive_obj(:, 1)));
fprintf('f2范围: [%.4f, %.4f]\n', min(archive_obj(:, 2)), max(archive_obj(:, 2)));
% 绘制结果
plot_results(archive_obj);
% ==================== 嵌套函数定义 ====================
function objectives = ZDT1_objective(x)
% ZDT1测试函数
% f1(x) = x1
% f2(x) = g(x) * h(f1, g)
% g(x) = 1 + 9/(n-1) * sum(xi, i=2 to n)
% h(f1, g) = 1 - sqrt(f1/g)
n = length(x);
% 第一个目标函数
f1 = x(1);
% 辅助函数g
if n > 1
g = 1 + 9 * sum(x(2:end)) / (n - 1);
else
g = 1;
end
% 第二个目标函数
h = 1 - sqrt(f1 / g);
f2 = g * h;
objectives = [f1, f2];
end
function mutated_solution = mutate(solution, mutation_rate)
% 高斯变异操作
mutated_solution = solution + mutation_rate * randn(size(solution));
% 边界处理 - 确保变量在[0,1]范围内
mutated_solution = max(0, min(1, mutated_solution));
end
function result = dominates(obj1, obj2)
% 判断obj1是否支配obj2 (最小化问题)
% 支配条件:obj1在所有目标上不劣于obj2,且至少在一个目标上严格优于obj2
all_better_or_equal = all(obj1 <= obj2);
at_least_one_better = any(obj1 < obj2);
result = all_better_or_equal && at_least_one_better;
end
function grid_coords = calculate_grid_coordinates(objectives, archive_obj, grid_divisions)
% 计算解在自适应网格中的坐标
if size(archive_obj, 1) == 1
grid_coords = zeros(1, length(objectives));
return;
end
% 计算目标空间的边界
min_obj = min(archive_obj, [], 1);
max_obj = max(archive_obj, [], 1);
% 避免除零错误
range_obj = max_obj - min_obj;
range_obj(range_obj == 0) = 1;
% 计算归一化坐标
normalized = (objectives - min_obj) ./ range_obj;
% 计算网格坐标
grid_coords = floor(normalized * grid_divisions);
% 确保坐标在有效范围内
grid_coords = max(0, min(grid_divisions - 1, grid_coords));
end
function crowding = calculate_crowding(grid_coords, archive_obj, grid_divisions)
% 计算指定网格的拥挤度(该网格中解的数量)
if size(archive_obj, 1) <= 1
crowding = 1;
return;
end
% 计算档案中所有解的网格坐标
crowding = 0;
for i = 1:size(archive_obj, 1)
current_grid = calculate_grid_coordinates(archive_obj(i, :), archive_obj, grid_divisions);
if isequal(current_grid, grid_coords)
crowding = crowding + 1;
end
end
end
function [accept, new_current, new_archive, new_archive_obj] = ...
evaluate_candidate(current, current_obj, candidate, candidate_obj, archive, archive_obj, params)
% 根据PAES规则评估候选解是否被接受
accept = false;
new_current = current;
new_archive = archive;
new_archive_obj = archive_obj;
% 情况1: 候选解支配当前解
if dominates(candidate_obj, current_obj)
accept = true;
new_current = candidate;
% 将候选解添加到档案
new_archive = [new_archive; candidate];
new_archive_obj = [new_archive_obj; candidate_obj];
return;
end
% 情况2: 当前解支配候选解
if dominates(current_obj, candidate_obj)
return; % 拒绝候选解
end
% 情况3: 两解互不支配,需要进一步判断
% 3.1 检查候选解是否被档案中的解支配
for i = 1:size(archive_obj, 1)
if dominates(archive_obj(i, :), candidate_obj)
return; % 被档案中的解支配,拒绝
end
end
% 3.2 检查候选解是否支配档案中的解
dominated_indices = [];
for i = 1:size(archive_obj, 1)
if dominates(candidate_obj, archive_obj(i, :))
dominated_indices = [dominated_indices, i];
end
end
% 如果候选解支配档案中的某些解
if ~isempty(dominated_indices)
accept = true;
new_current = candidate;
% 从档案中移除被支配的解
keep_indices = setdiff(1:size(archive, 1), dominated_indices);
new_archive = new_archive(keep_indices, :);
new_archive_obj = new_archive_obj(keep_indices, :);
% 添加候选解到档案
new_archive = [new_archive; candidate];
new_archive_obj = [new_archive_obj; candidate_obj];
return;
end
% 3.3 候选解与档案中所有解互不支配
if size(archive, 1) < params.archive_size
% 档案未满,直接接受
accept = true;
new_current = candidate;
new_archive = [new_archive; candidate];
new_archive_obj = [new_archive_obj; candidate_obj];
else
% 档案已满,根据拥挤度判断
candidate_grid = calculate_grid_coordinates(candidate_obj, [archive_obj; candidate_obj], params.grid_divisions);
current_grid = calculate_grid_coordinates(current_obj, [archive_obj; candidate_obj], params.grid_divisions);
candidate_crowding = calculate_crowding(candidate_grid, [archive_obj; candidate_obj], params.grid_divisions);
current_crowding = calculate_crowding(current_grid, [archive_obj; candidate_obj], params.grid_divisions);
% 如果候选解所在网格的拥挤度更小,则接受
if candidate_crowding < current_crowding
accept = true;
new_current = candidate;
new_archive = [new_archive; candidate];
new_archive_obj = [new_archive_obj; candidate_obj];
end
end
end
function [new_archive, new_archive_obj] = maintain_archive(archive, archive_obj, params)
% 当档案超过容量限制时,移除拥挤度最高的解
while size(archive, 1) > params.archive_size
% 计算所有解的拥挤度
crowding_values = zeros(size(archive, 1), 1);
for i = 1:size(archive, 1)
grid_coords = calculate_grid_coordinates(archive_obj(i, :), archive_obj, params.grid_divisions);
crowding_values(i) = calculate_crowding(grid_coords, archive_obj, params.grid_divisions);
end
% 找到拥挤度最高的解的索引
[~, max_crowding_indices] = max(crowding_values);
% 如果有多个解具有相同的最高拥挤度,随机选择一个
if length(max_crowding_indices) > 1
remove_idx = max_crowding_indices(randi(length(max_crowding_indices)));
else
remove_idx = max_crowding_indices(1);
end
% 移除选中的解
archive(remove_idx, :) = [];
archive_obj(remove_idx, :) = [];
end
new_archive = archive;
new_archive_obj = archive_obj;
end
function plot_results(archive_obj)
% 绘制优化结果对比图
figure('Position', [100, 100, 800, 600]);
% 绘制PAES找到的解
scatter(archive_obj(:, 1), archive_obj(:, 2), 60, 'ro', 'filled', 'MarkerEdgeColor', 'k');
hold on;
% 绘制ZDT1的真实Pareto前沿
f1_true = linspace(0, 1, 1000);
f2_true = 1 - sqrt(f1_true);
plot(f1_true, f2_true, 'b-', 'LineWidth', 2.5);
% 图形美化
xlabel('f_1', 'FontSize', 14, 'FontWeight', 'bold');
ylabel('f_2', 'FontSize', 14, 'FontWeight', 'bold');
title('PAES算法求解ZDT1问题结果对比', 'FontSize', 16, 'FontWeight', 'bold');
legend({'PAES找到的解', '真实Pareto前沿'}, 'FontSize', 12, 'Location', 'northeast');
grid on;
grid minor;
% 设置坐标轴
xlim([0, 1]);
ylim([0, 1]);
% 添加文本信息
text(0.05, 0.95, sprintf('解的数量: %d', size(archive_obj, 1)), ...
'FontSize', 12, 'BackgroundColor', 'white', 'EdgeColor', 'black');
% 计算并显示一些性能指标
fprintf('\n=== 性能分析 ===\n');
% 计算分布均匀性(相邻解之间的平均距离)
if size(archive_obj, 1) > 1
[~, sort_idx] = sort(archive_obj(:, 1));
sorted_obj = archive_obj(sort_idx, :);
distances = sqrt(sum(diff(sorted_obj).^2, 2));
avg_distance = mean(distances);
std_distance = std(distances);
fprintf('解的平均间距: %.4f\n', avg_distance);
fprintf('间距标准差: %.4f\n', std_distance);
end
% 计算收敛性(与真实前沿的平均距离)
min_distances = zeros(size(archive_obj, 1), 1);
for i = 1:size(archive_obj, 1)
f1_current = archive_obj(i, 1);
f2_true_at_f1 = 1 - sqrt(f1_current);
min_distances(i) = abs(archive_obj(i, 2) - f2_true_at_f1);
end
avg_convergence = mean(min_distances);
fprintf('与真实前沿的平均距离: %.6f\n', avg_convergence);
fprintf('\n算法运行完成!\n');
end
end
运行结果
代码简析
以下结合数学公式和代码逻辑,对PAES求解ZDT1问题的完整实现进行拆解讲解,帮助理解算法如何通过“变异-评估-存档维护”流程逼近帕累托前沿。
一、核心数学公式与算法逻辑
PAES(Pareto Archived Evolution Strategy)的核心是通过高斯变异探索解空间,用存档(archive)维护非支配解,并基于“支配关系”和“网格拥挤度”筛选解,最终逼近ZDT1的真实帕累托前沿 f 2 = 1 − f 1 f_2 = 1 - \sqrt{f_1} f2=1−f1。
二、代码模块与公式对应解析
1. ZDT1目标函数(数学定义与实现)
ZDT1的两个目标函数:
f 1 ( x ) = x 1 (第一个目标,直接取第一个决策变量) g ( x ) = 1 + 9 ⋅ ∑ i = 2 30 x i 29 (辅助函数,平衡多变量影响) f 2 ( x ) = g ( x ) ⋅ ( 1 − f 1 ( x ) g ( x ) ) (第二个目标,与 f 1 和 g 相关) \begin{align*} f_1(x) &= x_1 \quad \text{(第一个目标,直接取第一个决策变量)} \\ g(x) &= 1 + 9 \cdot \frac{\sum_{i=2}^{30} x_i}{29} \quad \text{(辅助函数,平衡多变量影响)} \\ f_2(x) &= g(x) \cdot \left(1 - \sqrt{\frac{f_1(x)}{g(x)}}\right) \quad \text{(第二个目标,与} f_1 \text{和} g \text{相关)} \end{align*} f1(x)g(x)f2(x)=x1(第一个目标,直接取第一个决策变量)=1+9⋅29∑i=230xi(辅助函数,平衡多变量影响)=g(x)⋅(1−g(x)f1(x))(第二个目标,与f1和g相关)
代码实现(嵌套函数 ZDT1_objective
):
function objectives = ZDT1_objective(x)
f1 = x(1); % 直接取第一个变量
g = 1 + 9 * sum(x(2:end))/(length(x)-1); % 计算g(x)
f2 = g * (1 - sqrt(f1/g)); % 计算第二个目标
objectives = [f1, f2]; % 输出目标向量
end
2. 高斯变异(探索新解的数学逻辑)
PAES通过高斯变异生成候选解,公式:
x i ′ = x i + mutation_rate ⋅ N ( 0 , 1 ) x_i' = x_i + \text{mutation\_rate} \cdot \mathcal{N}(0, 1) xi′=xi+mutation_rate⋅N(0,1)
其中 N ( 0 , 1 ) \mathcal{N}(0, 1) N(0,1)是标准正态分布随机数,mutation_rate
控制变异幅度(代码中设为 0.1
)。
代码实现(嵌套函数 mutate
):
function mutated_solution = mutate(solution, mutation_rate)
% 高斯变异:给原解加正态分布噪声
mutated_solution = solution + mutation_rate * randn(size(solution));
mutated_solution = max(0, min(1, mutated_solution)); % 边界截断(保证在[0,1])
end
3. 支配关系判断(多目标优化的核心逻辑)
若解 A A A在所有目标上不差于解 B B B,且至少一个目标严格优于 B B B,则称 A A A支配 B B B,公式:
dominates ( A , B ) = ( ∀ i , A i ≤ B i ) ∧ ( ∃ j , A j < B j ) \text{dominates}(A, B) = \left( \forall i, A_i \leq B_i \right) \land \left( \exists j, A_j < B_j \right) dominates(A,B)=(∀i,Ai≤Bi)∧(∃j,Aj<Bj)
代码实现(嵌套函数 dominates
):
function result = dominates(obj1, obj2)
all_better_or_equal = all(obj1 <= obj2); % 所有目标不差
at_least_one_better = any(obj1 < obj2); % 至少一个目标更优
result = all_better_or_equal && at_least_one_better; % 同时满足则支配
end
4. 网格拥挤度(维护解分布性的工具)
为避免解过度集中,PAES用网格划分量化拥挤度:
- 计算目标空间边界: min _ o b j = min ( archive_obj , [ ] , 1 ) \min\_obj = \min(\text{archive\_obj}, [], 1) min_obj=min(archive_obj,[],1), max _ o b j = max ( archive_obj , [ ] , 1 ) \max\_obj = \max(\text{archive\_obj}, [], 1) max_obj=max(archive_obj,[],1)
- 归一化目标值: normalized = objectives − min _ o b j max _ o b j − min _ o b j \text{normalized} = \frac{\text{objectives} - \min\_obj}{\max\_obj - \min\_obj} normalized=max_obj−min_objobjectives−min_obj(避免除零,若范围为0则设为1)
- 计算网格坐标: grid_coords = ⌊ normalized ⋅ grid_divisions ⌋ \text{grid\_coords} = \lfloor \text{normalized} \cdot \text{grid\_divisions} \rfloor grid_coords=⌊normalized⋅grid_divisions⌋
- 拥挤度定义:同一网格内的解数量,数量越多则拥挤度越高。
代码实现(嵌套函数 calculate_crowding
):
function crowding = calculate_crowding(grid_coords, archive_obj, grid_divisions)
crowding = 0;
for i = 1:size(archive_obj, 1)
current_grid = calculate_grid_coordinates(archive_obj(i, :), archive_obj, grid_divisions);
if isequal(current_grid, grid_coords)
crowding = crowding + 1; % 统计同网格解数量
end
end
end
5. 存档维护(保留优质解的逻辑)
- 添加解:若候选解不被支配,且存档未满则直接加入;若存档已满,比较“候选解所在网格”与“当前解所在网格”的拥挤度,拥挤度高的网格移除解。
- 移除解:当存档超过容量(
archive_size
),持续移除“拥挤度最高网格”中的解,直到容量达标。
代码实现(嵌套函数 maintain_archive
):
function [new_archive, new_archive_obj] = maintain_archive(archive, archive_obj, params)
while size(archive, 1) > params.archive_size
% 计算所有解的拥挤度
crowding_values = zeros(size(archive, 1), 1);
for i = 1:size(archive, 1)
grid_coords = calculate_grid_coordinates(archive_obj(i, :), archive_obj, params.grid_divisions);
crowding_values(i) = calculate_crowding(grid_coords, archive_obj, params.grid_divisions);
end
% 移除拥挤度最高的解(随机选一个拥挤度最高的)
[~, max_crowding_indices] = max(crowding_values);
remove_idx = max_crowding_indices(randi(length(max_crowding_indices)));
archive(remove_idx, :) = []; % 移除解
archive_obj(remove_idx, :) = []; % 移除对应目标值
end
new_archive = archive;
new_archive_obj = archive_obj;
end
6. 主循环(算法流程的串联)
PAES通过“变异→评估→更新→维护存档”的循环迭代优化:
for iter = 1:params.max_iterations
% 1. 变异产生候选解
candidate = mutate(current_solution, params.mutation_rate);
candidate_obj = ZDT1_objective(candidate);
% 2. 评估候选解(支配关系+存档规则)
[accept_candidate, new_current, archive, archive_obj] = evaluate_candidate(...);
% 3. 更新当前解
if accept_candidate
current_solution = new_current;
current_obj = ZDT1_objective(current_solution);
end
% 4. 维护存档大小
if size(archive, 1) > params.archive_size
[archive, archive_obj] = maintain_archive(archive, archive_obj, params);
end
end
三、算法流程总结
PAES通过以下步骤求解ZDT1:
- 初始化:随机生成初始解,计算目标值并初始化存档。
- 变异:用高斯变异生成候选解,探索新的解空间。
- 评估:基于支配关系判断是否接受候选解,若接受则更新当前解和存档。
- 维护存档:通过网格拥挤度移除冗余解,保证存档解分布均匀。
- 循环迭代:重复“变异-评估-维护”,直到达到最大迭代次数。
四、关键可视化与指标
- 真实帕累托前沿:
f2_true = 1 - sqrt(f1_true)
,代码中用蓝色线绘制。 - 算法找到的解:存档中的非支配解,用红色点绘制,分布越接近蓝色线且均匀,说明算法性能越好。
- 性能指标:
- 平均间距:排序后相邻解的平均欧氏距离,反映分布均匀性。
- 与真实前沿的平均距离:解到 f 2 = 1 − f 1 f_2 = 1 - \sqrt{f_1} f2=1−f1的垂直距离均值,反映收敛性。
该代码完整实现了PAES的核心逻辑,通过数学公式(如支配关系、高斯变异)与工程实现(如网格拥挤度、存档维护)的结合,有效求解ZDT1问题并逼近真实帕累托前沿。