一、NSGA-II算法核心原理
NSGA-II(Non-dominated Sorting Genetic Algorithm II)是多目标优化 的经典算法,通过非支配排序 区分解的优劣,拥挤度计算 保持种群多样性,精英保留策略提升收敛性,有效解决多目标冲突问题(如"成本-效率""重量-强度"权衡)。
1.1 核心概念
-
多目标优化问题 :
minF(x)=[f1(x),f2(x),...,fm(x)]T\min \quad \mathbf{F}(\mathbf{x}) = [f_1(\mathbf{x}), f_2(\mathbf{x}), ..., f_m(\mathbf{x})]^TminF(x)=[f1(x),f2(x),...,fm(x)]T
s.t.gi(x)≤0,hj(x)=0\text{s.t.} \quad g_i(\mathbf{x}) \leq 0, \quad h_j(\mathbf{x}) = 0s.t.gi(x)≤0,hj(x)=0其中 x=[x1,x2,...,xn]T\mathbf{x} = [x_1, x_2, ..., x_n]^Tx=[x1,x2,...,xn]T 为决策变量,mmm 为目标函数个数。
-
Pareto支配 :解 x1\mathbf{x}_1x1 支配 x2\mathbf{x}_2x2(记为 x1≺x2\mathbf{x}_1 \prec \mathbf{x}_2x1≺x2),当且仅当:
∀i,fi(x1)≤fi(x2)\forall i, f_i(\mathbf{x}_1) \leq f_i(\mathbf{x}_2)∀i,fi(x1)≤fi(x2) 且 ∃j,fj(x1)<fj(x2)\exists j, f_j(\mathbf{x}_1) < f_j(\mathbf{x}_2)∃j,fj(x1)<fj(x2)。 -
Pareto前沿:所有非支配解构成的集合,代表多目标优化的"最优权衡解"。
1.2 算法流程
满足终止条件
否则
初始化种群
非支配排序
计算拥挤度
选择操作(二元锦标赛)
交叉(SBX)与变异(多项式变异)
合并父代与子代种群
非支配排序+拥挤度计算
精英保留(选择N个个体)
输出Pareto前沿
二、NSGA-II核心步骤与MATLAB实现
2.1 主函数框架
matlab
function [pop, F, front, crowding] = nsga2(fun, nVar, varMin, varMax, nPop, maxGen, params)
% NSGA-II多目标优化主函数
% 输入:
% fun: 目标函数句柄(返回行向量 [f1, f2, ..., fm])
% nVar: 变量个数
% varMin/varMax: 变量边界(标量或向量)
% nPop: 种群大小
% maxGen: 最大迭代次数
% params: 算法参数(交叉概率pc、变异概率pm等)
% 输出:
% pop: 最终种群(nPop×nVar)
% F: 目标函数值(nPop×m)
% front: 非支配前沿等级(nPop×1)
% crowding: 拥挤度(nPop×1)
% 参数初始化
if nargin < 7, params = struct(); end
params = set_default_params(params, nVar);
% 1. 初始化种群
pop = init_population(nPop, nVar, varMin, varMax);
% 2. 主循环
for gen = 1:maxGen
% 2.1 评估目标函数
F = evaluate_population(fun, pop);
% 2.2 非支配排序
[front, rank] = non_domination_sort(F);
% 2.3 计算拥挤度
crowding = crowding_distance(F, front);
% 2.4 选择父代(二元锦标赛)
parents = tournament_selection(pop, front, crowding, nPop);
% 2.5 交叉与变异
offspring = genetic_operators(parents, params, varMin, varMax);
% 2.6 合并父代与子代
combined_pop = [pop; offspring];
combined_F = [F; evaluate_population(fun, offspring)];
% 2.7 环境选择(精英保留)
[pop, F, front, crowding] = environmental_selection(combined_pop, combined_F, nPop);
% 显示进度
if mod(gen, 10) == 0
fprintf('NSGA-II: Gen %d/%d, Pareto Size: %d\n', gen, maxGen, sum(front==1));
end
end
end
2.2 核心子函数实现
2.2.1 参数设置(默认值)
matlab
function params = set_default_params(params, nVar)
if ~isfield(params, 'pc'), params.pc = 0.9; end % 交叉概率
if ~isfield(params, 'pm'), params.pm = 1/nVar; end % 变异概率
if ~isfield(params, 'eta_c'), params.eta_c = 15; end % 交叉分布指数
if ~isfield(params, 'eta_m'), params.eta_m = 20; end % 变异分布指数
if ~isfield(params, 'show_progress'), params.show_progress = true; end;
end
2.2.2 种群初始化
matlab
function pop = init_population(nPop, nVar, varMin, varMax)
% 随机初始化种群(实数编码)
if isscalar(varMin), varMin = repmat(varMin, 1, nVar); end
if isscalar(varMax), varMax = repmat(varMax, 1, nVar); end
pop = rand(nPop, nVar) .* (varMax - varMin) + varMin;
end
2.2.3 目标函数评估
matlab
function F = evaluate_population(fun, pop)
% 评估种群目标函数值
nPop = size(pop, 1);
F = zeros(nPop, 0);
for i = 1:nPop
f = fun(pop(i, :)); % 调用目标函数
F(i, 1:length(f)) = f; % 存储为行向量
end
end
2.2.4 非支配排序
matlab
function [front, rank] = non_domination_sort(F)
% 非支配排序:将种群分为不同前沿(front=1为Pareto前沿)
nPop = size(F, 1);
nObj = size(F, 2);
front = zeros(nPop, 1); % 前沿等级
rank = zeros(nPop, 1); % 个体秩
dominated_count = zeros(nPop, 1); % 被支配次数
dominated_set = cell(nPop, 1); % 支配的个体集合
% 计算支配关系
for i = 1:nPop
for j = 1:nPop
if i == j, continue; end
if all(F(i, :) <= F(j, :)) && any(F(i, :) < F(j, :))
dominated_set{i} = [dominated_set{i}, j]; % i支配j
elseif all(F(j, :) <= F(i, :)) && any(F(j, :) < F(i, :))
dominated_count(i) = dominated_count(i) + 1; % j支配i
end
end
if dominated_count(i) == 0 % 不被任何个体支配,属于第1前沿
front(i) = 1;
rank(i) = 1;
end
end
% 构建后续前沿
current_front = 1;
while any(front == current_front)
next_front = current_front + 1;
for i = 1:nPop
if front(i) == current_front
for j = dominated_set{i}
dominated_count(j) = dominated_count(j) - 1;
if dominated_count(j) == 0
front(j) = next_front;
rank(j) = next_front;
end
end
end
end
current_front = next_front;
end
end
2.2.5 拥挤度计算
matlab
function crowding = crowding_distance(F, front)
% 计算拥挤度(保持种群多样性)
nPop = size(F, 1);
nObj = size(F, 2);
crowding = zeros(nPop, 1);
for m = 1:nObj % 对每个目标函数计算
[sorted_F, sort_idx] = sort(F(:, m)); % 按目标m排序
sorted_front = front(sort_idx);
crowding(sort_idx(1)) = Inf; % 边界点拥挤度设为无穷
crowding(sort_idx(end)) = Inf;
f_min = sorted_F(1);
f_max = sorted_F(end);
if f_max - f_min < 1e-6, continue; end % 避免除零
for i = 2:nPop-1
if sorted_front(i) ~= sorted_front(i-1) || sorted_front(i) ~= sorted_front(i+1)
continue; % 不同前沿的点不相互影响
end
% 归一化距离
dist = (sorted_F(i+1) - sorted_F(i-1)) / (f_max - f_min);
crowding(sort_idx(i)) = crowding(sort_idx(i)) + dist;
end
end
end
2.2.6 选择操作(二元锦标赛)
matlab
function parents = tournament_selection(pop, front, crowding, nPop)
% 二元锦标赛选择:优先选前沿等级高的,同前沿选拥挤度大的
n = size(pop, 1);
parents = zeros(nPop, size(pop, 2));
for i = 1:nPop
cand1 = randi(n);
cand2 = randi(n);
if front(cand1) < front(cand2) % 前沿等级高者胜出
winner = cand1;
elseif front(cand1) > front(cand2)
winner = cand2;
else % 同前沿,拥挤度大者胜出
if crowding(cand1) > crowding(cand2)
winner = cand1;
else
winner = cand2;
end
end
parents(i, :) = pop(winner, :);
end
end
2.2.7 遗传操作(交叉+变异)
matlab
function offspring = genetic_operators(parents, params, varMin, varMax)
% 交叉(SBX)与变异(多项式变异)
nPop = size(parents, 1);
nVar = size(parents, 2);
offspring = zeros(nPop, nVar);
for i = 1:2:nPop
p1 = parents(i, :);
p2 = parents(i+1, :);
% 交叉
if rand < params.pc
[c1, c2] = sbx_crossover(p1, p2, params.eta_c, varMin, varMax);
else
c1 = p1; c2 = p2;
end
% 变异
c1 = poly_mutation(c1, params.pm, params.eta_m, varMin, varMax);
c2 = poly_mutation(c2, params.pm, params.eta_m, varMin, varMax);
offspring(i, :) = c1;
if i+1 <= nPop, offspring(i+1, :) = c2; end
end
end
% SBX交叉
function [c1, c2] = sbx_crossover(p1, p2, eta_c, varMin, varMax)
nVar = length(p1);
c1 = p1; c2 = p2;
for i = 1:nVar
if rand < 0.5
beta = (2*rand)^(1/(eta_c+1)); % 交叉参数
c1(i) = 0.5*((1+beta)*p1(i) + (1-beta)*p2(i));
c2(i) = 0.5*((1-beta)*p1(i) + (1+beta)*p2(i));
% 边界处理
c1(i) = min(max(c1(i), varMin(i)), varMax(i));
c2(i) = min(max(c2(i), varMin(i)), varMax(i));
end
end
end
% 多项式变异
function c = poly_mutation(p, pm, eta_m, varMin, varMax)
nVar = length(p);
c = p;
for i = 1:nVar
if rand < pm
delta = (2*rand)^(1/(eta_m+1)) - 1; % 变异步长
c(i) = p(i) + delta*(varMax(i)-varMin(i));
c(i) = min(max(c(i), varMin(i)), varMax(i));
end
end
end
2.2.8 环境选择(精英保留)
matlab
function [new_pop, new_F, new_front, new_crowding] = environmental_selection(combined_pop, combined_F, nPop)
% 从合并种群(2nPop)中选择nPop个精英个体
nCombined = size(combined_pop, 1);
[front, rank] = non_domination_sort(combined_F);
crowding = crowding_distance(combined_F, front);
% 按前沿等级+拥挤度排序
[~, sort_idx] = sortrows([rank, -crowding], [1, 2]);
selected_idx = sort_idx(1:nPop); % 选择前nPop个个体
new_pop = combined_pop(selected_idx, :);
new_F = combined_F(selected_idx, :);
new_front = front(selected_idx);
new_crowding = crowding(selected_idx);
end
参考代码 基于nsga-2的多目标优化算法 www.youwenfan.com/contentcst/160578.html
三、应用案例:ZDT测试函数优化
3.1 ZDT1测试函数(双目标)
minf1(x)=x1\min \quad f_1(x) = x_1minf1(x)=x1
minf2(x)=g(x)(1−x1g(x))\min \quad f_2(x) = g(x) \left(1 - \sqrt{\frac{x_1}{g(x)}}\right)minf2(x)=g(x)(1−g(x)x1 )
g(x)=1+9∑i=2nxin−1,xi∈[0,1]g(x) = 1 + 9\sum_{i=2}^n \frac{x_i}{n-1}, \quad x_i \in [0,1]g(x)=1+9i=2∑nn−1xi,xi∈[0,1]
Pareto前沿:f2=1−f1,f1∈[0,1]f_2 = 1 - \sqrt{f_1}, f_1 \in [0,1]f2=1−f1 ,f1∈[0,1]。
3.2 MATLAB实现与可视化
matlab
%% 案例:NSGA-II优化ZDT1测试函数
clear; clc; close all;
% 1. 参数设置
nVar = 10; % 变量个数
varMin = 0; % 变量下界
varMax = 1; % 变量上界
nPop = 100; % 种群大小
maxGen = 200; % 迭代次数
params.pc = 0.9; % 交叉概率
params.pm = 1/nVar; % 变异概率
% 2. 目标函数(ZDT1)
zdt1 = @(x) deal(x(1), 1+9*sum(x(2:end))/(nVar-1) * (1 - sqrt(x(1)/(1+9*sum(x(2:end))/(nVar-1)))));
% 3. 运行NSGA-II
[pop, F, front, crowding] = nsga2(zdt1, nVar, varMin, varMax, nPop, maxGen, params);
% 4. 提取Pareto前沿
pareto_idx = find(front == 1);
pareto_F = F(pareto_idx, :);
% 5. 可视化Pareto前沿
figure;
plot(F(:,1), F(:,2), 'ro', 'MarkerSize', 6, 'DisplayName', '所有解');
hold on;
plot(pareto_F(:,1), pareto_F(:,2), 'b*', 'MarkerSize', 8, 'DisplayName', 'Pareto前沿');
xlabel('f1'); ylabel('f2'); title('ZDT1函数Pareto前沿');
legend; grid on;
% 6. 绘制理论Pareto前沿
f1_true = linspace(0, 1, 100);
f2_true = 1 - sqrt(f1_true);
plot(f1_true, f2_true, 'k--', 'LineWidth', 2, 'DisplayName', '理论前沿');
legend;
四、关键技术与性能评估
4.1 参数调优建议
- 种群大小:50-200(目标函数越多,种群需越大)。
- 交叉/变异概率 :pc=0.8∼0.95p_c=0.8\sim0.95pc=0.8∼0.95,pm=1/nvar∼1/2p_m=1/n_{\text{var}}\sim1/2pm=1/nvar∼1/2。
- 分布指数 :ηc=10∼20\eta_c=10\sim20ηc=10∼20(交叉),ηm=20∼100\eta_m=20\sim100ηm=20∼100(变异,值越大变异越温和)。
4.2 性能评估指标
- 世代距离(GD):衡量解集与真实Pareto前沿的距离(越小越好)。
- 反向世代距离(IGD):衡量真实Pareto前沿到解集的距离(越小越好)。
- 超体积(HV):衡量解集的"覆盖范围"(越大越好)。
- 间距(Spacing):衡量解集分布的均匀性(越小越好)。
4.3 约束处理方法
- 罚函数法 :将约束违反量加入目标函数,如 F′=F+λ⋅penaltyF' = F + \lambda \cdot \text{penalty}F′=F+λ⋅penalty。
- 可行解优先:选择时优先保留可行解,可行解间用拥挤度比较。
五、高级功能扩展
5.1 并行计算加速
matlab
% 用parfor并行评估目标函数
function F = evaluate_population_parallel(fun, pop)
nPop = size(pop, 1);
F = zeros(nPop, 0);
parfor i = 1:nPop % 需开启并行池(parpool)
f = fun(pop(i, :));
F(i, 1:length(f)) = f;
end
end
5.2 自适应参数调整
matlab
% 动态调整交叉/变异概率
if mod(gen, 50) == 0
params.pc = 0.9 - 0.1*gen/maxGen; % 随迭代降低交叉概率
params.pm = 1/nVar + 0.1*gen/maxGen; % 随迭代提高变异概率
end
5.3 高维目标处理
- 降维:用PCA将多目标降为2-3维可视化。
- 目标分组:将强相关的目标合并,减少目标数量。
六、总结
NSGA-II通过非支配排序 和拥挤度计算,高效求解多目标优化问题,广泛应用于工程设计、资源调度、机器学习超参数优化等领域。MATLAB实现需注意:
- 目标函数需返回行向量;
- 合理设置参数(种群大小、交叉/变异概率);
- 用可视化(Pareto前沿)评估优化效果。