☰
Matlab实现9种智能优化算法横向对比:从PSO到HHO的实验设计全解析
2026/10/14 7:45:12 网站建设 项目流程

科研做优化对比的人,大概都有过这种体验:论文里一排缩写看得人眼花,CGO、SCA、GWO、CSA、SSA、HHO、WOA、PSO、TSO,每个都说自己突破了极限。可真到Matlab里把代码拉通跑一遍,才发现光是弄清楚“SSA到底是樽海鞘还是麻雀”“CSA是乌鸦还是免疫克隆选择”,就能耗掉一下午。

我最近正好把一套9种智能优化算法的横向对比实验完整整理了一遍。统一测试函数、统一种群规模和迭代次数、30次独立运行,输出收敛曲线和统计表格,顺手把代码框架也搭好了。这篇文章就把这套对比怎么设计、代码怎么组织、结果怎么解读、有哪些坑,一次说清楚。适合正在写毕业论文需要对比实验的研究生,也适合想给实际工程项目选一个靠谱优化算法的工程师。

1. 为什么要把9种算法放在一起比:你要交的对比实验到底在比什么

1.1 算法年年有,今年特别多:对比实验到底解决什么问题

智能优化算法这几年呈现一种“井喷式”更新。从1995年Kennedy和Eberhart提出粒子群优化(PSO)开始,到2014年灰狼优化(GWO)、2016年鲸鱼优化(WOA)、2017年樽海鞘群算法(SSA)、2019年哈里斯鹰优化(HHO)、2021年前后的混沌博弈优化(CGO)和被囊群优化(TSO),几乎每两三年就有一批新名字冒出来。

名字越来越多,问题也就越来越尖锐:这些新算法真的比经典算法强吗?强在哪些场景?换一个测试函数结果还会一样吗?你问任何一个做过优化算法对比的人,大概率都会得到同一个答案——“取决于实现”。不同论文里参数设置完全不一样,有的把某算法调得极其细致,有的只是把原文代码搬过来跑一遍,结论自然千差万别。

所以横向对比实验的核心价值,不是在“比出一个天下第一”,而是把所有算法放在同一个评价框架下,用同一套测试函数、同样的迭代次数、同样的种群规模、同样数量的独立运行次数,尽可能客观地观察它们的收敛行为。这就像一个统一考试:题目一样、考试时间一样、评分标准一样,才有资格去聊谁考得更好。

1.2 版本认定:CGO、CSA、SSA、TSO这些缩写必须较真

这9个缩写里,PSO、GWO、WOA三个属于“辨识度极高”的,基本不会认错。但剩下几个,缩写歧义大到连从业者都容易翻车。

先说SSA。它在智能优化算法里同时是Salp Swarm Algorithm(樽海鞘群算法)和Sparrow Search Algorithm(麻雀搜索算法)的缩写。前者是Mirjalili在2017年提出的链式leader-follower结构,后者是2020年提出的生产者-跟随者-预警者三角色模型。你拿到的代码如果主循环里有个一长串位置数组依次跟着第一个个体更新,那是樽海鞘;如果有麻雀的发现者、加入者、警戒者三个角色分类,那是麻雀。完全两种东西。

CSA也类似,最常见的是Crow Search Algorithm(乌鸦搜索算法),但偶尔会碰到Clonal Selection Algorithm(克隆选择算法)。区分方法很简单:乌鸦搜索里一定有一个“察觉概率AP”和一个“飞行长度fl”,而且每只乌鸦有独立的记忆位置。没有这两个参数,多半不是乌鸦。

TSO也有两个常见解释:Tunicate Swarm Optimization(被囊群算法)和Tuna Swarm Optimization(金枪鱼群算法)。被囊群算法靠喷水推进和群体社交行为更新位置,金枪鱼群则模仿鱼群螺旋搜索和抛物线觅食。两个算法的英文缩写都是TSO,代码结构完全不同。

CGO相对好点,现在多数论文里的CGO是Chaos Game Optimization(混沌博弈优化),基于分形几何中的“混沌游戏”思想。不过偶尔也会有论文用CGO指Cosmic Gravitational Optimization(宇宙引力优化),教你怎么判断:混沌博弈算法代码里一定有个类似Sierpinski三角形生成的过程,通过随机种子组合更新位置;宇宙引力算法则会出现引力常数,特征很明显。

