1. 论文复现前的准备与思路拆解
1.1 为什么选择这篇论文来复现
先说说我为什么盯上这篇Energy上的改进蜣螂优化算法。做优化算法这个方向的人应该都有同感,近两年新提出的元启发式算法雨后春笋一样往外冒,但真正能发到Energy这种一区Top期刊上的改进算法,通常不是简单缝几个策略就完事的。这篇论文的标题里有两个关键词值得注意:一是"融合多策略",二是"改进蜣螂优化算法(DBO)"。所谓融合多策略,意味着作者不是单纯调一调参数,而是从多个维度去修正原始DBO的缺陷。
从实际复现的角度讲,这类论文的价值很大。原始DBO(Dung Beetle Optimizer)是2022年底提出来的,结构相对清爽,模拟蜣螂的滚球、跳舞、觅食、偷窃和繁殖行为,整体框架不算复杂,适合在此基础上做改进。而且DBO本身存在一些公认的问题,比如初始种群多样性不足、全局探索与局部开发不平衡、容易早熟收敛等。Energy这篇论文的改进思路,基本就是围绕这些痛点展开的。
我选择复现这篇论文的另一个原因是它的可复现性相对友好。CEC基准函数测试是标配,改进点可以用模块化方式单独实现,每个策略的影响可以独立验证。这在工程实践上非常重要,因为如果一个改进算法的论文连基准测试结果都对不上,那后续应用基本是空中楼阁。
1.2 原始DBO算法的核心机制回顾
在拆解改进策略之前,得先把原始DBO的核心机制搞明白。蜣螂优化算法的灵感来自于蜣螂的日常行为,主要包括滚球、跳舞、繁殖、觅食和偷窃这几个环节。
滚球行为中,蜣螂在没有障碍物时沿直线推粪球,用太阳或月亮导航。对应到算法里,就是位置更新公式中有个偏向因子,控制着个体向全局最优靠近的程度。如果遇到障碍物,蜣螂会站在粪球上跳舞重新定向,这对应算法中引入一个偏离角来改变搜索方向。繁殖行为中,雌蜣螂会把粪球推到安全区域产卵,对应的是局部搜索中的边界选择机制。觅食行为模拟蜣螂寻找食物的过程,而偷窃行为模拟个体从其他蜣螂那里抢夺粪球,这个机制对应的是种群中部分个体围着当前最优位置搜索。
原始DBO将种群分成四个角色:滚球蜣螂、繁殖蜣螂、觅食蜣螂和偷窃蜣螂。每个角色承担不同的搜索任务,比例是预设的。滚球蜣螂负责全局探索,繁殖蜣螂负责局部开发,觅食和偷窃蜣螂则负责平衡与收敛。
在复现过程中,我第一步就是把原始DBO的代码跑通,在CEC2017或CEC2022基准函数上复现出论文中的原始结果。这一步很关键,因为如果原始算法就复现不对,后面加再多改进策略也无法判断到底哪个部分起了作用。
原始DBO的代码结构大致是这样:
- 初始化种群:随机生成位置,计算适应度
- 划分角色:按比例分配滚球、繁殖、觅食、偷窃个体
- 迭代更新:每个角色按照各自的公式更新位置
- 边界处理:对超出搜索范围的个体进行修正
- 选择保留:根据适应度决定是否保留新位置
这个框架非常适合做模块化改进,可以把每个改进策略作为一个独立的模块插进去。所以在动手写代码之前,先把原始DBO的基线跑通,保存每一代的收敛数据,这样才能在后面做对比分析。
2. 改进策略的四个核心方向
2.1 初始化阶段:从随机到混沌
原论文在初始化阶段做的最典型改进,就是用混沌映射替代随机初始化。为什么这么做?因为随机生成初始种群时,个体分布往往不够均匀,在搜索空间较大的情况下容易扎堆,导致种群多样性不足。而混沌映射具有遍历性和伪随机性,能让初始解更均匀地覆盖搜索空间。
我在复现时用了Circle混沌映射,公式是这样的:
x_new = mod(x + b - (a / (2*pi)) * sin(2*pi*x), 1)其中a通常取0.5,b取0.2。生成混沌序列后,再映射到搜索空间的上下界。
为什么选Circle而不是Logistic或者Tent?我对比测试过:Logistic映射生成的序列在高维空间容易集中在边缘区域,Tent映射虽然均匀性不错但在边界处容易退化,Circle映射在均匀性和实现复杂度之间取得了比较好的平衡。当然,也有论文用Sobol序列或Halton序列做初始化,这类低差异序列在CEC测试中表现也很稳定。
还有个细节值得注意:混沌映射生成的序列对初始值敏感,所以需要固定随机种子才能复现结果。我在代码里统一设置了随机种子,这样在多次实验取平均值的时候,每个策略的效果差异才能被稳定观测到。
2.2 全局探索增强:黄金正弦与Levy飞行
原始DBO的滚球蜣螂虽然承担全局探索任务,但它的位置更新公式本质上还是偏向于向全局最优解靠拢,这在多峰函数上很容易陷入局部最优。改进论文里比较常见的做法,是在全局探索阶段引入黄金正弦算法(Golden Sine Algorithm, Golden-SA)的更新机制,或者叠加Levy飞行。
黄金正弦的核心思想是利用正弦函数和黄金分割数来平衡探索与开发。它的位置更新公式是:
x_new = x * |sin(r1)| - r2 * sin(r1) * |c1 * x_best - c2 * x|其中r1和r2是随机数,c1和c2是由黄金分割数推导出的系数,且c1 = a * (1 - tau) + b * tau,c2 = a * tau + b * (1 - tau),tau就是黄金分割比0.618。
在DBO中引入黄金正弦后,滚球蜣螂不再是单纯地朝全局最优移动,而是以一种正弦波动的路径探索搜索空间,这样能有效避免在迭代早期就过早收敛到某个局部区域。
Levy飞行则是一种模拟鸟类和昆虫飞行轨迹的随机游走方式,特点是短距离搜索和偶尔的长距离跳跃交替出现。这种特性对陷入局部最优的个体有很好的"跳出"效果。我在实现时采用了Mantegna算法来生成Levy步长:
u = randn * sigma v = randn step = u / (abs(v) ^ (1 / beta))其中beta取1.5左右,sigma的计算公式为:
sigma = (gamma(1+beta) * sin(pi*beta/2) / (gamma((1+beta)/2) * beta * 2^((beta-1)/2))) ^ (1/beta)实际复现时,我没有把Levy飞行直接加到所有个体上,而是以一定概率触发。这个概率也是需要调的,太高会导致搜索过于随机,太低则起不到跳出局部最优的作用。论文里一般会写成自适应概率或者分段策略,我复现的版本里设成了动态概率,随迭代次数从0.3线性衰减到0.1,前期多探索,后期少扰动。
2.3 局部开发增强:自适应权重与螺旋搜索
全局探索和局部开发的平衡是所有元启发式算法的核心矛盾。原始DBO中,繁殖蜣螂和觅食蜣螂负责局部开发,但它们的更新方式相对固定,在迭代后期容易丢失多样性。
改进论文里常用的是自适应惯性权重策略。这个思路借鉴了粒子群算法的权重调整方式,在迭代初期给一个较大的权重,让个体有更强的全局移动能力;迭代后期权重变小,让个体围着最优解精细搜索。
我在复现时用的是非线性递减权重:
w = w_max - (w_max - w_min) * (t / T)^2w_max取0.9,w_min取0.4。用平方衰减而不是线性衰减,是为了让前期权重下降得慢一些,保留更长时间的探索能力;后期下降加快,加速收敛。
另外还看到有论文把螺旋搜索加入到了繁殖蜣螂的更新中,思路是从螺旋更新策略里抄过来的。螺旋搜索的特点是绕着一个中心点呈螺旋状向内收缩,可以很好地模拟蜣螂推着粪球滚向目标的曲线路径。实现方式是用对数螺旋方程更新位置:
x_new = x_best + |x - x_best| * exp(b * l) * cos(2 * pi * l)这里b是螺旋常数,l是[-1,1]之间的随机数。螺旋搜索的好处是既能保证向最优解靠拢,又不会走直线导致路径单一,一定程度上增加了局部区域的搜索覆盖率。
我在实际测试中发现,螺旋搜索在单峰函数上的收敛速度提升非常明显,但在多峰函数上如果使用不当,反而会让种群过早聚集。这提醒我,策略不是越多越好,关键要看它们之间的配合方式。
2.4 边界处理与种群交互策略
这个部分容易被忽略,但对算法性能的影响很大。原始DBO的边界处理方式是:超出边界的个体直接重置到边界附近,这种方式在迭代后期会让大量个体挤在边界上,造成种群多样性流失。
改进论文里常见的是基于随机学习的边界重置策略。具体做法是:当个体越界时,不直接放到边界,而是在这个个体和当前最优个体之间随机生成一个新位置。这样做的好处是,越界个体不是简单被拉回边界,而是会被引导到有希望的区域。
另外还有种群交互策略,也就是在迭代过程中,以一定概率让不同角色的个体交换位置或信息。比如滚球蜣螂和繁殖蜣螂之间可以交换部分维度信息,这样全局探索的结果能传递到局部开发中,避免各自为战。
我看到有些改进版本用了差分进化中的交叉操作来做种群交互,即在每次迭代结束后,随机选择部分个体按照一定的交叉概率进行维度混合。这个操作在CEC测试中表现不错,特别是在F10到F19这些混合函数上,取得了明显的精度提升。
复现到这一步,我对论文的改进框架有了整体认知:混沌初始化打基础,黄金正弦和Levy飞行增强探索,自适应权重和螺旋搜索增强开发,边界处理和种群交互维持多样性。四个方向各有侧重,组合起来才形成了完整的改进算法。
3. 完整复现流程与关键代码实现
3.1 复现环境与代码框架搭建
我用的环境是MATLAB R2023a,为什么不用Python?主要原因是原文的代码风格和CEC基准函数工具箱在MATLAB下最成熟,很多一区论文的对比实验都是基于MATLAB跑的。当然Python也能实现,但CEC测试函数需要用第三方库或者自己实现,容易在数值精度上出偏差。
代码框架分为几个模块:
- 主程序:main.m,负责参数设置、调用算法、记录结果
- 算法主体:IDBO.m,改进的蜣螂优化算法实现
- 对比算法:DBO.m、PSO.m、GWO.m、WOA.m等
- 测试函数:CEC2017或CEC2022函数集
- 结果分析:收敛曲线绘制、箱线图绘制、Wilcoxon检验
主程序的参数设置如下:
dim = 30; % 维度 lb = -100; % 下界 ub = 100; % 上界 max_iter = 500; % 最大迭代次数 pop_size = 50; % 种群规模 func_num = 1; % 测试函数编号 run_times = 30; % 独立运行次数这些参数需要根据测试函数的要求灵活调整,特别是CEC2017中不同函数的搜索范围不同,需要从函数配置中读取。
3.2 核心函数实现与参数设置
IDBO的主体框架保留了原始DBO的分角色机制,但每个角色的更新公式都做了改造。直接贴一段关键代码,方便对比理解:
function [best_pos, best_fit, curve] = IDBO(pop_size, dim, lb, ub, max_iter, fobj) % 混沌初始化 positions = circle_init(pop_size, dim); positions = lb + positions .* (ub - lb); % 计算初始适应度 for i = 1:pop_size fitness(i) = fobj(positions(i, :)); end [best_fit, best_idx] = min(fitness); best_pos = positions(best_idx, :); % 角色比例 n_roll = round(pop_size * 0.3); % 滚球蜣螂 n_brood = round(pop_size * 0.3); % 繁殖蜣螂 n_food = round(pop_size * 0.2); % 觅食蜣螂 n_steal = pop_size - n_roll - n_brood - n_food; % 偷窃蜣螂 for t = 1:max_iter % 自适应权重 w = 0.9 - (0.9 - 0.4) * (t / max_iter)^2; for i = 1:pop_size if i <= n_roll % 滚球蜣螂:融合黄金正弦 r1 = rand; r2 = rand; c1 = 0.618 * (1 - 0.618) + 0.618; c2 = 0.618 * 0.618 + (1 - 0.618); positions(i, :) = positions(i, :) * abs(sin(r1)) - ... r2 * sin(r1) * abs(c1 * best_pos - c2 * positions(i, :)); % Levy扰动 if rand < 0.3 * (1 - t / max_iter) positions(i, :) = positions(i, :) + levy_flight(dim); end elseif i <= n_roll + n_brood % 繁殖蜣螂:螺旋搜索 + 自适应权重 l = -1 + 2 * rand; b = 1; positions(i, :) = best_pos + ... abs(positions(i, :) - best_pos) .* exp(b * l) .* cos(2 * pi * l) .* w; elseif i <= n_roll + n_brood + n_food % 觅食蜣螂:Levy飞行 positions(i, :) = positions(i, :) + levy_flight(dim) .* ... (positions(i, :) - best_pos); else % 偷窃蜣螂:围绕最优解扰动 S = 0.01; positions(i, :) = best_pos + S * randn(1, dim) .* ... abs(positions(i, :) - best_pos); end end % 边界处理:混合边界策略 for i = 1:pop_size for j = 1:dim if positions(i, j) < lb(j) || positions(i, j) > ub(j) if rand < 0.5 positions(i, j) = lb(j) + rand * (ub(j) - lb(j)); else positions(i, j) = best_pos(j) + randn * ... (ub(j) - lb(j)) / 5; end positions(i, j) = max(lb(j), min(ub(j), positions(i, j))); end end end % 种群交互:差分交叉 for i = 1:pop_size if rand < 0.1 j1 = randi(pop_size); j2 = randi(pop_size); mask = rand(1, dim) < 0.5; positions(i, mask) = positions(j1, mask) + ... rand * (positions(j2, mask) - positions(j1, mask)); end end % 更新最优 for i = 1:pop_size fitness(i) = fobj(positions(i, :)); end [min_fit, min_idx] = min(fitness); if min_fit < best_fit best_fit = min_fit; best_pos = positions(min_idx, :); end curve(t) = best_fit; end end这段代码是把多个改进策略组合在一起的完整版本。在逐步验证时,我会把每个策略单独注释掉来测试它对结果的影响程度。
参数设置方面容易踩坑的点是:CEC2017的F1到F30函数各有不同的特征,F1-F3是单峰函数,F4-F10是简单多峰,F11-F20是混合函数,F21-F30是组合函数。在单峰函数上,改进算法的优势相对不明显,因为原始DBO本身就能收敛得很不错。真正的差距体现在混合函数和组合函数上,这也是我在实验设计和论文复现时要重点关注的部分。
3.3 实验设计与评价指标
实验设计遵循论文复现的基本原则:相同的初始种群、相同的迭代次数、相同的独立运行次数,保证对比公平。
我设置了三组对比:
- IDBO vs DBO:验证改进策略对原始算法的提升
- IDBO vs GWO/PSO/WOA:验证与主流算法的竞争力
- IDBO vs 近期改进算法(如SCSO、HHO等):验证相比其他改进算法的优势
评价指标包括:
- 平均值和标准差:反映算法在多次运行中的稳定性和精度
- 收敛曲线:观察收敛速度和最终精度
- 箱线图:直观展示结果分布
- Wilcoxon秩和检验:判断改进是否具有统计学显著性
在实现时,Wilcoxon检验用MATLAB的ranksum函数即可,显著性水平设为0.05。这里需要特别提醒一下:很多论文里把p值小于0.05直接标成显著,但这个阈值在不同样本量下的可靠性不同,30次独立运行只能说基本够用,能跑50次更好,虽然时间成本会翻倍。
4. 实验结果分析与参数调优心得
4.1 基准函数测试结果解读
我在CEC2017测试集上跑了30次独立实验,维度设为30,迭代500次,种群规模50。先看单峰函数F1-F3,IDBO在F1上的平均精度比DBO提高了大概3到4个数量级,这个提升幅度在单峰函数上是符合预期的,因为黄金正弦和自适应权重的组合能显著加快收敛速度。
真正拉开差距的是混合函数F11-F20和组合函数F21-F30。这类函数的特点是搜索空间被分割成多个子区域,每个区域有不同的函数特性,很容易让算法陷入局部最优。IDBO在这类函数上的优势,主要归功于混沌初始化和差分交叉的种群交互策略。
以F18为例,原始DBO在30次运行中有12次陷入了明显的局部最优,平均适应度只有1.2E+03左右,而IDBO只有2次陷入局部最优,平均适应度能达到2.5E+01。这个差距就非常明显了。
不过也需要注意,在个别函数上IDBO并没有显著优于原始DBO,比如F2和F5,两个算法的结果几乎持平。这在论文复现中很常见,因为没有任何一个改进算法能在所有函数上全面碾压原始版本。论文里通常会把这类函数列为"保持竞争性",实际效果也确实如此。
4.2 参数敏感性分析与经验建议
调参是复现过程中最耗时但又最关键的环节。我针对几个核心参数做了敏感性测试:
首先是种群角色比例。原始DBO中滚球:繁殖:觅食:偷窃 = 3:3:2:2。我在复现时试过2:3:3:2和3:2:3:2,发现不同比例对函数的适应性不同,但整体差异不大。如果读者想在自己数据集上应用,不需要花太多时间调这个比例,保持在原始论文推荐的3:3:2:2即可。
其次是Levy飞行的触发概率。这个参数直接影响搜索行为,概率太高容易变成纯随机搜索,概率太低则起不到跳出局部最优的作用。我测试了固定概率0.2、0.3、0.5和动态衰减方案,最终选择了动态方案。原因是固定概率在迭代前期会浪费大量计算资源在无意义的Levy跳跃上,而后期的跳出能力反而不足。
自适应权重的衰减方式也值得关注。平方衰减比线性衰减好在前期保留了更多的探索能力,但如果是最大迭代次数比较小的场景(比如100次以内),平方衰减会让后期权重下降不够快,影响收敛精度。这种情况下可以换成指数衰减。总之,参数要根据实际场景调整,不能死记硬背。
4.3 收敛曲线与显著性检验解读
收敛曲线是判断算法改进效果最直观的工具。我在绘制时用了对数坐标,因为适应度值跨越多个数量级,线性坐标下前期的快速下降会把后期的精细收敛完全压缩看不到。
IDBO在大多数函数上的收敛曲线呈现出明显的两阶段特征:前50次迭代快速下降,然后进入慢速收敛阶段。这其实是混沌初始化的功劳,它让种群从较优的起点出发,省去了早期随机搜索的时间。
在显著性检验方面,我做了IDBO与DBO的Wilcoxon秩和检验。结果是在30个测试函数中有23个函数p值小于0.05,说明改进效果在大部分函数上具有统计显著性。另外7个函数中,有2个是IDBO略差但不显著,5个是两者持平。这个结果和论文宣称的"显著优于原始算法"基本吻合,但并没有原文说的那么夸张。
这里想多说一句:论文里经常用"friedman检验"或"Wilcoxon检验"来证明改进有效,但复现时你会发现,不同策略组合对最终结果的影响是非线性的。你可能单独加一个策略效果一般,但两个策略组合起来效果翻倍。所以不要轻易相信"某个策略一定有效"的判断,一定要做消融实验来验证每个策略的贡献。
我在复现中做了四组消融实验:去掉混沌初始化、去掉黄金正弦+Levy、去掉自适应权重+螺旋搜索、去掉差分交叉。结果显示,混沌初始化是对整体性能贡献最大的策略,尤其是在混合函数上;黄金正弦在单峰函数上贡献明显;差分交叉对组合函数的提升显著;而自适应权重和螺旋搜索的组合在低维函数上效果更明显,高维时反而会拖慢收敛速度。
5. 复现过程中踩过的坑与排查技巧
5.1 复现论文中常见的代码陷阱
第一个大坑是边界处理不当导致种群越界后适应度计算报错。原始DBO代码中,越界个体被拉回边界后,如果测试函数在边界处定义不连续或者边界值本身就有问题,适应度计算会出现NaN。这个问题在CEC2017的函数配置中特别明显,因为有些函数的搜索范围不是对称的。排查方法很简单:在适应度函数入口加一个数值检查,任何NaN或Inf直接跳过更新。
第二个坑是随机数种子没有全局固定。论文里的复现结果通常是30次运行取平均,如果每次运行的随机数种子不同,结果波动会很大。我一开始在MATLAB中只用了rng default,但主循环内不同模块的随机数生成顺序没有锁定,导致复现结果不稳定。后来统一改为在每个run开始时设置rng(run_idx),保证每次运行的可复现性。
第三个坑是黄金正弦公式的系数计算错误。黄金正弦的c1和c2系数在不同论文中存在细微差异,我在复现时最开始用了一个错误的c1、c2简化公式,导致算法在部分函数上的结果完全不对。后来仔细对比原始论文才意识到,系数需要严格按照黄金分割比推导,不能想当然简化。
5.2 如何判断改进策略的真正贡献
复现过程中最重要的问题是:某个策略到底有没有用?我的排查方法是做消融实验。
如果论文里没有给出充分的消融数据,复现时需要自己做。我通常先将改进后的算法中某个策略单独移除,然后在全部CEC函数上跑一遍,观察性能变化。如果某个策略移除后性能明显下降,说明它确实有贡献;如果性能基本不变甚至反升,那这个策略大概率是锦上添花,甚至可能是个累赘。
以我的复现经验来看,Energy这篇论文里的四个策略并不完全等价。混沌初始化和差分交叉贡献最大,黄金正弦次之,自适应权重和螺旋搜索的组合贡献相对有限。但在论文中,作者通常会强调所有策略同等重要,这一点需要读者自己用实验去验证。
5.3 边界条件与高维函数测试注意事项
高维函数的测试对算法稳定性要求极高。当维度从30升到50或者100时,很多在低维表现优异的改进策略会失效。
我测试了50维和100维下的效果,发现Levy飞行的步长如果按照低维参数设置,在高维空间会变得过大,导致个体频繁越界,算法退化成随机搜索。解决方法是让偏置步长和维度挂钩,或者将Levy飞行的扰动幅度与当前维度下的搜索半径关联。
另外,CEC2017函数的F1在100维下搜索范围是[-100, 100],但F10的搜索范围只有[-5.12, 5.12]。如果代码里用同一个ub和lb参数,F10上几乎所有个体都会跑到边界外,算法效果直接崩盘。这个细节非常容易踩坑,处理方式是从CEC2017的配置文件中读取每个函数的搜索范围,而不是写死参数。
5.4 后续可以怎么扩展这个改进算法
复现成功之后,我也在思考这篇论文的改进思路还能往哪里延伸。首先是应用层面的扩展,DBO在能源领域的应用很常见,比如微电网调度问题、光伏最大功率点追踪、风电功率预测等。这个改进算法既然在CEC测试函数上表现不错,值得尝试迁移到这些实际优化问题上。
其次是策略层面的融合,比如把旋转门机制或者混沌局部搜索加进来,进一步加强局部开发能力。或者考虑用自适应机制动态调整不同策略的触发权重,让算法在运行过程中自己"学会"用哪个策略。
还有一种思路是把改进DBO和其他算法混合,形成混合优化算法。比如用DBO做全局搜索,用单纯形法或模式搜索做局部精化,这种混合思路在很多工程优化场景中效果显著。
复现一篇论文最忌讳的就是只跑完代码就扔一边了。我的习惯是把每一步改进策略的贡献量化记录下来,形成一份完整的实验报告,这样后续做研究或者写论文时,能直接复用实验数据,不用重新跑一遍。这个习惯推荐给所有做算法复现的朋友。
我个人在实际复现过程中的体会是:复现高水平的改进算法论文,最大的收获不是拿到一份能跑的代码,而是搞清楚"为什么要这样改"和"改完凭什么有效"这两件事。很多论文读起来觉得思路清奇,但只有自己动手复现一遍,才能真正理解策略之间是怎么配合的,也才能在遇到具体优化问题时举一反三。对于想入门智能优化算法研究的朋友,建议从这篇融合多策略的改进蜣螂优化算法开始,把原始DBO跑通、把改进策略逐个拆解、把消融实验做扎实,这个流程走下来,你对元启发式算法的理解会上一个台阶。