
简介斑马算法的MATLAB实现涵盖算法初始化、主循环、目标函数评估与结果可视化等完整模块适合需要求解多峰值或非线性复杂优化问题的研究人员、工程师及本科及以上学生直接使用或二次开发。该算法模拟斑马种群的社会等级与运动模式在收敛速度和计算开销上具有较好平衡。压缩包共12个文件内含8个m脚本、3张效果图片与1个asv备份文件整体仅56KB轻量易部署。主要函数功能明确初始化文件负责建立斑马种群的位置、速度等参数主文件控制迭代更新与最优解记录目标函数文件支持多种测试函数验证寻优能力绘图脚本用于绘制适应度收敛曲线便于直观评估算法稳定性。代码包含测试数据与详细注释可结合参数反演、函数寻优等场景改造扩展也可作为算法对比或教学演示基础。已有108人学习下载适合作为群体智能优化算法的入门参考或教学示例。1. 斑马算法是什么为什么值得用MATLAB复现斑马算法Zebra Optimization Algorithm简称ZOA是2022年前后提出的群智能优化算法模拟斑马觅食和防御捕食者两类行为。它和粒子群、灰狼算法处在同一类框架里但结构上多了一个“防御阶段”把探索和开发拆得更明确。对MATLAB用户来说斑马算法的吸引力在于参数少、向量化容易核心更新公式只有两行却能在局部收敛速度和全局搜索能力之间做动态取舍。很多人把代码下载下来跑一遍发现结果和论文差距大问题通常不在算法本身而在边界修复、掩码向量生成或者测试预算设置上。这篇文章完整给出斑马算法的MATLAB实现并在标准基准函数上验证测试效果适合做优化算法复现、学术对比实验或工程参数寻优的人参考。读完可以直接把代码改成自己的目标函数。2. 斑马算法的两类搜索行为与MATLAB代码骨架2.1 觅食与防御ZOA的两阶段位置更新斑马算法的一次迭代由两个阶段组成每个阶段对应一种生物行为。第一阶段是觅食个体在“先锋斑马”的引导下向当前最优位置移动这是整个算法的主要开发手段。位置更新公式常见写法是X_new X r .* (P - A .* X)其中P表示当前种群最优个体的位置r是[0,1]均匀随机数A是随机生成的二进制掩码向量每个维度上以一定概率取0或1。掩码向量是把“跟随最优”和“保留原值”混合起来的关键。如果不加AX会被整体拉到P附近所有维度同步变化高维空间里容易出现“早熟收敛”加上A后一部分维度被最优位置替换另一部分维度保持原位置让个体在最优解邻域内仍有维度层面的探索空间。第二阶段模拟斑马防御捕食者。算法为每个个体生成一个概率p当p 0.5时采用逃跑策略斑马向全局最优方向移动当p 0.5时采用攻击策略从当前种群中随机挑选另一个体作为参照向它靠近。逃跑策略偏向开发攻击策略偏向探索。随机参照个体可以打破种群趋同性避免所有斑马都挤到同一个局部谷底。2.1.1 觅食阶段为什么用掩码向量掩码向量A的取值方式是rand(N, dim) 0.5也就是每个维度独立地以50%概率取0或1。这种设计保证个体在靠近最优解时不会所有维度同时发生大位移而是类似遗传算法的按位交叉。实际使用中可以把0.5换成0.7或0.3调节“跟随最优”的维度比例。掩码比例越大被最优位置影响的维度越多开发能力越强比例越小保留的个人信息越多探索能力越强。2.1.2 防御阶段的两个分支在平衡探索与开发从实现角度看第二阶段的逃跑分支和攻击分支共享同一个公式骨架只是参照个体Q不同。逃跑分支的Q取全局最优攻击分支的Q取种群随机个体。这样设计的好处是不需要额外的速度变量或惯性权重只靠分支选择就能实现搜索策略切换。要注意的是两个分支公式里的r1和r2应各自独立生成不能共用一个随机矩阵否则维度间的随机扰动相关性过强会削弱搜索方向多样性。2.2 MATLAB实现ZOA主循环的骨架下面是一个可直接运行的ZOA主函数输入目标函数句柄和边界条件输出最优位置、最优值以及每次迭代的收敛记录。function [best_x, best_f, conv] ZOA(fun, dim, lb, ub, N, MaxIter) % ZOA 斑马优化算法主体 % 输入 % fun - 目标函数句柄输入行向量返回标量 % dim - 决策变量维度 % lb,ub - 变量下界/上界可为标量或向量 % N - 斑马种群规模建议取 20~50 % MaxIter- 最大迭代次数 % 输出 % best_x - 全局最优位置 % best_f - 全局最优目标值 % conv - 每次迭代的最优值记录用于画收敛曲线 % 1. 均匀随机初始化 X lb rand(N, dim) .* (ub - lb); fit arrayfun((i) fun(X(i, :)), 1:N); % 记录初始最优 [best_f, idx] min(fit); best_x X(idx, :); conv zeros(MaxIter, 1); for t 1:MaxIter % ---------- 阶段一觅食 ---------- A rand(N, dim) 0.5; % 二进制掩码向量 r rand(N, dim); % 随机步长 forage X r .* (best_x - A .* X); forage bound_repair(forage, lb, ub); f_forage arrayfun((i) fun(forage(i, :)), 1:N); upd f_forage fit; X(upd, :) forage(upd, :); fit(upd) f_forage(upd); % ---------- 阶段二防御捕食者 ---------- p rand(N, 1); % 防御策略概率 A rand(N, dim) 0.5; r1 rand(N, dim); r2 rand(N, dim); Q zeros(N, dim); for i 1:N if p(i) 0.5 Q(i, :) best_x; % 逃跑向最优靠拢 else pool setdiff(1:N, i); % 攻击随机选参照个体 Q(i, :) X(pool(randi(length(pool))), :); end end defense X r1 .* (best_x - A .* X) r2 .* (Q - A .* X); defense bound_repair(defense, lb, ub); f_def arrayfun((i) fun(defense(i, :)), 1:N); upd f_def fit; X(upd, :) defense(upd, :); fit(upd) f_def(upd); % 更新全局最优与收敛记录 [best_f, idx] min(fit); best_x X(idx, :); conv(t) best_f; end end代码里每个阶段都先按公式生成候选位置然后用边界修复函数处理越界个体最后用贪婪策略决定是否接受新位置。整体没有慢速循环除了防御阶段里随机选个体的部分用了小循环其余都是矩阵运算在MATLAB里运行效率可以接受。需要提醒的是这里的边界修复函数bound_repair需要单独提供下面是一个最简单的钳位版本。若把越界个体直接压到边界会让边界处累积大量个体影响多样性。后面会给出更稳妥的修复方式。function Xb bound_repair(X, lb, ub) Xb max(min(X, ub), lb); end2.2.1 运行前要确认的输入参数主函数有六个输入量作用如下表所示。理解每个参数的含义是测试效果好坏的前提。参数常用范围作用与影响N20~60种群规模。过小容易早熟过大会显著增加每次迭代的计算量MaxIter100~1000迭代次数。高维测试函数建议不小于500dim10~50决策变量维度。维度越高对探索能力要求越高lb / ub视函数而定搜索边界。对无约束优化边界决定初始种群分布fun任意标量函数目标函数句柄必须支持行向量输入best_x-初始最优位置将参与第一阶段和逃跑分支的更新如果目标函数对行向量和列向量敏感建议在fun内部用x x(:);统一转成行向量避免因维度不匹配报错或出现错误输出。参数N和MaxIter直接决定函数评价次数也就是FESFunction Evaluations。做算法对比时必须保证不同算法的FES一致否则结果不具备可比性。3. 用标准测试函数跑通斑马算法的最小算例3.1 三个基准函数单峰、多峰、伪多峰要判断斑马算法的测试效果第一步不是直接上手工程问题而是先在标准基准函数上验证。给出三个有代表性的函数Sphere是单峰函数Rastrigin布满局部极小值Griewank则在高维空间中存在大量欺骗性局部结构。函数名表达式搜索范围理论最优值Spheresum(x.^2)[-100, 100]0Rastriginsum(x.^2 - 10cos(2pi*x) 10)[-5.12, 5.12]0Griewank1 sum(x.^2)/4000 - prod(cos(x./sqrt(1:numel(x))))[-600, 600]0其中Rastrigin函数在三维以上的空间里会形成密集的等间距波峰波谷是检验算法能否跳出局部最优的常客。Griewank在高维时局部极小值分布不像视觉上那么有规律适合检验算法在“看似连续但存在大量局部陷阱”的曲面上的表现。3.2 完整测试脚本与MATLAB调用方式把三个基准函数写成函数句柄再循环调用ZOA主函数观察不同函数下的最优值、收敛曲线和稳定性。下面的脚本可以直接复制到MATLAB里运行。clear; clc; rng(2024); % 定义测试函数 f_sphere (x) sum(x.^2); f_rast (x) sum(x.^2 - 10*cos(2*pi*x) 10); f_griew (x) 1 sum(x.^2)/4000 - prod(cos(x./sqrt(1:numel(x)))); % 统一参数 dim 30; MaxIter 500; N 40; % 函数列表配置 problems { f_sphere, -100, 100, Sphere; f_rast, -5.12, 5.12, Rastrigin; f_griew, -600, 600, Griewank }; for k 1:size(problems, 1) fun problems{k, 1}; lb problems{k, 2}; ub problems{k, 3}; name problems{k, 4}; [best_x, best_f, conv] ZOA(fun, dim, lb, ub, N, MaxIter); fprintf(%s: best_f %.6e\n, name, best_f); % 画收敛曲线 figure; semilogy(conv, LineWidth, 1.5); title([name, 收敛曲线]); xlabel(迭代次数); ylabel(最优目标值); grid on; end3.2.1 三个测试函数的适配说明在运行上面脚本之前要注意各函数搜索范围的差异。Sphere的范围是[-100,100]Rastrigin是[-5.12,5.12]Griewank是[-600,600]。维度相同范围不同初始种群覆盖的体积差别很大。范围大的函数更适合检验算法在稀疏搜索空间中的探索能力范围小的函数更容易出现边界附近个体扎堆的现象。建议三个函数分开设置lb和ub不要统一取[-100,100]否则Rastrigin会被边界严重压缩。另外rng(2024)的作用是固定随机数种子。元启发式算法本质是随机算法不固定种子每次运行结果都会不一样。写论文或做测试效果分析时必须在实验前固定随机种子或在记录结果时保存每次运行的种子值。3.2.2 边界修复方式的改进前面给出的bound_repair只做钳位长期运行会让边界附近积累大量个体。更推荐的两种方式如下第一种是随机反弹第二种是镜像反射。% 方式一随机反弹 function Xb bound_random(X, lb, ub) Xb X; mask (X lb) | (X ub); Xb(mask) lb (ub - lb) .* rand(sum(mask(:)), 1); end % 方式二镜像反射 function Xb bound_mirror(X, lb, ub) Xb X; Xb(Xb lb) 2 * lb - Xb(Xb lb); Xb(Xb ub) 2 * ub - Xb(Xb ub); end随机反弹的优点是让越界个体回到搜索空间内任意位置增加探索性缺点是最优解在边界附近时容易把它弹走。镜像反射能把越界个体映射回边界内侧同时又保持和边界的距离信息适合目标函数最优解恰好落在边界附近的情况。我一般会默认使用bound_random在工程问题中配合钳位一起用先镜像反射若仍越界再钳位。4. 测试效果怎么量化收敛曲线、寻优精度与对比实验4.1 多次独立运行的统计指标单次运行的最优值没有说服力。评估优化算法的测试效果至少要记录多次独立运行后的最小值、平均值、标准差和成功率。标准差越小说明算法对这个函数的求解越稳定平均最优值离理论最优越近说明精度和收敛能力越强。M 30; % 独立运行次数 F zeros(M, 1); for run 1:M [best_x, F(run), conv] ZOA(f_sphere, dim, -100, 100, 40, 500); end success_rate sum(F 1e-8) / M * 100; fprintf(min %.3e, mean %.3e, std %.3e, success %.1f%%\n, ... min(F), mean(F), std(F), success_rate);这段代码把30次运行的最优值单独存放之后可以计算出统计指标。成功率以F小于1e-8为阈值实际使用时可以根据问题精度要求调整。Sphere最优值是0取1e-8作为成功标准比较合理对Rastrigin或Griewank若想观察是否收敛到全局最优可以把阈值放宽松到1e-5。统计结果最好保存到Excel或MAT文件里方便后续画箱线图和做显著性分析。保存方式也比较简单save(zoa_result.mat, F, conv)即可。4.2 与经典算法的公平对比函数评价次数是唯一标尺斑马算法测试效果好不好只看收敛曲线不够还要和常用算法对比。常见的对比对象有粒子群算法PSO、灰狼算法GWO和鲸鱼算法WOA。这些算法迭代方式不同不能直接按代数比要比函数评价次数FES。FES N × MaxIter。下面给出一个统一评价次数后的对比逻辑。% 以FES20000为例不同算法用各自种群和迭代次数组合 FES 20000; N1 40; MaxIter1 FES / N1; N2 50; MaxIter2 FES / N2; % 在相同FES下运行ZOA和PSO并记录最优值 [~, F_zoa, ~] ZOA(f_rast, dim, -5.12, 5.12, N1, MaxIter1); [~, F_pso, ~] PSO(f_rast, dim, -5.12, 5.12, N2, MaxIter2); fprintf(ZOA: %.3e, PSO: %.3e\n, F_zoa, F_pso);注意这里PSO需要额外实现速度更新和惯性权重参数一般设置为w0.729c1c21.494这个组合是经典收敛性配置。对比实验至少要运行30次取平均值、标准差和箱线图单次结果不能说明任何问题。4.2.1 参数设置会给对比带来什么偏差对比实验里最容易被问倒的问题就是参数来源。ZOA只有N和MaxIter两个控制参数GWO也几乎没有额外参数而PSO需要调惯性权重、个体学习因子和社会学习因子。如果PSO的参数设置不恰当即使ZOA赢了也不能证明算法本身更好。为避免这种质疑可以优先和参数同样少的GWO或WOA对比若必须与PSO对比需要在文中明确写出PSO的参数设置值并引用经典文献。另外所有算法应使用相同的边界修复方式不能ZOA用随机反弹而PSO用钳位。4.3 收敛曲线的画法与判读MATLAB里画收敛曲线的最常用命令是semilogy因为目标函数值往往跨越多个数量级。如果直接画线性坐标早期下降过程会被压缩。绘制多条曲线对比时要区分颜色和线型并设置图例。% 绘制ZOA、GWO、PSO三条收敛曲线于同一张图 iters 1:500; figure; semilogy(iters, conv_zoa, b, LineWidth, 1.5); hold on; semilogy(iters, conv_gwo, r--, LineWidth, 1.5); semilogy(iters, conv_pso, k-., LineWidth, 1.5); xlabel(迭代次数); ylabel(最优目标值); legend(ZOA, GWO, PSO, Location, northeast); set(gca, YScale, log); grid on;判读收敛曲线有三个要点。第一看前期的下降斜率斜率大说明开发能力强第二看中后期是否存在长时间平台期平台长且位置较高说明陷入局部最优第三看曲线末尾是否还在下降如果末端仍有明显下降趋势说明迭代次数不够可以延长MaxIter或增大种群规模。更严谨的做法是画出30次运行的平均收敛曲线和标准差阴影带使用fill函数实现。阴影带越小算法稳定性越强。5. 参数敏感性、常见陷阱与斑马算法进阶改型5.1 三个影响测试效果的关键设置斑马算法整体参数少但仍有几个容易被忽略的设置点。第一个是掩码向量A的生成概率代码里默认用小于0.5作为阈值直接把0.5改成0.7相当于让更多维度跟随最优收敛会加快但探索能力会下降。第二个是是否使用贪婪选择。若把upd f_forage fit改成无条件更新会给种群带来更大波动适合多峰函数但单峰函数上会拖慢收敛。第三个是边界修复方式前文提过随机反弹和镜像反射的适用场景建议在同一个测试集上统一用一种方式否则不同算法之间会引入额外变量。还有一个常见陷阱是目标函数内部写了disp打印语句。当FES达到几万次时MATLAB的控制台打印会严重拖慢循环。可以把打印语句注释掉或改用if mod(fes, 1000) 0条件输出。5.2 两个低成本改进方向直接改公式就能见效的改进有两个。第一个是给觅食阶段引入线性递减惯性权重模仿粒子群的做法让前期多探索、后期多开发。% 在ZOA主循环内部加入权重递减 w 0.9 - 0.5 * (t / MaxIter); forage X w .* r .* (best_x - A .* X);第二个是把ZOA和差分进化做简单混合每迭代一定代数后对种群做一次DE变异变异个体如果适应度更优就替换原个体。这个操作成本低但能显著改善多峰函数上的测试效果。% 每10代执行一次DE/best/1变异 if mod(t, 10) 0 mutant best_x 0.5 * (X(randperm(N,1),:) - X(randperm(N,1),:)); for i 1:N trial mutant(i, :); if fun(trial) fit(i) X(i, :) trial; fit(i) fun(trial); end end end做改进实验时要保留原版ZOA作为baseline控制变量只改一处并在相同FES下比较30次结果这样得出的结论才有说服力。改进方向能否落地最终还是要回到目标函数上验证建议先用低维、中维、高维三组同函数跑完再替换成自己的工程目标函数例如MATLAB优化工具箱里常见的约束问题。这样得到的测试效果才完整且可复现。本文还有配套的精品资源点击获取