较真版本认定不是吹毛求疵。审稿人只要发现你对比的“SSA”和他认知里的“SSA”不是同一个东西,整篇实验的可信度就会被打问号。我的习惯是拿到任何代码先看一眼主更新公式,再决定文章里怎么命名。

1.3 一套可复用的对比实验框架包含哪些模块

一个正经的对比实验,代码组织上至少要有四个模块:

  • 目标函数集合:Sphere、Rosenbrock、Rastrigin、Griewank、Ackley这类经典测试函数,有能力的上CEC2017或CEC2022竞赛函数集;
  • 算法实现集合:每个算法一个独立函数,统一接收目标函数句柄、维度、上下界、种群规模、最大迭代次数;
  • 指标统计模块:记录每次运行的最优值、平均值、标准差、最差值,以及多次运行后的排序;
  • 可视化模块:收敛曲线、箱线图、平均排名条形图。

这四块分开写,好处是加新算法、换测试函数都只需要改一个入口,不用整套重来。我这次整理的Matlab工程就是按这个结构组织的,后面会给出核心代码。

2. 九种算法机制拆解:谁在模仿自然,谁在玩数学

2.1 群体智能派:PSO、GWO、WOA的进攻与淘汰

先看三个最经典的群体智能代表。

PSO(粒子群优化)是最接地气的算法,理解起来就像一群外卖骑手在找最近的取餐点。每个骑手(粒子)有当前位置和当前速度,跑过的路线里自己觉得最好的位置记下来,同时整个团队目前发现的“最高分位置”大家也都知道。下一次移动就是“惯性飞一会儿 + 朝自己最好位置靠近一点 + 朝全局最好位置靠近一点”。三个系数对应三个参数:惯性权重w、自我认知学习因子c1、社会认知学习因子c2。w大则探索能力强,c1大则个体更有主见,c2大则跟风更明显。PSO的优势是简单稳定,缺点是容易早熟,一旦群体都聚到局部最优附近,c1和c2也难以把它再拉出来。

GWO(灰狼优化算法)用了狼群的社会等级。种群里有alpha、beta、delta三层“领导”,剩下的是omega。每只狼根据当前三匹头狼的位置加权更新自己的位置,同时用一个控制参数a从2线性降到0,模拟从“大范围搜索”到“精细开发”的过渡。GWO有个突出的工程优势:几乎没有需要手工调的敏感参数,a的衰减曲线就是主要的控制手段。这让它在工程场景里特别有吸引力,毕竟很多人不想花时间调PSO的三个参数。

WOA(鲸鱼优化算法)模拟座头鲸的泡泡网捕猎。有两种核心模式:一种是以当前最优个体为靶向去包围猎物,另一种是让个体绕最优解做螺旋运动。两种模式根据随机概率切换。WOA的特点是对早熟的抑制比PSO好一点,因为螺旋运动会强制个体在局部最优附近来回绕,相当于多了一层震荡,有时候能借此跳到邻域外面。

这三兄弟放在一起对比时,收敛速度的典型特征是“前期PSO和GWO都很猛,中后期WOA的螺旋策略容易维持收敛势头;PSO如果参数没调好,很可能在某个局部坑里趴着不动。”

2.2 数学几何派:SCA与CGO,不靠动物也能打

很多人习惯把优化算法都归类成“仿生算法”,但其实有相当一部分靠的是纯数学构造,SCA(正余弦算法)就是典型代表。

SCA的核心更新公式里有一个正弦和一个余弦项。个体下一时刻的位置,等于当前位置加上“正弦/余弦值 × 一个随机幅度 × 到当前最优解的距离”。正余弦函数的周期性让个体在搜索空间中做“来回震荡”式的运动,振幅r1随着迭代次数线性递减,相当于一开始放大步子做全局探索,后面越走越细,逼近最优解。SCA在低维问题上表现轻快,但它不怎么“聪明”,只是个数学节奏器,在高维复杂问题上很容易精度不够。

CGO(混沌博弈优化)更具几何趣味。它的灵感来自分形几何里的“混沌游戏”生成Sierpinski三角形方法:在一个三角形里随机取一个初始点,然后不停地在“当前点和某个顶点连线的中点”处放点,重复足够多次,最终这些点会自组织成著名分形图案。优化算法把“顶点”换成当前群体里的几个候选解,用类似“随机中点插值”的方式生成新位置,同时引入几个随机因子控制插值比例和方向。这个机制赋予CGO很强的探索性,位置更新方式非线性程度高,所以在一些多峰函数上有意外惊喜,但随机性也比较大,单次运行结果很不稳定,必须多跑几次统计学才立得住。

