深入理解多目标优化实数编码:SBX交叉与多项式变异(PM)原理解析与MATLAB可视化
在多目标优化(Multi-Objective Optimization, MOO)领域,特别是在编写像 NSGA-II 这样经典的进化算法时,会遇到一个门槛:传统的 0/1 二进制交叉和变异算子没法用了。
为什么?因为实际工程问题中的决策变量往往是连续的实数(比如长度、重量、温度)。为了直接在实数空间中进行遗传操作,研究人员提出了两个极为经典的实数编码算子:模拟二进制交叉(Simulated Binary Crossover, SBX) 和 多项式变异(Polynomial Mutation, PM)。
一、 直观感悟:我们为什么需要 SBX 和 PM?
在传统的二进制算法中,子代基因通常是在父代基因的基础上的重新组合或小概率翻转。当转移到实数空间时,我们希望新的算子能够保留这种"遗传特性":
- 局部搜索(大概率): 子代应该大概率分布在父代个体的附近。
- 全局探索(小概率): 子代也有一定的小概率发生"远距离跳跃",以保持种群多样性。
SBX 和 PM 引入了一个核心概念:分布指数(Distribution Index,通常记为 η\etaη) 。
你可以把分布指数想象成一根弹簧:
- 弹簧越紧(η\etaη 越大 ),子代被牢牢拴在父代身边,算法的局部搜索能力强。
- 弹簧越松(η\etaη 越小 ),子代分布得越广,算法的全局探索能力强。
二、 模拟二进制交叉 (SBX) 原理解析
1. 核心思想
SBX 的目标是模拟二进制单点交叉的特性,即"两个子代到父代的平均距离等于父代之间的距离"。它通过引入一个扩展因子(Spread Factor)β\betaβ 来决定子代的具体位置。
2. 数学推导
假设有两个父代个体 x1x_1x1 和 x2x_2x2,我们要生成两个子代 y1y_1y1 和 y2y_2y2。
扩展因子 β\betaβ 定义为子代间距与父代间距的比值:
β=∣y2−y1x2−x1∣\beta = \left| \frac{y_2 - y_1}{x_2 - x_1} \right|β= x2−x1y2−y1
在 SBX 中,β\betaβ 的概率密度函数受交叉分布指数 ηc\eta_cηc 控制:
P(β)={0.5(ηc+1)βηc如果 β≤10.5(ηc+1)(1β)ηc+2如果 β>1P(\beta) = \begin{cases} 0.5(\eta_c + 1)\beta^{\eta_c} & \text{如果 } \beta \le 1 \\ 0.5(\eta_c + 1)\left(\frac{1}{\beta}\right)^{\eta_c+2} & \text{如果 } \beta > 1 \end{cases}P(β)=⎩ ⎨ ⎧0.5(ηc+1)βηc0.5(ηc+1)(β1)ηc+2如果 β≤1如果 β>1
(注:β≤1\beta \le 1β≤1 意味着子代落在父代之间;β>1\beta > 1β>1 意味着子代落在父代外侧)
如何用代码生成服从该分布的 β\betaβ?
由于计算机只能生成均匀分布的随机数 u∼U(0,1)u \sim U(0,1)u∼U(0,1),我们需要使用逆变换采样法来计算 β\betaβ:
β={(2u)1ηc+1如果 u≤0.5(12(1−u))1ηc+1如果 u>0.5\beta = \begin{cases} (2u)^{\frac{1}{\eta_c+1}} & \text{如果 } u \le 0.5 \\ \left(\frac{1}{2(1-u)}\right)^{\frac{1}{\eta_c+1}} & \text{如果 } u > 0.5 \end{cases}β=⎩ ⎨ ⎧(2u)ηc+11(2(1−u)1)ηc+11如果 u≤0.5如果 u>0.5
最后,计算出两个子代基因:
y1=0.5[(1+β)x1+(1−β)x2]y_1 = 0.5[(1+\beta)x_1 + (1-\beta)x_2]y1=0.5[(1+β)x1+(1−β)x2]
y2=0.5[(1−β)x1+(1+β)x2]y_2 = 0.5[(1-\beta)x_1 + (1+\beta)x_2]y2=0.5[(1−β)x1+(1+β)x2]
三、 多项式变异 (PM) 原理解析
1. 核心思想
变异是为了防止算法早熟收敛。PM 通过在父代个体的基础上加上一个由多项式分布决定的扰动量来实现变异。
2. 数学推导
假设父代基因为 xxx,变量的取值范围为 [xL,xU][x_L, x_U][xL,xU]。变异后的子代 yyy 计算公式为:
y=x+δ⋅(xU−xL)y = x + \delta \cdot (x_U - x_L)y=x+δ⋅(xU−xL)
这里的 δ\deltaδ 就是扰动因子,它由变异分布指数 ηm\eta_mηm 和随机数 u∼U(0,1)u \sim U(0,1)u∼U(0,1) 共同决定:
δ={(2u)1ηm+1−1如果 u≤0.51−(2(1−u))1ηm+1如果 u>0.5\delta = \begin{cases} (2u)^{\frac{1}{\eta_m+1}} - 1 & \text{如果 } u \le 0.5 \\ 1 - (2(1-u))^{\frac{1}{\eta_m+1}} & \text{如果 } u > 0.5 \end{cases}δ={(2u)ηm+11−11−(2(1−u))ηm+11如果 u≤0.5如果 u>0.5
避坑指南 :在实际写代码(如 NSGA-II)时,还需要考虑边界截断 。如果计算出的子代 yyy 超出了 [xL,xU][x_L, x_U][xL,xU] 的范围,通常需要将其强制拉回边界。
四、 MATLAB 实时交互代码:眼见为实
光看公式难免枯燥。我编写了一段 MATLAB 脚本,利用其强大的 UI 交互功能,将 SBX 和 PM 的概率密度分布进行了可视化。
你可以直接复制以下代码在 MATLAB 中运行。 界面中包含了滑动条和手动输入框,你可以随意调节 ηc\eta_cηc 和 ηm\eta_mηm,实时观察"弹簧"的松紧是如何改变概率分布曲线的!
matlab
function sbx_pm_demo_with_input()
% SBX 与 PM 算法实时分布可视化 (带手动输入功能)
% 运行此脚本将弹出一个交互式窗口
% 1. 初始参数设置
x_range = linspace(-2, 12, 1000); % x轴范围
p1 = 2; p2 = 8; % SBX 的两个父代位置
pm_parent = 5; % PM 的父代位置
eta_c = 15; % 初始 SBX 指数
eta_m = 20; % 初始 PM 指数
% 2. 创建图形窗口
fig = figure('Name', '实数编码算子实时演示', 'Position', [100, 100, 950, 650], 'Color', 'w');
% --- SBX 子图 ---
ax1 = axes('Parent', fig, 'Position', [0.1 0.58 0.85 0.35]);
hold(ax1, 'on'); grid on;
hSBX = plot(ax1, x_range, zeros(size(x_range)), 'LineWidth', 2, 'Color', [0 0.45 0.74]);
xline(ax1, p1, '--r', 'P1', 'LabelVerticalAlignment', 'bottom');
xline(ax1, p2, '--r', 'P2', 'LabelVerticalAlignment', 'bottom');
title(ax1, ['SBX 子代分布 (P1=2, P2=8), \eta_c = ', num2str(eta_c)]);
ylabel(ax1, '概率密度');
% --- PM 子图 ---
ax2 = axes('Parent', fig, 'Position', [0.1 0.15 0.85 0.35]);
hold(ax2, 'on'); grid on;
hPM = plot(ax2, x_range, zeros(size(x_range)), 'LineWidth', 2, 'Color', [0.85 0.33 0.1]);
xline(ax2, pm_parent, '--k', 'Parent', 'LabelVerticalAlignment', 'bottom');
title(ax2, ['PM 变异分布 (Parent=5), \eta_m = ', num2str(eta_m)]);
xlabel(ax2, '决策变量值'); ylabel(ax2, '概率密度');
% 3. 添加交互控件 (滑块 + 手动输入框)
% -- SBX 控件组 --
uicontrol(fig, 'Style', 'text', 'String', 'SBX 指数 \eta_c:', 'Position', [80 20 80 20], 'BackgroundColor', 'w');
edt_c = uicontrol(fig, 'Style', 'edit', 'String', num2str(eta_c), ...
'Position', [160 20 40 22], 'Callback', @(s,e) syncControls('edit', 'c'));
sld_c = uicontrol(fig, 'Style', 'slider', 'Min', 1, 'Max', 100, 'Value', eta_c, ...
'Position', [210 20 200 20], 'Callback', @(s,e) syncControls('slider', 'c'));
% -- PM 控件组 --
uicontrol(fig, 'Style', 'text', 'String', 'PM 指数 \eta_m:', 'Position', [480 20 80 20], 'BackgroundColor', 'w');
edt_m = uicontrol(fig, 'Style', 'edit', 'String', num2str(eta_m), ...
'Position', [560 20 40 22], 'Callback', @(s,e) syncControls('edit', 'm'));
sld_m = uicontrol(fig, 'Style', 'slider', 'Min', 1, 'Max', 100, 'Value', eta_m, ...
'Position', [610 20 200 20], 'Callback', @(s,e) syncControls('slider', 'm'));
% 初始绘制
updatePlots();
% =========================================================
% 核心逻辑嵌套函数
% =========================================================
% 控件同步函数:确保滑块和输入框的值一致
function syncControls(sourceType, paramType)
if strcmp(paramType, 'c')
if strcmp(sourceType, 'slider')
val = get(sld_c, 'Value');
set(edt_c, 'String', num2str(val, '%.1f'));
else
val = str2double(get(edt_c, 'String'));
if isnan(val), val = get(sld_c, 'Value'); end
val = max(1, min(100, val));
set(sld_c, 'Value', val);
set(edt_c, 'String', num2str(val, '%.1f'));
end
else
if strcmp(sourceType, 'slider')
val = get(sld_m, 'Value');
set(edt_m, 'String', num2str(val, '%.1f'));
else
val = str2double(get(edt_m, 'String'));
if isnan(val), val = get(sld_m, 'Value'); end
val = max(1, min(100, val));
set(sld_m, 'Value', val);
set(edt_m, 'String', num2str(val, '%.1f'));
end
end
updatePlots();
end
% 图形更新函数
function updatePlots()
ec = get(sld_c, 'Value');
em = get(sld_m, 'Value');
% --- 计算并更新 SBX 曲线 ---
dist = p2 - p1;
beta_vals = abs(2 * (x_range - (p1+p2)/2) / dist);
pdf_sbx = zeros(size(beta_vals));
idx1 = beta_vals <= 1;
pdf_sbx(idx1) = 0.5 * (ec + 1) * (beta_vals(idx1).^ec);
idx2 = beta_vals > 1;
pdf_sbx(idx2) = 0.5 * (ec + 1) * (1 ./ (beta_vals(idx2).^(ec + 2)));
pdf_sbx = pdf_sbx / dist;
set(hSBX, 'YData', pdf_sbx);
title(ax1, ['SBX 子代分布 (P1=2, P2=8), \eta_c = ', num2str(ec, '%.1f')]);
% --- 计算并更新 PM 曲线 ---
delta = (x_range - pm_parent) / 5; % 假设步长缩放因子为5
pdf_pm = 0.5 * (em + 1) * (1 - abs(delta)).^em;
pdf_pm(abs(delta) > 1) = 0;
set(hPM, 'YData', pdf_pm);
title(ax2, ['PM 变异分布 (Parent=5), \eta_m = ', num2str(em, '%.1f')]);
drawnow;
end
end
