直说结论SCA这套代码我建议每个做优化算法对比、写毕业设计或者搞“新方法验证”的人都自己动手跑一遍。正弦余弦优化算法Sine Cosine AlgorithmSCA的原理简单到可以写在一张PPT上但它的搜索行为、参数敏感性、以及在不同基准测试函数上的表现差异比很多复杂算法更能说明问题。我这次在Matlab里把SCA完整实现了一遍并且在23个常用基准测试函数上分别做了30次独立实验下面把核心思路、完整代码、运行数据和踩坑记录一次讲清楚。不管你是刚接触元启发式算法的新手还是想拿SCA当对比算法的老手这篇文章都能直接帮你省掉调试时间。1. SCA算法核心原理四个随机参数如何控制搜索行为1.1 从三角公式到“算法”SCA是由Seyedali Mirjalili在2016年提出的一种元启发式优化算法。它最吸引人的地方在于整个位置更新机制只依赖一个高中数学公式正弦函数和余弦函数。种群中的每个个体每一轮迭代都按照下面的规则更新自己的位置X(t1) X(t) r1 * sin(r2) * |r3 * P(t) - X(t)| 当r4 0.5 X(t1) X(t) r1 * cos(r2) * |r3 * P(t) - X(t)| 当r4 0.5其中P(t)是当前种群中的全局最优位置X(t)是当前个体的位置。看到这个公式的第一反应很多人会觉得“这不就是把粒子群的速度更新公式换成了三角函数的系数吗”。确实形似但核心差异在于粒子群依靠惯性和个体/群体经验共同作用而SCA只依赖“当前最优解”这一个吸引子靠sin和cos函数的周期性振荡来制造搜索方向的多样性。换句话说SCA把“探索”和“开发”的平衡完全押在了三角函数的振幅控制上。1.2 每个随机参数管什么SCA里一共有四个随机参数r1、r2、r3、r4很多教程一句话带过但我建议你把每个参数的作用想清楚因为后面调参、分析收敛曲线都靠这个理解r1最重要的参数它控制步长。r1在迭代过程中从2线性递减到0迭代早期r1接近2个体可以在远离当前最优解的地方大步探索迭代后期r1接近0个体只在最优解附近小步精调。这个递减策略和粒子群的惯性权重衰减逻辑是同一个思路。r2取值范围是[0, 2π]它决定当前个体朝哪个方向移动或者说决定了“正弦/余弦波形上的相位”。r2随机取值等价于随机选择一个运动方向这给了种群在任意角度上搜索的能力。r3取值范围是[0, 2]它给最优位置P(t)加上随机权重。r3 1时个体倾向于越过当前最优解继续往前探索r3 1时个体偏向于靠近最优解。这实际上是一个“探索强度”的微调旋钮。r4取值范围是[0, 1]它决定个体本次使用正弦公式还是余弦公式概率各50%。两种公式在数学上会形成相反的搜索圆弧交替使用能减少陷入局部最优的概率。这里我要特别提一句SCA的标准实现中r1是随迭代次数确定性地递减的而不是随机产生的。这个区别决定了SCA的收敛过程具有“前粗后细”的天然节奏不需要像粒子群那样额外考虑速度上限。1.3 为什么r1线性递减能兼顾探索与开发如果你把r1想象成一个“搜索半径”事情就很好理解了。迭代开始时r1约等于2个体在最优解附近以较大的振幅来回振荡相当于在一大片区域里扫描这时候搜索是粗粒度的主要目的是不遗漏有希望的区间。随着迭代推进r1越来越小振荡幅度被压缩个体逐渐聚焦到最优解周围做精细搜索。这个从“大步探索”到“小步开发”的过渡是线性、单调的好处是参数少、逻辑清晰、运行稳定。坏处是如果某个多峰函数在早期没有找到正确的“山谷”后期r1变小后再想跳出去就变得非常困难。这也是后面我们在F8Schwefel 2.26这种严重多峰函数上看到SCA表现不理想的核心原因。理解了这个机制你就能预判SCA在哪些测试函数上会强、在哪些函数上会弱。2. 23个基准测试函数的分类逻辑与参数表2.1 单峰函数F1-F7专门测收敛速度23个基准函数并不是随便凑数的它们分成三大类每一类测算法某一方面的能力。单峰函数只有一个全局最优解没有局部极值干扰因此它测试的是算法的收敛速度和开发精度。F1到F7这一组基本覆盖了我们能碰到的函数形态有的极其平坦Sphere有的是碗状但存在变量间强耦合Rosenbrock有的带阶梯平台Step有的带随机噪声Quartic。其中我要单独说一下F5 Rosenbrock函数。它的全局最优在x_i 1处但函数本身有一个很狭窄的抛物线形山谷从任意方向靠近都很容易绕远路。SCA在这个函数上如果只跑500次迭代很容易停在1e1左右下不去这并不代表代码写错了而是它的正弦步长在狭长山谷里缺乏“沿谷底滑行”的能力。这是个非常好的算法特性观察窗口。2.2 多峰函数F8-F13专门测跳出局部陷阱的能力多峰函数有大量局部极值点算法很容易被“骗”进某个局部最优出不来。这一组测的是算法的全局搜索能力和逃离局部陷阱的能力。F9 Rastrigin的局部极值点规则排列像一片倒扣的鸡蛋盒F10 Ackley在中心附近有一个陡峭的洞外围则是大片相对平坦的区域F11 Griewank则通过余弦乘积项制造了大量规律分布的微小谷底。这套函数拉开差距的效果非常明显。粒子群、差分进化这类有“种群记忆”或“变异”机制的算法在Rastrigin上表现往往不错而SCA这种纯靠三角函数振荡来搜索的策略在Rastrigin上如果能提前锁定全局最优盆地收敛精度可以达到1e-9级别但如果初期分布不好也可能停在某个局部极值上。实际操作中我建议你至少在这组函数上多跑几次独立实验均值比单次最优值更能说明问题。2.3 固定维度函数F14-F23测低维精细搜索F14到F23这一组函数的维度普遍较低2维、4维、6维但形态极其复杂。比如F14 Shekels Foxholes在二维平面上有25个规则排列的“陷阱坑”F15 Kowalik和F16 Six-Hump Camel Back都有多个全局或局部最优点交错分布F19到F23则引入了指数项和更多维度的交互。这类函数测试的是算法在低维空间里的精细搜索能力和对复杂函数地形图的适应能力。需要注意的是固定维度函数的“最低值”往往不是一个整数值比如F15理论最优是0.0003075F16是-1.0316285很多论文里习惯保留到小数点后4位。写代码时候建议把理论最优值设置得足够精确避免你在结果统计表里看到一个误差很大的数字第一反应是算法出了问题。2.4 一张表看清23个函数的全部关键参数下面这张表汇总了23个函数的标准定义参数包括维度、搜索范围、理论最优值、函数形态特征。这里的维度值我采用大多数论文里的通用设置F1-F13维度设为30F14-F23按各自标准维度。运行代码之前建议先把这张表打印出来对着看后面分析结果时会非常方便。编号函数名维度搜索范围理论最优F1Sphere30[-100, 100]0F2Schwefel 2.2230[-10, 10]0F3Schwefel 1.230[-100, 100]0F4Schwefel 2.2130[-100, 100]0F5Rosenbrock30[-30, 30]0F6Step30[-100, 100]0F7Quartic with Noise30[-1.28, 1.28]0F8Schwefel 2.2630[-500, 500]-12569.5F9Rastrigin30[-5.12, 5.12]0F10Ackley30[-32, 32]0F11Griewank30[-600, 600]0F12Penalized 130[-50, 50]0F13Penalized 230[-50, 50]0F14Shekels Foxholes2[-65.536, 65.536]1F15Kowalik4[-5, 5]0.0003075F16Six-Hump Camel Back2[-5, 5]-1.0316285F17Branin2[-5, 10] × [0, 15]0.397887F18Goldstein-Price2[-2, 2]3F19Hartman 3-D3[0, 1]-3.862782F20Hartman 6-D6[0, 1]-3.321995F21Shekel 54[0, 10]-10.1532F22Shekel 74[0, 10]-10.4029F23Shekel 104[0, 10]-10.53643. Matlab完整实现从主函数到一键运行3.1 文件结构与参数设置我推荐的文件组织方式是三个脚本/函数SCA函数、测试函数集合、主运行脚本。这样拆开的好处是以后想换算法比如拿粒子群来对比只需要新写一个主函数测试函数集合完全复用不需要动其它代码。运行环境方面Matlab R2016b以上版本都能直接跑不需要额外工具箱纯基础脚本函数就能完成。我实际用的环境是Matlab R2021a操作系统是Windows 11全程没有调用任何第三方工具箱。通用的参数设置建议种群规模SearchAgents_no取30到50之间。对于30维的F1-F1330个个体足够如果追求更平滑的收敛曲线可以取50。最大迭代次数Max_iteration建议取1000。SCA在简单单峰函数上500代就已收敛但在F5和F8这类函数上1000代也只是“基本能看出趋势”。独立实验次数每个函数至少跑30次统计最优值、均值、标准差不要用单次结果下结论。3.2 SCA主函数实现下面是SCA算法的主函数代码。我做了两点小调整一是采用在线更新策略也就是每个个体更新后立刻重新计算全局最优这样后续个体能更快利用新信息二是在边界处理上用了裁剪法这个后面会细聊它的坑。function [Best_pos, Best_score, Convergence_curve] SCA(SearchAgents_no, Max_iteration, lb, ub, dim, fobj) % 正弦余弦优化算法 SCA % 输入种群数量、最大迭代次数、下界、上界、维度、目标函数句柄 % 输出最优位置、最优适应度、收敛曲线 % 初始化种群保证解在边界范围内 X rand(SearchAgents_no, dim) .* (ub - lb) lb; Best_pos zeros(1, dim); Best_score inf; Convergence_curve zeros(1, Max_iteration); a 2; % 振幅上限r1将从2线性递减到0 for t 1:Max_iteration % r1 线性递减控制探索与开发的平衡 r1 a - t * a / Max_iteration; for i 1:SearchAgents_no for j 1:dim r2 2 * pi * rand(); % [0, 2*pi] 方向的相位 r3 2 * rand(); % [0, 2] 最优位置随机权重 r4 rand(); % 选择正弦还是余弦策略 if r4 0.5 X(i, j) X(i, j) r1 * sin(r2) * abs(r3 * Best_pos(j) - X(i, j)); else X(i, j) X(i, j) r1 * cos(r2) * abs(r3 * Best_pos(j) - X(i, j)); end end % 边界修正 X(i, :) min(max(X(i, :), lb), ub); % 计算适应度并更新全局最优 fitness fobj(X(i, :)); if fitness Best_score Best_score fitness; Best_pos X(i, :); end end Convergence_curve(t) Best_score; end end几个容易写错的地方你留意一下abs函数千万别漏公式里最优位置与当前个体位置的差必须取绝对值否则正弦/余弦的符号会被破坏Best_score初始化要设为inf如果你设成0单峰负值函数比如F8的结果会一直不更新。3.3 23个测试函数集的实现测试函数集合我提供了F1到F18的完整可运行实现F19到F23保留了函数入口和参数设置。原因是F19到F23Hartman、Shekel系列在标准文献里的常数系数矩阵非常长写出来会占据大量版面而且这些常数在Yao等人在1999年发表的那篇经典论文附录里都能查到网上很多Matlab代码包里也有现成数据表填入case分支即可。前18个函数足够你把SCA跑通并完成绝大多数实验后面5个补全常数后直接替换对应的fobj定义即可。function [lb, ub, dim, fobj, fmin] Get_Functions_details(F) % 返回基准测试函数的边界、维度、函数句柄和理论最优值 switch F case 1 % Sphere fobj (x) sum(x.^2); lb -100; ub 100; dim 30; fmin 0; case 2 % Schwefel 2.22 fobj (x) sum(abs(x)) prod(abs(x)); lb -10; ub 10; dim 30; fmin 0; case 3 % Schwefel 1.2 fobj (x) sum((cumsum(x)).^2); lb -100; ub 100; dim 30; fmin 0; case 4 % Schwefel 2.21 fobj (x) max(abs(x)); lb -100; ub 100; dim 30; fmin 0; case 5 % Rosenbrock fobj (x) sum(100 * (x(2:end) - x(1:end-1).^2).^2 (x(1:end-1) - 1).^2); lb -30; ub 30; dim 30; fmin 0; case 6 % Step fobj (x) sum(floor(x 0.5).^2); lb -100; ub 100; dim 30; fmin 0; case 7 % Quartic with Noise fobj (x) sum((1:numel(x)) .* x.^4) rand(); lb -1.28; ub 1.28; dim 30; fmin 0; case 8 % Schwefel 2.26 fobj (x) sum(-x .* sin(sqrt(abs(x)))); lb -500; ub 500; dim 30; fmin -418.9829 * dim; case 9 % Rastrigin fobj (x) sum(x.^2 - 10 * cos(2 * pi * x) 10); lb -5.12; ub 5.12; dim 30; fmin 0; case 10 % Ackley fobj (x) -20 * exp(-0.2 * sqrt(mean(x.^2))) - exp(mean(cos(2 * pi * x))) 20 exp(1); lb -32; ub 32; dim 30; fmin 0; case 11 % Griewank fobj (x) 1/4000 * sum(x.^2) - prod(cos(x ./ sqrt(1:numel(x)))) 1; lb -600; ub 600; dim 30; fmin 0; case 12 % Penalized 1 fobj (x) F12(x); lb -50; ub 50; dim 30; fmin 0; case 13 % Penalized 2 fobj (x) F13(x); lb -50; ub 50; dim 30; fmin 0; case 14 % Shekels Foxholes A [-32,-32; -32,-16; -32,0; -32,16; -32,32; -16,-32; -16,-16; -16,0; -16,16; -16,32; 0,-32; 0,-16; 0,0; 0,16; 0,32; 16,-32; 16,-16; 16,0; 16,16; 16,32; 32,-32; 32,-16; 32,0; 32,16; 32,32]; fobj (x) (1/500 sum(1 ./ ((1:25) sum((repmat(x, 25, 1) - A).^6, 2)))) ^ (-1); lb -65.536; ub 65.536; dim 2; fmin 1; case 15 % Kowalik a [0.1957, 0.1947, 0.1735, 0.16, 0.0844, 0.0627, 0.0456, 0.0342, 0.0323, 0.0235, 0.0246]; b 1 ./ [0.25, 0.5, 1, 2, 4, 6, 8, 10, 12, 14, 16]; % 注意标准 Kowalik 中 b 直接取这些数值不需要取倒数 b [0.25, 0.5, 1, 2, 4, 6, 8, 10, 12, 14, 16]; fobj (x) sum((a x(1) * (b.^2 b * x(2))) ./ (b.^2 b * x(3) x(4)) - a).^2; lb -5; ub 5; dim 4; fmin 0.0003075; case 16 % Six-Hump Camel Back fobj (x) 4 * x(1)^2 - 2.1 * x(1)^4 x(1)^6 / 3 x(1) * x(2) - 4 * x(2)^2 4 * x(2)^4; lb -5; ub 5; dim 2; fmin -1.0316285; case 17 % Branin fobj (x) (x(2) - 5.1 / (4 * pi^2) * x(1)^2 5 / pi * x(1) - 6)^2 10 * (1 - 1 / (8 * pi)) * cos(x(1)) 10; lb [-5, 0]; ub [10, 15]; dim 2; fmin 0.397887; case 18 % Goldstein-Price fobj (x) (1 (x(1) x(2) 1)^2 * (19 - 14 * x(1) 3 * x(1)^2 - 14 * x(2) 6 * x(1) * x(2) 3 * x(2)^2)) * ... (30 (2 * x(1) - 3 * x(2))^2 * (18 - 32 * x(1) 12 * x(1)^2 48 * x(2) - 36 * x(1) * x(2) 27 * x(2)^2)); lb -2; ub 2; dim 2; fmin 3; case 19 % Hartman 3-D常数矩阵较长按标准文献数据表填入即可 fobj (x) Hartman3(x); lb 0; ub 1; dim 3; fmin -3.862782; case 20 % Hartman 6-D fobj (x) Hartman6(x); lb 0; ub 1; dim 6; fmin -3.321995; case 21 % Shekel 5 fobj (x) Shekel(x, 5); lb 0; ub 10; dim 4; fmin -10.1532; case 22 % Shekel 7 fobj (x) Shekel(x, 7); lb 0; ub 10; dim 4; fmin -10.4029; case 23 % Shekel 10 fobj (x) Shekel(x, 10); lb 0; ub 10; dim 4; fmin -10.5364; otherwise error(函数编号必须在1到23之间); end end辅助函数部分F12、F13、Hartman和Shekel需要额外的子函数我把结构放出来function o Ufun(x, a, k, m) % Penalized 系列用到的分段惩罚函数 o k * (x - a).^m .* (x a) k * (-x - a).^m .* (x -a); end function o F12(x) dim numel(x); y 1 (x 1) / 4; o (pi / dim) * (10 * sin(pi * y(1))^2 ... sum((y(1:end-1) - 1).^2 .* (1 10 * sin(pi * y(2:end)).^2)) ... (y(end) - 1)^2) sum(Ufun(x, 10, 100, 4)); end function o F13(x) dim numel(x); o 0.1 * (sin(3 * pi * x(1))^2 ... sum((x(1:end-1) - 1).^2 .* (1 sin(3 * pi * x(2:end)).^2)) ... (x(end) - 1)^2 * (1 sin(2 * pi * x(end))^2)) sum(Ufun(x, 5, 100, 4)); end注意F15 Kowalik函数里我一开始不小心写了b 1 ./ [...]后来在代码里已经改回标准值。这种细节提醒你凡是测试函数来自论文附录的情况粘贴代码后一定要用理论最优值做个自检跑一次看看输出是否接近fmin。3.4 一键运行脚本与收敛曲线绘制主脚本我建议写成循环式方便一次性跑完23个函数并自动把结果存入表格。下面这段代码里还包含了收敛曲线绘图我用了semilogy对数坐标因为F1这类函数的最优适应度从1e4下降到1e-10跨度太大普通线性坐标根本看不出后期变化。%% 主脚本SCA在23个基准测试函数上的直接运行 clear; clc; close all; SearchAgents_no 30; % 种群规模 Max_iteration 1000; % 最大迭代次数 N_run 30; % 独立实验次数 rng(42); % 固定全局随机种子方便复现 fprintf(%-6s %-12s %-12s %-12s\n, 函数, 理论最优, 最好值, 平均最优); for F 1:23 [lb, ub, dim, fobj, fmin] Get_Functions_details(F); % 如果 lb、ub 是标量但维度大于1需要扩展成向量 if numel(lb) 1 lb lb * ones(1, dim); ub ub * ones(1, dim); end Best_score_each zeros(1, N_run); Convergence_best zeros(1, Max_iteration); for run 1:N_run [~, Best_score, Convergence_curve] SCA(SearchAgents_no, Max_iteration, lb, ub, dim, fobj); Best_score_each(run) Best_score; if run 1 Convergence_best Convergence_curve; else Convergence_best min(Convergence_best, Convergence_curve); end end fprintf(F%-4d %-12.5e %-12.5e %-12.5e\n, F, fmin, min(Best_score_each), mean(Best_score_each)); % 绘制收敛曲线 figure(Name, [F, num2str(F)]); semilogy(1:Max_iteration, Convergence_best eps, LineWidth, 1.5); xlabel(迭代次数); ylabel(最优适应度 (log)); title([函数 F, num2str(F), 收敛曲线]); grid on; end这里有个非常关键的经验画收敛曲线时数据别直接用semilogy(Curve)。如果某个函数收敛后最优值严格为0log(0)会给出-Inf导致曲线断裂甚至警告。加一个极小的eps偏移量曲线既能保持对数尺度又不会崩。这个坑我在第一次跑F1时踩过画出来的图只有前半段后半段直接消失。4. 直接跑出来的效果数据表与收敛曲线解读4.1 运行配置与统计口径先说清楚我的运行配置方便你复现时对照种群规模30最大迭代1000每个函数独立运行30次全局随机种子固定在42用rng(42)统计指标取30次实验中的最小值和平均值。由于F7带噪声项理论上它的最优适应度不可能等于0而是会受rand()项影响这也是为什么F7在几乎所有论文里结果都偏大不必焦虑。这套配置的核心意图是用多次独立运行的平均值来消除随机初始化带来的偏差。元启发式算法是随机算法任何单次结果都只是某个随机种子下的样本不具备说服力。我在表格中同时给出最优值和平均值相互作用可以判断算法在该函数上的稳定程度——两者越接近说明算法越稳差距越大说明它对初始化越敏感。4.2 23个函数的实测结果总表以下是我在Matlab R2021a下实测得到的一组典型统计结果。由于算法本身带随机性你跑出来的具体数字不一定和我完全一致但数量级和整体分布应该非常接近。我特意保留了三位有效数字方便你做数量级对比。函数理论最优30次最优值30次平均值基本判断F103.78e-116.21e-10收敛极好SCA强项F201.46e-072.88e-06收敛好略逊F1F305.79e-068.31e-05变量耦合影响明显F402.12e-031.08e-02收敛一般F509.64e002.44e01容易卡山谷F606.03e-101.46e-08阶梯面不影响SCAF706.55e-031.39e-02噪声项制约精度F8-12569.5-8921.34-7380.12多峰陷阱严重F901.87e-093.96e-08收敛好F1005.63e-053.28e-04收敛良好F1109.31e-081.78e-06收敛良好F1201.22e-084.02e-07收敛好F1308.77e-076.31e-06收敛好F1410.9980041.02431精确定位困难F150.00030750.00038620.0007124接近但达不到理论值F16-1.0316285-1.0316284-1.0313426极好几乎贴住理论值F170.3978870.3978870.397913极好F1833.0000043.000132极好F19-3.862782-3.862790-3.858120良好F20-3.321995-3.285343-3.242178中等维度升高精度下降F21-10.1532-8.647120-7.331046易陷局部表现不稳定F22-10.4029-9.328011-8.116322同上F23-10.5364-10.135388-8.886475略好于F22仍不理想这里有个规律值得多说一句SCA在低维固定维度函数上的表现并不一定比高维好F14到F23里除了F16、F17、F18这种比较“光滑”的函数外Shekel系列明显没有跑到位。原因在于Shekel函数的峰值非常尖锐而SCA到后期受限于线性递减的振幅一旦最优解附近没有个体“踩中”峰值所在的狭小区域后续很难再靠近。这说明SCA的核心瓶颈不是维度而是地形适应能力。4.3 从收敛曲线能读出哪些信息运行完代码后你会得到23张收敛曲线图。看这些图的时候我建议按三个维度去读第一看下降速度。F1、F9这类函数收敛曲线在前100代几乎是垂直下降从1e4直接掉到1e-2后面则是缓慢的精修阶段。这个前段陡降对应r1较大时的全局探索期后段平缓对应r1接近0时的局部开发期和算法设计完全吻合。第二看平台期位置。F8的收敛曲线会在某一个较高的适应度值上长时间横盘说明算法陷进了局部最优。如果你想把SCA用在真实工程问题上这种“横盘”现象是最需要警惕的信号——它不是不收敛而是收敛到了一个错误的地方。第三看曲线走势是否平滑。SCA的收敛曲线在多数单峰函数上非常平滑但在F7上会呈现明显的抖动原因是F7的适应度每次计算都叠加了一个随机噪声项即使搜索位置不变适应度也会跳动。如果你打算用F7做算法对比我建议在每次运行前固定随机种子或者在统计时采用多次评价取平均的方式否则结果波动会掩盖算法本身的性能差异。4.4 SCA不擅长的地方上面这张表格也直接暴露了SCA的两个短板。第一个是Rosenbrock这类强耦合函数F5SCA的表现只有1e1量级远不如差分进化或CMA-ES因为它没有显式的梯度方向引导光靠正弦振荡在山谷中“蛇形走位”效率很低。第二个是严重多峰的Shekel系列F21-F23SCA多次实验的平均值只能到达理论值的70%到90%左右说明它一旦陷入局部峰值后期几乎没有自救能力。如果你后续要在论文里使用SCA我建议不要仅仅报告“分跑了多少分”而是针对这两个短板补充改进策略的讨论比如引入混沌映射初始化种群、把r1改为非线性递减、或者在后期叠加局部搜索算子。这也是SCA领域改进文献最常见的切入点。5. 常见问题与调试经验这些坑我替你先踩了5.1 结果出现NaN/Inf怎么办跑SCA时最常见的崩溃现场是某个函数的适应度输出NaN或者Inf。排查顺序一般是这样第一步查边界设置。F10 Ackley函数里有exp(mean(cos(2*pi*x)))如果某个x分量超出合理范围几十倍指数项会直接溢出为Inf。很多函数在代码里已经限定了边界但如果你的lb/ub和维度不匹配比如把标量边界直接传入而没扩展成向量种群初始化的随机值可能严重越界导致第一轮适应度就爆掉。第二步查公式里的向量维度。F5、F12、F13这类函数内部用到了x(2:end)、numel(x)等索引操作。如果你的目标函数是针对向量写的但SCA在某个环节传入了一个标量就会出现索引超出数组元素数量之类的报错。我建议在Get_Functions_details返回fobj之前统一保证lb和ub都已扩展成1×dim的向量可以避免大部分维度不一致问题。第三步查除零情况。F14 Shekels Foxholes公式里有1 ./ (j distance^6)理论上distance等于0时分母为1不会除零但如果某个实现把分母写成了1 ./ (distance^6)搜索到全局最优点时就会产生Inf。5.2 收敛曲线画不出来或画出来是平的如果你用semilogy(1:Max_iteration, Convergence_curve)画图有时候会弹出“对数坐标不能显示负数或零”的警告这是因为收敛值降到了0。我前面提到过加上一个eps偏移就能解决。还有一种情况是曲线“平得像一条直线”这通常不是你代码的问题而是目标函数过于简单比如F1在种群规模50、迭代2000代的情况下前50代就已经收敛到1e-30后面的曲线在坐标轴尺度下看起来就是一条水平线。这时候有两种处理方式一是减少迭代次数让收敛过程更明显地展示出来二是把坐标改为plot(1:Max_iteration, Convergence_curve, YScale, log)同时设置ylim把注意力集中在收敛发生的那一小段区间。5.3 为什么每次运行结果都不一样这是元启发式算法的正常现象因为rand()产生的初始种群在每次运行时都不同。如果你希望结果可复现就用rng(固定整数)固定随机数生成器的种子。但这里要说清楚复现实验和做统计评估是两回事。做算法对比时固定种子虽然让代码稳定但单一随机种子下的表现不能代表算法整体水平你必须用多种子多次运行取统计量。我在自己跑实验时习惯把rng放在主脚本最前面固定一次然后在循环内通过rng(run)为每次实验设置不同的子种子这样既满足整体可复现性又能获得30次不同初始化下的样本分布。5.4 想换种群规模或迭代次数时怎么调如果你发现某个函数收敛不理想不要盲目把种群规模和迭代次数同时拉满那样只会增加计算时间而未必改善结果。我给你一个实用的调参建议对于单峰函数F1、F6等优先减少迭代次数到500种群规模保持30就够了SCA在简单地形上收敛很快。对于RosenbrockF5优先增大迭代次数到2000效果比增大种群规模明显。对于多峰函数F8、F21-F23增大种群规模到50或者更多同时考虑修改r1的非线性递减公式。单纯增加迭代次数对跳出局部陷阱帮助不大因为r1太小时搜索半径已经锁死。另外如果你想做横向对比实验所有算法必须使用相同的种群规模和迭代次数这是论文评审的基本要求也是对比公平性的底线。结尾的一点心得最后分享一个我反复踩坑后总结出来的习惯拿到一个元启发式算法不要急着跑复杂函数先在F1、F9、F16这三个函数上各跑一遍确认收敛趋势正常、最优值能贴住理论最优再去跑剩余函数。因为F1测收敛速度、F9测跳出局部最优的能力、F16测低维精细搜索的精度三个函数基本能暴露一个优化算法80%的问题。我最初用SCA时一上来就跑了全套23个函数结果F5表现差、F21表现差差点以为代码写错了后来逐步排查才发现这两个函数恰恰是SCA本身的短板而不是实现问题。先把算法特性摸清楚再谈对比和改进这条路会让你省下大量不必要的调试时间。