2.3 行为策略派:CSA、SSA、TSO的群体智慧

CSA(乌鸦搜索算法)模拟的是乌鸦偷藏食物的行为。每只乌鸦有两个关键状态:自己记忆里藏食的地方,以及是否意识到背后有人跟踪。位置更新时,乌鸦会随机挑另一只乌鸦“跟着走”,但如果被发现概率高于AP(察觉概率),就干脆随机跳到一个新地方。AP可以看作“跟踪成功率”的反向控制旋钮,AP越大,乌鸦越警觉,偷别人藏食点越难,群体探索越强;AP越小,越容易聚到局部。实际测试中AP从0.2调到0.8,结果能差一个数量级,这不是夸张。

SSA(樽海鞘群算法)设计上比较优雅优雅:一条“樽海鞘链”由leader带路,后面的follower一个接一个按链式公式往leader方向靠。leader的位置决定整个群体的探索方向,follower则主要做局部跟随。这种链式结构让SSA的搜索相对平滑,很少出现某些算法那种“群体突然炸开”的情况,但代价是高维问题上链太长,收敛速度会被拖慢。

TSO(被囊群算法)从海鞘类生物的喷水推进行为里提炼了两个阶段:推进与社交。推进阶段产生新的候选位置,社交阶段让个体向邻域最优靠拢。相比PSO的直接“教条式更新”,TSO的位置更新公式里加了很多随机系数,行为更飘忽,更适合需要强探索的场景,但后期收敛精度需要额外在意。

2.4 HHO的特殊地位:多策略切换的代表

HHO(哈里斯鹰优化算法)在9个算法里属于“策略切换”做得最复杂的一个。它模拟哈里斯鹰协同围捕猎物,核心是定义一个兔子逃逸能量E。当E的绝对值较大时,鹰群做探索;当E变小时,转入开发。开发阶段又根据兔子逃逸成功率分成“软围攻”和“硬围攻”,再根据鹰群是否能形成有效包围分成四种情况。这套决策树让HHO在不同阶段能切换不一样的更新公式,理论上兼顾了探索与开发的平衡。

实际跑下来,HHO的优点和缺点都来自同一个地方:它太“剧本化”了。机制越复杂,状态切换越依赖随机数,单次运行结果的方差往往大于PSO、GWO这类算法。做实验必须给它充分的独立运行次数,否则一条好看的收敛曲线很可能是碰运气出来的。

2.5 一张表快速记住9种算法的核心机制

算法核心隐喻主控参数最容易被坑的点
PSO鸟群觅食共享信息w、c1、c2速度未限制导致越界
GWO狼群等级围猎a线性递减几乎无额外参数,适合做基线
WOA座头鲸螺旋捕食a、b螺旋更新概率影响后期精度
HHO鹰群围攻兔子E逃逸能量策略切换多,方差大
SCA正余弦震荡搜索r1振幅衰减高维精度有限
CGO混沌游戏生成分形种子组合系数随机性强,需多次运行
CSA乌鸦跟踪偷藏食物AP、flAP取值对结果影响巨大
SSA樽海鞘链式跟随c1衰减高维收敛慢
TSO被囊群喷水推进c1、c2、c3不同论文定义不同,需先确认版本

3. 用Matlab跑完9种算法对比:核心代码与结果解读

3.1 实验设计:先把公平性这块地基打好

做对比实验,最重要的不是算法写得多花哨,而是“考试环境”必须公平。这个公平性主要体现在四个地方:

  • 测试函数统一。每个函数都有标准的搜索范围。Sphere一般取[-100, 100],Rosenbrock取[-30, 30]或者[-10, 10],Rastrigin取[-5.12, 5.12],Griewank取[-600, 600],Ackley取[-32, 32]。不同函数边界不一样,写代码时不能用同一个边界硬套。
  • 基本参数统一。种群规模、最大迭代次数、独立运行次数必须完全一致。我的做法是种群N=30、最大迭代MaxIter=500、独立运行Runs=30,三个数字全局统一。
  • 初始种群可控。为了让结果更公平,每次算法运行前都用同一个随机种子方案重新设置随机数流。常见做法是用“1000 + 函数索引100 + 算法索引10 + 运行次数r”作为种子,这样每个算法在同一个测试函数上的同一轮运行,面对的初始种群是完全一样的,能有效避免“算法A运气好抽到了好初始点”的争议。
  • 边界处理方式一致。我统一用截断方式处理越界个体,就是clamp,把超出边界的维度直接钳到边界上。有人喜欢反射、周期延拓,虽然各有道理,但所有算法必须用同一套。

