尧图精选

樽海鞘优化算法SSA改进:Tent混沌映射与自适应权重Matlab实现

🕒 发布时间:2026/10/2 14:48:11 📁 来源:尧图网络
1. 从樽海鞘的链式游动说起SSA算法的核心机制1.1 算法想法的由来樽海鞘优化算法SSASalp Swarm Algorithm是 Mirjalili 等人在 2017 年提出的群智能优化算法模拟的是樽海鞘在深海中结成链状群体游动觅食的行为。这个算法最大的特点就是结构非常简单把种群分成一条领导者leader和一堆追随者follower领导者负责探路追随者一条接一条地跟在后面形成一条“活的链条”。这种结构极其清爽没有粒子群算法中那么多速度、惯性和个体记忆的概念也没有遗传算法里选择、交叉、变异那套繁琐的算子。我做优化算法的第一感觉是SSA 的代码量少参数也少非常容易从零写出来。但实际跑起来之后才发现越是这种“看着简单”的算法越容易在复杂优化问题上暴露收敛精度差、容易早熟的问题。所以后来我做了一个改进版本策略就是标题里写的那句话Tent混沌初始化种群 自适应权重改进领导者位置更新。今天这篇博文就专门把这个版本的思路、公式、Matlab 代码和实验验证完整拆开讲适合正在做毕业论文、写小论文或者想快速上手群智能算法改进的读者参考。1.2 位置更新的两个核心环节原始SSA中种群的链条一共分两类角色。第一类是领导者也就是链条最前面的那个个体它直接朝着当前食物源也就是全局最优解的位置移动。领导者的位置更新公式是[ x_1^j F^j c_1 \cdot ((ub_j - lb_j) \cdot c_2 lb_j) ]当 (c_2 0.5) 时公式中的加号变成减号相当于随机决定向食物源靠近还是远离。这里的 (c_1) 是整个算法中最关键的系数它的计算方式是[ c_1 2 \cdot e^{-(4t/T)^2} ]其中 (t) 是当前迭代次数(T) 是最大迭代次数。也就是说迭代初期 (c_1) 接近 2领导者的步子迈得很大越到后期 (c_1) 越小步长被压缩算法逐渐从全局探索过渡到局部开发。第二类是追随者。追随者的位置更新没有任何随机量纯粹是牛顿运动定律的简化版本[ x_i^j \frac{1}{2}(x_i^j x_{i-1}^j) ]也就是当前个体与它前面那个个体的中点。这个设计非常有意思链条上后面的人永远在向前面那个人靠拢信息从链头传到链尾是有延迟的。这种“链式联动机制”是 SSA 区别于其他群智能算法的最大特色。1.3 原始SSA的痛点我最初把标准SSA放到 Matlab 里跑了一些经典测试函数总结下来有三个比较明显的问题第一初始种群是纯随机生成的。rand函数虽然能生成均匀分布但在维度高、种群规模小的情况下随机点的空间覆盖性并不好有些区域点扎堆有些区域大片空白。一旦初始群体离全局最优解区域太远整个链条很容易被带偏。第二领导者步长虽然受 (c_1) 调控但这个调控曲线是固定的和当前种群的实际收敛状态没关系。遇到 Rastrigin、Ackley 这类充满局部极小值的多峰函数时链条前端的个体很容易被某个局部极值点“粘住”后面的追随者排着队往那个方向挤群体多样性快速下降最终早熟收敛。第三到迭代后期领导者的步长已经压得很小了搜索精度受限于步长尺度很难在最优解附近做更精细的移动。针对这三个问题我选了两个侵入式最小的改进点一是把纯随机初始化换成 Tent混沌序列初始化二是给领导者位置更新加一个自适应权重。这两个改动都只影响局部代码逻辑不破坏原始SSA的链式结构。2. 第一处手术用Tent混沌映射重做种群初始化2.1 随机初始化到底差在哪先看一个简单例子。假设一个二维问题解空间是 ([0,1]^2)种群规模 30。用rand生成的初始点虽然理论上服从独立均匀分布但实际分布往往有聚簇和空洞。如果其中恰巧没有点在某个关键区域附近优化器初期就失去了对那块区域的感知能力。群智能算法本质上是从初始种群开始扩散搜索的起点分布越均匀前期探索的覆盖面就越完整。混沌映射生成的序列有一个很吸引人的特性遍历性。也就是说序列在取值范围内来回游走几乎能“走遍”整个区间而且不重复地覆盖空间。把这种序列映射到解空间里初始种群在空间上的分布会比纯随机更均匀而且点与点之间还保留了混沌序列特有的相关性。Tent映射就是这类序列里结构最简单、计算代价最低的一种。2.2 Tent比Logistic好在哪Tent映射的迭代公式是分段的[ x_{n1} \begin{cases} x_n / \alpha, 0 \le x_n \le \alpha \ (1 - x_n) / (1 - \alpha), \alpha x_n \le 1 \end{cases} ]当 (\alpha 0.5) 时映射函数图像像一个帐篷因此得名。选择 (\alpha 0.5) 是最常见的做法这种参数下 Tent 序列在区间 ([0,1]) 上的分布接近均匀。很多人做混沌初始化时会优先想到 Logistic 映射因为 Logistic 名气更大。但我对比过两者的分布特性Logistic 的序列在靠近 0 和 1 的区间密度明显偏高中间区域反而稀疏用来初始化种群会导致个体集中在边界附近Tent 映射的遍历均匀性明显更好而且计算只有除法和减法速度更快。两者的 Lyapunov 指数都能保证混沌特性但“均匀覆盖”这个需求上 Tent 更合适。换成人话讲Logistic 序列像是一个喜欢在边界徘徊的探索者Tent 序列更像一个认认真真把整个区域过一遍的侦察员。做种群初始化更信任后者。下面是 Tent 映射和 Logistic 映射的分布对比表格映射类型序列分布特征计算复杂度是否适合种群初始化Logistic边界区域密度高中间区域稀疏一次乘法、一次减法一般Tent整体近似均匀遍历性好一次除法、一次减法更合适2.3 Tent初始化在Matlab里的落地写法用 Matlab 实现 Tent 混沌初始化时我建议按维度独立生成。也就是说对每一个决策变量维度单独跑一条长度为种群规模 (N) 的 Tent 序列再把序列映射到该维度的上下界范围内。这么做的原因是让每个维度上的初始值不存在明显的拉格朗日相关性空间分布更均匀。核心代码如下function X TentInit(N, D, lb, ub, alpha) % N种群规模 % D决策变量维度 % lb, ub维度下界与上界 % alphaTent映射参数默认0.5 if nargin 5 alpha 0.5; end if abs(alpha - 0.5) 1e-12 alpha 0.5; % 确保alpha精确为0.5 end X zeros(N, D); for d 1:D % 初始值要避开不动点0和0.5 x rand; while x 1e-6 || abs(x - alpha) 1e-6 || x 1 - 1e-6 x rand; end for i 1:N if x alpha x x / alpha; else x (1 - x) / (1 - alpha); end % 处理小周期退化点加一个极小的随机扰动避免序列失真 if x 1e-6 || x 1 - 1e-6 x mod(x 0.01 * rand, 1); if x 1e-6 x 1e-6; end end % 映射到解空间 X(i, d) lb(d) x * (ub(d) - lb(d)); end end end这里有一个非常容易踩的坑Tent 映射在实际迭代中可能陷入不动点或小周期。比如初始值正好是 0那序列永远停在 0初始值正好是 0.5序列会在 0.5 和 1 之间来回跳动失去混沌特性。写代码的时候必须对这类退化点做处理。我在上面的代码里加了两个保险一是起点避开特殊值二是迭代过程中一旦发现数值小于 (10^{-6}) 或大于 (1-10^{-6})就加一个 0.01 量级的随机扰动。注意这里加的扰动不能太大否则会破坏Tent序列的遍历结构导致初始化退化成普通随机。3. 第二处手术自适应权重改进领导者更新3.1 从粒子群的惯性权重抄作业粒子群算法PSO里有个惯性权重 (w)它的作用非常经典(w) 大时粒子的速度惯性大前期全局探索能力强(w) 小时粒子更容易受个体和全局最优吸引后期局部开发能力强。很多 PSO 改进论文的核心就是设计一条好的 (w) 衰减曲线。原始SSA没有这个权重领导者的位置更新只能靠 (c_1) 这一个系数控步长。但 (c_1) 是非线性衰减的指数函数衰减速度是固定的不区分当前阶段是否真的需要更强的探索或更精细的开发。我给它加了一个乘性权重 (w)让领导者在迭代前期保持更大幅度的移动后期把步长压得更低最终收敛精度更高。3.2 权重的衰减公式设计权重 (w) 的初值 (w_{max}) 和终值 (w_{min})我沿用了 PSO 里比较成熟的经验区间(0.9) 到 (0.4)。这个区间在很多文献里都被验证过不会因为初始权重太大导致后期振荡也不会因为初始太小导致前期探索不足。但权重随迭代次数的下降方式有讲究。最常见的线性递减公式是[ w w_{max} - (w_{max} - w_{min}) \cdot \frac{t}{T} ]这个公式实现简单但我实测下来收敛效果一般因为它在迭代中期的权重变化速度太快会导致很多原本有潜力的个体仅仅因为权重变小就被迫向食物源靠拢多样性损失过快。我更推荐的是二次递减[ w w_{max} - (w_{max} - w_{min}) \cdot \left(\frac{t}{T}\right)^2 ]这个曲线的特点是在迭代前期下降得慢保持较长时间的“大权重”探索状态到了迭代后期权重加速下降让领导者迅速转入高精度开发。相当于把黄金探索期拉长了。另外还有一种指数型方案类似于模拟退火的思想[ w w_{min} (w_{max} - w_{min}) \cdot e^{-\lambda \cdot t / T} ](\lambda) 一般取 3 到 5。这个方案的前期衰减速度更快适合对收敛速度要求比较高的场景。3.3 改进后的位置更新公式与代码改进后的领导者位置更新公式变成了[ x_1^j F^j w \cdot c_1 \cdot ((ub_j - lb_j) \cdot c_2 lb_j) ]当 (c_2 0.5) 时公式中加号变减号。本质上就是给原来的随机步长项整体乘上一个随时间递减的缩放因子 (w)。在迭代初期(w) 接近 0.9搜索范围大在迭代后期(w) 接近 0.4步长被进一步压缩领导者更倾向于在食物源附近精细打磨。追随者的更新不需要改动因为问题主要出在领导者的探索和开发平衡上追随者只需要跟着链条走就行。这是我试过“同时也给追随者加权重”之后得出的结论追随者加了权重之后链条信息传递会变得不稳定反而更容易散架。写进 Matlab 主循环后核心片段是这样的wMax 0.9; wMin 0.4; ... for t 1:MaxIter c1 2 * exp(-(4 * t / MaxIter)^2); w wMax - (wMax - wMin) * (t / MaxIter)^2; % 领导者位置更新 for j 1:dim if rand 0.5 X(1, j) bestX(j) w * c1 * ((ub(j) - lb(j)) * rand lb(j)); else X(1, j) bestX(j) - w * c1 * ((ub(j) - lb(j)) * rand lb(j)); end end % 追随者链式更新 for i 2:N X(i, :) 0.5 * (X(i, :) X(i - 1, :)); end end4. 完整Matlab实现与测试脚本4.1 主函数设计为了方便对比实验我把原版SSA和带改进的SSA写在同一个主函数里通过开关控制是否启用 Tent 混沌初始化和自适应权重。这样能保证除了改进点之外其他实验条件完全一致对比结果更可信。function [bestF, bestX, conv] SSA_run(N, MaxIter, lb, ub, dim, fitness, useTent, useWeight) % 参数说明 % N种群规模 % MaxIter最大迭代次数 % lb, ub维度下界与上界向量或标量 % dim决策变量维度 % fitness目标函数句柄最小化问题 % useTent是否启用Tent混沌初始化 % useWeight是否启用自适应权重改进 % 自动扩展标量边界为向量 if isscalar(lb) lb lb * ones(1, dim); ub ub * ones(1, dim); end % 种群初始化 if useTent X TentInit(N, dim, lb, ub, 0.5); else lbMat repmat(lb, N, 1); ubMat repmat(ub, N, 1); X lbMat rand(N, dim) .* (ubMat - lbMat); end % 初始适应度评估 fitnessVals zeros(N, 1); for i 1:N fitnessVals(i) fitness(X(i, :)); end [bestF, idx] min(fitnessVals); bestX X(idx, :); conv zeros(MaxIter, 1); % 自适应权重参数 wMax 0.9; wMin 0.4; for t 1:MaxIter c1 2 * exp(-(4 * t / MaxIter)^2); if useWeight w wMax - (wMax - wMin) * (t / MaxIter)^2; else w 1; % 标准SSA相当于没有权重 end % 领导者位置更新 for j 1:dim if rand 0.5 X(1, j) bestX(j) w * c1 * ((ub(j) - lb(j)) * rand lb(j)); else X(1, j) bestX(j) - w * c1 * ((ub(j) - lb(j)) * rand lb(j)); end end % 追随者链式更新 for i 2:N X(i, :) 0.5 * (X(i, :) X(i - 1, :)); end % 边界处理截断法 lbMat repmat(lb, N, 1); ubMat repmat(ub, N, 1); X min(max(X, lbMat), ubMat); % 重新评估适应度 for i 1:N fitnessVals(i) fitness(X(i, :)); end [minNow, idxNow] min(fitnessVals); if minNow bestF bestF minNow; bestX X(idxNow, :); end conv(t) bestF; end end这个主函数里的局部函数调用方式需要注意如果你的 Matlab 版本是 R2016b 之前不能直接在脚本里写局部函数需要把TentInit单独存成一个.m文件或者把整个代码封装成函数文件。我推荐直接存成函数文件这样调用参数一目了然。4.2 测试函数配置经典测试函数我用四个覆盖单峰和多峰两类典型问题函数名公式搜索范围最优值特点Sphere(f \sum x_i^2)([-100, 100])0单峰测试收敛精度Rastrigin(f \sum [x_i^2 - 10\cos(2\pi x_i) 10])([-5.12, 5.12])0多峰局部极值多Ackley(f -20e^{-0.2\sqrt{\bar{x^2}}} - e^{\bar{\cos(2\pi x)}} 20 e)([-32, 32])0多峰中心陷阱Griewank(f \frac{1}{4000}\sum x_i^2 - \prod\cos(\frac{x_i}{\sqrt{i}}) 1)([-600, 600])0多峰有周期性干扰对应的 Matlab 目标函数定义如下function y Sphere(x) y sum(x.^2); end function y Rastrigin(x) y sum(x.^2 - 10 * cos(2 * pi * x) 10); end function y Ackley(x) n length(x); term1 -0.2 * sqrt(mean(x.^2)); term2 mean(cos(2 * pi * x)); y -20 * exp(term1) - exp(term2) 20 exp(1); end function y Griewank(x) n length(x); y sum(x.^2) / 4000 - prod(cos(x ./ sqrt(1:n))) 1; end4.3 跑一遍对比结果怎么看测试配置我建议这样设置种群规模 (N30)最大迭代次数 (MaxIter500)维度 (dim30)。这个配置在大多数普通PC上只需要几秒钟就能跑完但已经足以看出算法之间的性能差异。测试脚本如下% 测试配置 N 30; MaxIter 500; dim 30; lb [-100, -5.12, -32, -600]; ub [100, 5.12, 32, 600]; funcs {Sphere, Rastrigin, Ackley, Griewank}; % 对每个测试函数运行原版和改进版 for fIdx 1:4 lbVec lb(fIdx) * ones(1, dim); ubVec ub(fIdx) * ones(1, dim); fitFunc funcs{fIdx}; % 原版SSA [bestF1, ~, conv1] SSA_run(N, MaxIter, lbVec, ubVec, dim, fitFunc, false, false); % 改进版SSA [bestF2, ~, conv2] SSA_run(N, MaxIter, lbVec, ubVec, dim, fitFunc, true, true); fprintf(函数%d: 标准SSA best%.4e, 改进SSA best%.4e\n, fIdx, bestF1, bestF2); end我自己的机器上跑出来的典型结果是Sphere 函数上标准SSA能到 (10^{-8}) 量级改进版能到 (10^{-12}) 量级Rastrigin 函数上标准SSA经常停在 20 到 30 之间改进版能压在 0.1 以下Ackley 函数上标准SSA收敛到 9 左右改进版能到 (10^{-4}) 左右Griewank 函数上改进版的优势主要体现在收敛速度和稳定度上。从这些结果可以得到一个规律Tent混沌初始化 自适应权重的组合在多峰函数上的提升幅度明显大于单峰函数。原因是多峰函数对初始分布和“逃跑能力”要求更高初始点覆盖均匀能减少群体被困在同一个局部区域的概率自适应权重则保留了后期继续跨出局部谷的能力。提示如果你想把这些数据写进论文至少要独立运行 20 次统计最优值、平均值和标准差不能只拿单次运行结果做结论。5. 实操中的坑与排查心得5.1 混沌序列不动点问题Tent 映射最典型的坑就是序列退化到不动点或小周期。我一开始写初始化代码时没有做特殊值防护结果有几次初始化出来的种群几乎是复制粘贴的个体之间距离极小整个链条直接失去搜索能力。排查方法很简单打印初始种群的前几行如果发现大量重复数值就说明混沌序列退化了。解决方案是在迭代中检测特殊值。不仅是初始值 0 和 0.5某些迭代中间值也可能进入周期循环。我建议在每个迭代步都检查 (x) 是否小于 (10^{-6}) 或大于 (1-10^{-6})如果是就加一个小的随机扰动把序列“踢”出来。这个扰动幅度我通常取 0.01太大反而会破坏混沌特性。5.2 边界处理方式的选择种群更新之后一定会有个体越界。处理越界有两种常用办法截断法和随机重生法。截断法就是把越界值直接拉回边界简单高效随机重生法是在边界范围内重新随机生成一个新点能保持种群多样性但会导致链条结构被破坏。我实际测试下来SSA 这种链式算法更适合截断法。因为追随者依赖于前一个个体的位置如果中间某个个体的位置被随机重生成一个新点整个链条的连续性就断裂了后续个体的更新会变得非常跳跃。标准SSA和带改进的SSA做对比实验时两边的边界处理方式必须一致否则这个变量会干扰最终结论。5.3 权重参数调参与敏感性权重参数 (w_{max}) 和 (w_{min}) 不是随便取的。我做了几组实验发现规律比较明显参数设置收敛速度最终精度备注(w_{max}1.0, w_{min}0.4)前期快一般后期振荡略大(w_{max}0.9, w_{min}0.4)前期较快较好推荐(w_{max}0.7, w_{min}0.2)前期慢后期好适合低维问题无权重前期快较差标准SSA如果你发现改进版SSA的表现反而不如标准版优先检查两件事一是权重是否放在了随机步长项上而不是放在食物源 (F) 的前面二是迭代次数 (MaxIter) 是否太短。自适应权重在后期才能发挥高精度搜索优势如果迭代次数只有 50、100权重还没来得及压下来搜索就结束了效果自然不明显。5.4 对比实验的统计严谨性做算法对比时有一个特别容易犯的错只跑一次看到改进版数值更小就下结论。群智能算法本身带有随机性单次运行的结果受初始种子影响很大可能会得到完全相反的结论。我在写论文阶段就吃过这个亏当时改进版在某次运行里比标准版差了一个数量级差点以为改进方向有问题后来多跑几次才发现那只是运气差。正确的做法是固定相同种群规模、相同迭代次数、相同边界处理方式分别独立运行至少 20 次统计三个指标——最优值、平均值、标准差。最优值反映算法的峰值能力平均值反映整体性能标准差反映稳定性。改进算法如果只提升了最优值而平均值很差说明算法是靠运气取胜不具备说服力。另外绘制平均收敛曲线时要把每次迭代对应的最优适应度值存下来跑完多次后逐代求均值。上面代码里的conv向量就是干这个用的。画出的收敛曲线如果是前 100 代快速下降、后期缓慢平滑下降基本可以说明改进策略是有效的。最后分享一个我在实际操作中的体会Tent混沌初始化和自适应权重这两个改进单独用任何一个都能看到效果但组合起来才能同时改善“起点质量”和“过程平衡”。做这类改进不要只盯着单峰函数Rastrigin、Ackley 这类多峰函数才是检验算法迸发力的试金石。同样的代码框架换一个指数型权重公式、换一个混沌映射参数很可能又是一组新结果这一套思路后续可以继续扩展开来玩。
上一篇/下一篇内容由系统自动关联 返回资讯列表 →