3.2 核心Matlab代码框架:一套能复用到毕业的模板

文件组织方式建议:

compare_algorithms/ ├─ run_comparison.m % 主程序 ├─ run_algorithm.m % 算法分发接口 ├─ test_functions.m % 测试函数集合 ├─ algorithms/ │ ├─ pso.m │ ├─ gwo.m │ ├─ woa.m │ ├─ hho.m │ ├─ sca.m │ ├─ cgo.m │ ├─ csa.m │ ├─ ssa.m │ └─ tso.m └─ results/

主程序run_comparison.m的核心结构如下,它把所有测试函数、所有算法、所有独立运行都串起来:

%% 9种智能优化算法对比实验主程序 % 测试函数:Sphere, Rosenbrock, Rastrigin, Griewank, Ackley % 统一参数:种群N=30,最大迭代MaxIter=500,独立运行Runs=30 clear; clc; algorithms = {'CGO','SCA','GWO','CSA','SSA','HHO','WOA','PSO','TSO'}; funcs = {@sphere, @rosenbrock, @rastrigin, @griewank, @ackley}; bounds = [-100 100; -30 30; -5.12 5.12; -600 600; -32 32]; dim = 30; N = 30; MaxIter = 500; Runs = 30; for fi = 1:length(funcs) f = funcs{fi}; lb = bounds(fi,1) * ones(1,dim); ub = bounds(fi,2) * ones(1,dim); for a = 1:length(algorithms) algo = algorithms{a}; fprintf('测试:函数%d | %s\n', fi, algo); for r = 1:Runs % 保证所有算法同一函数同一轮的初始种群一致 rng(1000 + fi*100 + a*10 + r, 'twister'); [BestScore, BestPos, ConvCurve] = ... run_algorithm(algo, f, dim, lb, ub, N, MaxIter); % 保存当前单次运行结果到 results/ 目录 save(sprintf('results/%s_func%d_run%d.mat', algo, fi, r), ... 'BestScore', 'BestPos', 'ConvCurve'); end end end

run_algorithm.m是算法分发接口,所有算法共用同一个目标函数句柄:

function [BestScore, BestPos, ConvCurve] = run_algorithm(algo, f, dim, lb, ub, N, MaxIter) switch lower(algo) case 'pso' [BestScore, BestPos, ConvCurve] = pso(f, dim, lb, ub, N, MaxIter); case 'gwo' [BestScore, BestPos, ConvCurve] = gwo(f, dim, lb, ub, N, MaxIter); case 'woa' [BestScore, BestPos, ConvCurve] = woa(f, dim, lb, ub, N, MaxIter); case 'hho' [BestScore, BestPos, ConvCurve] = hho(f, dim, lb, ub, N, MaxIter); case 'sca' [BestScore, BestPos, ConvCurve] = sca(f, dim, lb, ub, N, MaxIter); case 'cgo' [BestScore, BestPos, ConvCurve] = cgo(f, dim, lb, ub, N, MaxIter); case 'csa' [BestScore, BestPos, ConvCurve] = csa(f, dim, lb, ub, N, MaxIter); case 'ssa' [BestScore, BestPos, ConvCurve] = ssa(f, dim, lb, ub, N, MaxIter); case 'tso' [BestScore, BestPos, ConvCurve] = tso(f, dim, lb, ub, N, MaxIter); otherwise error('未知算法:%s', algo); end end

测试函数文件test_functions.m里放几个经典函数,注意Rastrigin的写法要对,公式是“10n + 求和(xi^2 - 10cos(2πxi))”:

function y = sphere(x) y = sum(x .^ 2); end function y = rosenbrock(x) y = sum(100 * (x(2:end) - x(1:end-1).^2).^2 + (1 - x(1:end-1)).^2); end function y = rastrigin(x) n = numel(x); y = 10 * n + sum(x .^ 2 - 10 * cos(2 * pi * x)); end function y = griewank(x) n = numel(x); y = sum(x.^2 / 4000) - prod(cos(x ./ sqrt(1:n))) + 1; end function y = ackley(x) n = numel(x); sum1 = sum(x.^2); sum2 = sum(cos(2 * pi * x)); y = -20 * exp(-0.2 * sqrt(sum1 / n)) - exp(sum2 / n) + 20 + exp(1); end

以PSO为例,展示算法函数的标准写法。其他8种算法只需要保证输入参数和返回格式完全一致,内部按各自机理写即可:

function [BestScore, BestPos, ConvCurve] = pso(f, dim, lb, ub, N, MaxIter) vmax = 0.2 * (ub - lb); x = lb + (ub - lb) .* rand(N, dim); v = -vmax + 2 * vmax .* rand(N, dim); pbest = x; pbest_score = zeros(N, 1); for i = 1:N pbest_score(i) = f(x(i, :)); end [gbest_score, idx] = min(pbest_score); gbest = pbest(idx, :); ConvCurve = zeros(1, MaxIter); w = 0.9; c1 = 2; c2 = 2; for t = 1:MaxIter for i = 1:N v(i, :) = w * v(i, :) + c1 * rand * (pbest(i, :) - x(i, :)) ... + c2 * rand * (gbest - x(i, :)); v(i, :) = max(min(v(i, :), vmax), -vmax); x(i, :) = x(i, :) + v(i, :); x(i, :) = max(min(x(i, :), ub), lb); score = f(x(i, :)); if score < pbest_score(i) pbest_score(i) = score; pbest(i, :) = x(i, :); end end [gbest_score, idx] = min(pbest_score); gbest = pbest(idx, :); ConvCurve(t) = gbest_score; end BestScore = gbest_score; BestPos = gbest; end

重点说明两点。第一,pbest_score初始化时不能图省事全设为inf,因为如果所有个体得分都很差,第一轮gbest可能长时间不更新,收敛曲线会有一段平线。第二,边界截断用的是max(min(...)),这个方法简单粗暴但工程上最稳,也最容易让审稿人看懂你做了什么处理。GWO和WOA虽然没有速度限制,但边界截断逻辑要保留,否则种群容易跑飞。

3.3 结果统计与收敛曲线:别让画图拖后腿

跑完一轮实验,你会得到每个算法在每个函数上30次的BestScore和收敛曲线。统计阶段要算五个指标:

  • Best:30次中的最优值,说明算法上限;
  • Mean:30次的平均值,说明整体水平;
  • Std:标准差,说明稳定性;
  • Worst:最差值,说明下限;
  • Rank:对Mean排序得到的排名,用于横向对比。

统计代码块:

function stat = compute_statistics(scores) stat.best = min(scores); stat.mean = mean(scores); stat.std = std(scores); stat.worst = max(scores); [~, idx] = sort(scores); stat.median = median(scores); end

收敛曲线的核心画法是用对数纵轴semilogy:

figure; hold on; colors = lines(length(algorithms)); for a = 1:length(algorithms) curve_all = zeros(Runs, MaxIter); for r = 1:Runs data = load(sprintf('results/%s_func%d_run%d.mat', algorithms{a}, fi, r)); curve_all(r, :) = data.ConvCurve; end curve_mean = mean(curve_all, 1); curve_mean = max(curve_mean, 1e-300); % 避免log(0)警告 semilogy(1:MaxIter, curve_mean, 'LineWidth', 1.8, 'Color', colors(a, :)); end legend(algorithms, 'Location', 'northeast'); xlabel('迭代次数'); ylabel('当前最优适应度(对数坐标)'); set(gca, 'YScale', 'log'); grid on;

这里有个细节非常容易踩:如果某个算法在Sphere函数上收敛到了0,或者数值低于1e-300,semilogy会发出警告,因为log(0)没有定义。一行max(curve_mean, 1e-300)就能解决,但很多人第一次画图都会卡在这里几小时。还有一点,收敛曲线记录的是“每一代当前全局最优适应度”,而不是“这一代种群平均适应度”。两者含义完全不同,审稿人看到后者基本直接退回重改。

3.4 我这轮实测的典型结果与观察

下面这张是一轮有代表性的结果结构示意,数字不是普适结论,但能反映常见行为模式:

测试函数收敛精度第一梯队稳定性最差
SpherePSO、GWOHHO、CGO
RosenbrockWOA、HHOSCA
RastriginHHO、WOASSA
GriewankGWO、PSOCGO、TSO
AckleyPSO、GWOSCA

我观察到几个比较有代表性的现象。

在单峰函数Sphere上,PSO和GWO凭借数学上“朝最优靠拢”的机制,收敛速度非常快,30维环境下能快速压到极低水平。而HHO、CGO这类随机性更强的算法,虽然探索能力强,但在平坦单峰面上的“精确逼近”反而不如经典算法,这就是收敛精度被甩开的常见原因。

在多峰函数Rastrigin上,HHO和WOA这种带复杂跳跃机制的算法更有明显优势。Rastrigin函数布满了很多局部最优点,PSO靠c1、c2很难爬出局部坑,但WOA的螺旋运动和HHO的策略切换都相当于给种群补充了“跳出坑”的随机动量,所以更容易在众多局部最优里找到接近全局最优的解。

SCA在多数高维函数上属于“早起很猛、后期乏力”,因为正余弦振幅随迭代递减,到了后期步长限制得太死,精度很难继续提升。CSA的表现高度依赖AP参数,AP大则搜索范围广,但收敛慢;AP小则容易早熟。SSA樽海鞘链在维度升高后模型更新速度明显下降,30维就已经能看出疲惫。TSO在几个函数上波动大,默认参数需要在不同测试函数上分别验证。

这些都是我这套实现下的观察,换一个随机种子策略、换参数设置,排序可能会局部变化。所以写论文时一定不要把结论说死,配合后续的统计检验才能站得住脚。

4. 我踩过的坑:缩写歧义、参数公平性和画图问题

4.1 缩写陷阱:同一个SSA可能完全不是同一个算法

这个坑我在前面版本认定已经提过,但还是值得单独立一条:无论你从哪下载代码,第一步不是跑,也不是欣赏,而是翻主循环。我吃过的亏是,曾经把樽海鞘代码误标成麻雀,论文初稿被导师当场指出来,场面相当尴尬。

判断方式再总结一下:

  • SSA:看主循环有没有“leader持续更新、follower依次链式跟随”的特征,有就是樽海鞘;看有没有生产者、跟随者、警戒者三类角色,有就是麻雀。
  • CSA:找代号“AP”的察觉概率参数,以及有没有单独的“记忆矩阵”。有就是乌鸦。
  • TSO:找有没有喷水推进相关的数学表达式、邻域社交更新,有就是被囊群;找有没有类似WOA的螺旋搜索,有就是金枪鱼群。
  • CGO:看代码里有没有随机迭代生成Sierpinski三角形点序列的逻辑,有就是混沌博弈优化。

4.2 参数公平性:新人最容易翻车的地方

参数公平性是审稿人最喜欢抓的一个点。很多入门者做对比实验时,把PSO的w、c1、c2和HHO的一堆参数直接拿来跑,结果PSO表现不佳,于是得出“新算法远优于PSO”的结论。问题是,PSO没有经过任何调整,而HHO本身就是个多策略复杂算法,这种对比天然不公平。

公平做法有两种。第一种是全部用原始论文推荐参数,谁都不用调,然后在论文里写明“所有算法参数均按原文默认设置”。第二种是对每个算法做一轮独立的适度调参,把调好的参数写进论文附录。不管是哪种,都不能让部分算法被精调、部分算法拿到裸默认参数。我实验里PSO固定w=0.9,c1=c2=2;GWO用默认a线性递减;WOA用默认的b=1和螺旋概率0.5,这些都要在代码注释里写明白,方便别人复现。

4.3 收敛曲线的画法陷阱

很多期刊的审稿人对收敛曲线相当敏感,稍有不严谨就会被质疑。

只画单次运行曲线是最常见的问题。任何算法带上“随机”两个字,单次运行的收敛曲线都像彩票,起伏完全无法代表算法真实水平。要画就画30次运行的平均曲线,经费充足的可以对曲线做窗口平滑。只画一条线也是问题,把9种算法塞进同一张图虽然壮观,但线一多必然彼此遮挡,期刊排版根本看不清。建议每个函数单独出图,或者最多分三组对比。

另一个坑是“曲线美但数值没意义”。有些算法在目标函数值降到1e-200这种极低水平时,曲线看起来非常漂亮,但浮点精度早已无法区分真实误差。此时就应该在纵轴上做截断,或者直接说明“达到机器精度下限”。不要为了图好看硬把曲线拉到最小值,审稿人不傻,数据异常一眼就能看出来。

4.4 复现别人代码的7条实用建议

以下七条是我反复踩坑后总结的经验,每一条都能帮你节省至少半天时间:

  1. 拿到代码先固定随机种子。Matlab里用rng(seed, 'twister'),让实验结果可复现。
  2. 所有算法共用一个目标函数句柄,不要每个算法内部重写一份目标函数。
  3. 第一次跑先做冒烟测试:维度降到5,迭代次数降到50,确认程序能跑通再上正式参数。
  4. 统一边界处理方式,我建议clamp截断。
  5. 记录收敛值时要保存每代全局最优,不是当代种群平均。
  6. 使用工作区之前尽可能不用全局变量。多个算法在同一个脚本里跑,上一个算法的临时变量没清理容易干扰下一个。
  7. 把结果自动保存成.mat文件,免得跑完30次后Matlab崩溃丢掉全部数据。

5. 实验做完怎么选算法:从工程和论文两个角度聊

5.1 工程快速优化:选择简单且稳的算法

如果是在实际工程里用,不在乎算法是不是新发表,只在乎能不能快速集成、稳定复现,我的建议是PSO和GWO优先。PSO的好处是几乎所有编程语言都有现成实现,参数少,行为透明;GWO的好处是几乎不用调参,适合不想在参数上花时间的人。这两个算法在中等维度、目标函数平滑的场景下足够好用。

WOA和HHO要做实际工程也不是不行,但你需要对它们的收敛机制有把握,尤其是HHO策略切换多,调参成本高。如果项目周期紧,我不会冒险选这种需要仔细磨合的算法做第一版。

5.2 论文需要新算法:HHO和CGO的价值

写论文时,情况反过来了。经典算法PSO、GWO作为基线是必须的,但如果你所有对比算法都是十几年前的“老面孔”,创新点可能不够亮眼。这时加入HHO、CGO这类较新的算法,会让实验设计显得更贴近前沿。

需要额外注意的是,新算法在别人论文里效果好,通常是因为作者在测试集上调过参数。你直接把代码搬过来跑,效果不一定理想。如果HHO在你选定的函数上表现不佳,不要急着删掉它,试着调整逃逸能量公式里的随机因子范围,这可能就会改变收敛曲线走势。

5.3 多样性与局部最优:SCA、SSA的可组合性

很多做改进算法的人喜欢把两种算法混在一起,比如“SCA+PSO混合算法”。SCA早期振幅大、探索强,正好可以弥补PSO早熟的毛病;SSA的链式结构配合CSA的记忆机制,也有不少论文尝试过。如果你准备做“融合改进”方向的课题,SCA和SSA是很好的“混合底座”,因为它们的更新公式结构相对灵活,容易塞入别的算法机制。

但混合改进不是“谁好拼谁”。每个算法有自己的参数体系,硬拼会产生大量需要调的新参数,容易变成调参大战。建议混合时只保留两个各自最核心的策略,把其余部分砍掉,控制模型复杂度。

5.4 记着做统计检验:Wilcoxon秩和检验

对比实验如果只给表格和曲线,还不够扎实。算法在30次运行中的平均值看起来更好,也不代表统计意义上真的更好。学术界常规操作是两两比较时用Wilcoxon秩和检验,零假设是两个算法性能无显著差异,计算出的p值如果小于0.05,才能说“算法A在这个函数上显著优于算法B”。

Matlab里可以用ranksum函数,两两对比,生成一个9×9的p值矩阵。输出后把显著优于对手的数量加总,得到每个算法的总胜出次数,这才是评价综合性能的可靠依据,远比单看Mean值谁小更有说服力。这也是为什么前面坚持要做30次独立运行,样本太少检验就没有意义。

这套9种算法对比的Matlab工程,到现在已经帮我在三个项目里完成对比实验了。我个人最大的体会是,算法比较真正考验的不是算法本身,而是你的工程控制能力:版本认定、参数公平、随机种子、统计口径,任何一环出了纰漏,审稿人一眼就能看穿。与其到处下代码堆数量,不如把每个算法的机理弄透,再想想它在你的测试函数上为什么会有这个表现,这样写进论文里的分析才有深度。如果你正准备搭一套对比实验,建议从这份框架做起,先固定3种算法,跑通全流程,再逐步加满9种。数据结构对了,后面换算法就是十几分钟的事。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询