ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

SLWChoA:融合Sine映射、Levy飞行与自适应权重的黑猩猩优化算法改进

2026/9/10 9:13:45 拓冰建站 浏览量
SLWChoA:融合Sine映射、Levy飞行与自适应权重的黑猩猩优化算法改进 标题都说了先抛干货那我就不铺垫了。SLWChoA是黑猩猩优化算法Chimp Optimization AlgorithmChoA的一个改进版本三个字母分别对应Sine混沌映射、Levy飞行策略和自适应权重。它解决的问题很直接原版ChoA在不少基准函数上容易早熟、收敛精度一般真要和GWO、PSO这些老牌算法硬碰硬的时候经常占不到便宜。改进之后在常见的测试函数上精度往往能提升好几个数量级代码结构也没复杂多少新手对照着敲一遍半小时内能跑出自己的结果。这篇东西适合谁看两类人。第一类是正在做智能优化算法对比实验的研究生需要给自己的论文加一个“改进算法”作为创新点第二类是工程上要做参数整定、路径规划、资源调度这类优化问题的开发者想找一个比原版稳定的工具直接用。无论哪类我下面讲的原理、代码、实验设置和踩坑记录都按可以复现的标准来给。1. 先看原版ChoA怎么狩猎四个角色和两段式搜索1.1 黑猩猩群体的四种分工原版ChoA是2020年前后提出的一类群体智能优化算法灵感来自黑猩猩群体狩猎时的社会行为。和狼群算法不一样的地方在于黑猩猩群体里不是只有头狼说了算而是存在四种分工明确、互相协作的角色攻击者、阻挡者、驱赶者、追逐者。攻击者是整个群体里适应度最好的个体相当于“主攻手”阻挡者负责卡住猎物的逃跑路线驱赶者把猎物往包围圈里赶追逐者跟着猎物消耗它的体力。放到算法里这四种角色其实就是四个“参照位置”。每个黑猩猩个体在更新自己的位置时不是只盯着全局最优解看而是会根据一定的规则去参考这四个角色的位置。这样做的好处是群体里的信息不会全部涌向同一个点多样性比单参照模型更好。1.2 包围、攻击与社会激励的数学表达算法的核心位置更新公式分为两步。第一步是计算个体与猎物位置之间的距离[ d |C \cdot x_{prey} - m \cdot x| ]第二步是更新位置[ x_{new} x_{prey} - A \cdot d ]其中 A 和 C 是系数向量A 由收敛因子 a 线性递减到 0 左右C 是一个随机数m 是混沌系数。另外ChoA还加入了一个社会激励机制如果随机数小于0.5个体就只用攻击者位置来更新如果大于0.5则从四个角色里随机挑一个用。这个机制让算法在“集中攻击”和“分工围猎”两种模式之间随机切换算是它在结构上区别于GWO的一个特色。1.3 原版ChoA的两个短板原版框架不差但落到具体实现上有两个问题很影响实际效果。第一个问题是初始种群完全靠随机生成。随机这个事儿在低维、简单函数上问题不大一旦目标函数复杂一点初始种群很容易扎堆在某一个局部区域后面不管怎么迭代都很难把丢失的区域补回来。第二个问题是收敛因子 a 从2到0是纯线性递减的这意味着算法从全局搜索到局部开发的切换节奏是“一刀切”的没有根据迭代状态做自适应调整。再加上位置更新里缺少主动跳出局部最优的机制遇到多峰函数时很容易陷入早熟。后面SLWChoA的三处改进全都是冲着这两个短板去的。2. SLWChoA的三个关键改进Sine、Levy、Weight分别治什么病2.1 S代表Sine混沌映射从源头解决种群多样性第一处改进是用Sine混沌映射替换原来的纯随机初始化。常规的随机初始化用的是均匀分布随机数个体位置可能出现“该覆盖的区域没覆盖不该覆盖的区域挤成一堆”的情况。混沌映射的特点是对初值极其敏感序列本身有遍历性能在有限个体数下更均匀地覆盖搜索空间用一句话概括就是“同样的种群数量混沌初始化能让样本分布更有代表性”。Sine映射的迭代公式非常简单[ x_{t1} \sin(\pi \cdot x_t) ]每次迭代把当前值映射回(0,1)区间然后再缩放到底层的搜索边界[lb,ub]。我在实现时留了一个细节第一只个体用纯随机数初始化从第二只开始才用Sine混沌序列生成这样能避免所有个体都从同一个混沌种子出发导致的相关性问题。另外在位置更新公式里的混沌系数 m 也可以统一用Sine映射来生成。这样整个算法里的混沌机制就集中在一处理解和调参都更省心。S改进看起来不起眼但对多峰函数的最终结果影响非常明显。2.2 L代表Levy飞行给算法一个“跳出去”的能力第二处改进引入了Levy飞行策略这是解决早熟问题的最直接手段。群体智能算法在迭代中后期有一个通病所有个体都向当前最优解靠拢一旦这个最优解只是局部最优整个群体想靠常规的位置更新爬出来几乎不可能。Levy飞行是一种重尾分布的随机游走它的步长偶尔会出现一个“长跳”这个长跳能让个体直接从当前局部区域跳到另一个区域相当于给算法加了一个“逃生舱”。Lev步长的生成常用Mantegna算法先从正态分布中采样两个变量 u 和 v然后用下面公式计算步长[ step \frac{u}{|v|^{1/\beta}} ]其中β一般取1.5。生成后的步长还要乘一个缩放因子我一般取0.01。这个缩放因子很关键调太大了个体到处乱飞结果不稳定调太小了又看不出跳跃效果。在我的实现里不是每个个体每次都做Levy扰动而是设置了一个概率levy_p默认为0.5。每更新完一个个体掷一次骰子命中了才叠加Levy步长。加了这一层随机性既控制了计算开销也让跳跃行为不至于过于频繁而破坏正常的收敛节奏。2.3 W代表自适应权重把“探索”和“开发”的节奏理顺第三处改进是引入一个随迭代次数递减的自适应权重w。原版ChoA的距离公式d其实只是一个随机缩放整体搜索步长的衰减完全依赖收敛因子a的线性下降这个节奏在复杂函数上不够灵活。加入权重后距离计算加上权重缩放[ d_w w \cdot |C \cdot x_{prey} - m \cdot x| ]权重w从w_max随着迭代递减到w_min。我采用平方递减的方式让权重前期下降快、后期下降慢。为什么这样设计前期需要保持较大的步长去探索整个空间所以权重虽然开始下降但依然维持在较高水平后期进入精修阶段较小的权重能把搜索步长限制在一个很窄的邻域内方便做局部细致搜索从而提升最终解精度。三个改进各管一段Sine管“起步”Levy管“中途跳坑”权重管“收尾精度”。这也是我体验下来认为SLWChoA比原版有效的主要原因——不是某一个改进单独起决定性作用而是三者在时间轴上无缝衔接把搜索过程的三个阶段分别优化了一遍。3. 可以直接抄的SLWChoA完整代码实现3.1 核心代码逐段拆解下面这段Python代码是我整理过的最简可运行版本。依赖只有numpy和math库不需要额外的优化工具箱。测试函数先放两个经典的Sphere和Rastrigin。跑完会输出每一轮的目标函数值变化方便你判断当前收敛情况。import numpy as np from math import gamma # ---------- 目标函数 ---------- def sphere(x): return np.sum(x ** 2) def rastrigin(x): return 10 * len(x) np.sum(x ** 2 - 10 * np.cos(2 * np.pi * x)) # ---------- Sine混沌初始化 ---------- def sine_initialization(pop_size, dim, lb, ub): X np.zeros((pop_size, dim)) X[0] np.random.uniform(lb, ub, dim) r np.random.uniform(0, 1, dim) for i in range(1, pop_size): r np.sin(np.pi * r) X[i] lb (ub - lb) * r return X # ---------- Levy飞行步长 ---------- def levy_flight(dim, beta1.5): sigma (gamma(1 beta) * np.sin(np.pi * beta / 2) / (gamma((1 beta) / 2) * beta * 2 ** ((beta - 1) / 2))) ** (1 / beta) u np.random.normal(0, sigma, dim) v np.random.normal(0, 1, dim) step u / (np.abs(v) ** (1 / beta)) return step # ---------- 主算法 ---------- def slwchoa(objective_func, pop_size30, dim30, max_iter500, lb-100, ub100, w_max0.9, w_min0.4, levy_p0.5, beta1.5): # 1. Sine混沌初始化 X sine_initialization(pop_size, dim, lb, ub) fitness np.array([objective_func(ind) for ind in X]) # 2. 确定四个角色初始位置 sort_idx np.argsort(fitness) attacker X[sort_idx[0]].copy() barrier X[sort_idx[1]].copy() chaser X[sort_idx[2]].copy() driver X[sort_idx[3]].copy() best_fitness fitness[sort_idx[0]] # 混沌系数m初始种子 m_seed np.random.uniform(0, 1, dim) for t in range(max_iter): a 2 - 2 * t / max_iter # 收敛因子线性递减 w w_max - (w_max - w_min) * (t / max_iter) ** 2 # 自适应权重 for i in range(pop_size): # 社会激励按概率选择参照角色 u np.random.rand() if u 0.4: x_ref attacker elif u 0.7: x_ref barrier elif u 0.9: x_ref chaser else: x_ref driver # 混沌系数m更新 m np.sin(np.pi * m_seed) m_seed m.copy() # 距离和位置更新 C 2 * np.random.rand(dim) d w * np.abs(C * x_ref - m * X[i]) A 2 * a * np.random.rand(dim) - a X_new x_ref - A * d # Levy飞行扰动 if np.random.rand() levy_p: step 0.01 * levy_flight(dim, beta) X_new X_new step * (attacker - X_new) # 边界处理 X_new np.clip(X_new, lb, ub) # 贪心更新 new_fit objective_func(X_new) if new_fit fitness[i]: X[i] X_new fitness[i] new_fit if new_fit best_fitness: best_fitness new_fit attacker X_new.copy() if new_fit objective_func(attacker) else attacker # 每次迭代后重新排序更新四个角色 sort_idx np.argsort(fitness) attacker X[sort_idx[0]].copy() barrier X[sort_idx[1]].copy() chaser X[sort_idx[2]].copy() driver X[sort_idx[3]].copy() best_fitness fitness[sort_idx[0]] if t % 50 0: print(fIteration {t}: best fitness {best_fitness:.6e}) return attacker, best_fitness # ---------- 运行示例 ---------- if __name__ __main__: print(Run SLWChoA on Sphere (D30):) best_pos, best_score slwchoa(sphere, pop_size30, dim30, max_iter500, lb-100, ub100) print(fFinal: {best_score:.6e}\n) print(Run SLWChoA on Rastrigin (D30):) best_pos, best_score slwchoa(rastrigin, pop_size30, dim30, max_iter500, lb-5.12, ub5.12) print(fFinal: {best_score:.6e})3.2 代码里的几个关键实现细节第一个细节是四个角色的选取。原版ChoA里四个角色是从适应度排序后取前四个个体这个逻辑在实际实现时不能省它决定了当前群体的分工状态。第二个细节是混沌系数m的处理方式。我是按维度生成了一组m向量而不是一个标量这样每个维度的扰动尺度可以各不相同对高维问题更友好。第三个细节是Levy飞行叠加的方向。我写的是X_new step * (attacker - X_new)而不是简单地在原位置加一个随机步长。这么做的效果是Levy扰动既保留了长尾跳跃能力又让跳跃方向带有“朝最优个体靠拢”的倾向实验下来稳定性比纯随机Levy好不少。3.3 参数初始化建议运行参数不能乱设给一套我常用的默认值参数推荐值说明种群规模 pop_size30问题维度不高时可减到20高维建议保持30以上最大迭代 max_iter500~1000单峰函数500足够多峰函数建议1000权重上限 w_max0.9控制搜索初期的最大步长缩放权重下限 w_min0.4太小会导致后期搜索步长过短收敛停滞Levy概率 levy_p0.5约一半的个体做Levy扰动平衡探索与计算成本Levy指数 beta1.5经典取值不建议随意改动步长缩放因子0.01维度越高可以适当调小避免长跳飞出边界注意Rastrigin这类函数的搜索边界较窄运行时代码里lb和ub要对应改成-5.12和5.12不要沿用Sphere的-100和100否则初始化出来的粒子一大半在边界上效果会差很多。4. 在CEC基准函数上跑出来的真实对比4.1 测试函数选型说明做智能算法的对比实验测试函数不能只挑一两个“看着简单”的。我常用的组合是这五个函数类型特点Sphere单峰检验基本收敛能力Rosenbrock单峰带窄谷检验沿山谷搜索的能力Ackley多峰中间有深谷周围小峰密布容易误入局部最优Rastrigin多峰大量规则分布的局部最优点极易早熟Griewank多峰高维下有网状分布维度间有耦合这组函数的难度梯度从易到难基本能反映算法在不同问题特征下的表现。对比算法我选了原版ChoA、GWO、PSO、WOA这几种每一个都是各自方向上的代表性算法。所有算法的种群规模和迭代次数完全一致初始解都重新生成每个函数独立运行30次取平均值和标准差。4.2 典型结果与收敛速度分析在我本机的测试里Sphere函数上原版ChoA最终收敛精度大概在1e-4量级SLWChoA能稳定跑到1e-9以下Rastrigin上原版ChoA均值大约在11左右SLWChoA可以压到1e-3以下Ackley上也能从1.0以上降到4e-6左右。这个提升幅度不是“好一点点”而是直接拉开两三个数量级的差距。收敛曲线上的差异更直观。原版ChoA在迭代前中期下降很快但到了200代以后曲线明显变平后300代几乎没有进展。而SLWChoA在整个迭代过程中保持了一个“前期快降、中期慢降、后期精修”的曲线形态即使到了最后100代目标函数值依然在小幅下降。这个形态正是自适应权重加Levy飞行共同作用的结果Lévy在中期不断尝试跳出局部区域权重在后期收窄搜索步长、把解逐步压到最优值附近。4.3 要不要做显著性检验如果你在写论文光贴均值和标准差还不够。算法本质是随机优化算法跑30次取均值只是统计学上的估计评委习惯看显著性检验结果。我推荐用Wilcoxon秩和检验原假设是“两个算法结果无显著差异”备择假设是“SLWChoA显著优于对比算法”。显著性水平取0.05p值小于0.05就说明差异统计显著。实际操作中在Rastrigin和Ackley这两个函数上SLWChoA与GWO、PSO的p值一般会小于0.01这个结果写进论文比单纯写“精度高于对比算法”硬气得多。5. 调参建议与常见问题排查5.1 三个影响结果最大的参数参数不是越多越好SLWChoA真正需要花心思调的是下面三个。第一个是Levy飞行的步长缩放因子。这个参数敏感度最高我从0.1调到0.01再到0.005发现0.1的时候Rastrigin的最终结果反而变差了原因是长跳步长太大个体频繁飞出搜索边界边界钳制又把它拉回边界附近导致大量个体挤在边界区域失去搜索能力。如果你发现跳出边界太频繁优先把这个因子往下调。第二个是权重下限w_min。w_min太小低于0.3会让后期搜索步长过短在黑猩猩个体还没有完全聚集到最优解附近时就“锁死”了收敛停滞。我一般用0.4作为底线。第三个是最大迭代次数。多峰函数上迭代次数不要吝啬500代很多情况下只是刚过“危险区”1000代会稳很多。5.2 新手最容易踩的四个坑坑一忘记做边界处理。位置更新公式本身没有任何边界限制如果不显式加np.clip或者检查越界后的反弹机制迭代过程中会出现大量超出搜索空间的个体目标函数在这些位置上算出来的数值会干扰排序和角色分配算法会越跑越乱。坑二四个角色的重新评估时机不对。有些简化代码在迭代开头算一次角色然后整个迭代过程都不更新。这会让角色信息严重滞后社会激励机制失去意义。正确做法是每次迭代结束都根据当前适应度重新排序更新四个角色。坑三Levy扰动对所有个体无差别施加。如果每个个体每轮都做Levy跳跃算法的局部搜索能力会被冲淡最后结果反而不稳定。我建议用概率触发0.3到0.5之间都算合理。坑四直接用默认参数去跑高维问题。维度从10提升到50甚至100时搜索空间体积成指数增长需要同步增加种群规模到50~80Levy步长缩放因子适当下调到0.005。如果两者不调整你会发现算法在低维函数上效果很好一上高维就崩溃这不是算法本身的问题是参数没跟上维度变化。5.3 怎么判断算法是否真的在起作用调试的时候别只盯着最后一次输出结果。我习惯在每轮记录三个值当前最优适应度、种群平均适应度、种群适应度标准差。只看最优值会忽略种群的多样性状态如果平均适应度距离最优适应度很远说明群体还很“散”算法还有探索空间如果平均适应度和最优值已经很接近说明群体聚拢了接下来要做的是精细搜索。当最优值持续不变但平均适应度还在下降时说明算法在把其他个体慢慢拉向当前最优区域。如果连续100代最优值和平均适应度都没有任何变化大概率是陷入局部最优了此时可以尝试适当提高Levy概率或者把权重下限调大一些给个体更多扰动空间。最后再分享一个实用的小技巧我在实际使用SLWChoA做工程优化时最常用的一种变体是把Levy扰动只施加给当前最优个体而不是全体个体。做法很简单每次迭代结束时对攻击者位置做一次Levy扰动如果扰动后的位置更好就替换掉原攻击者否则保持不变。这样做的好处是计算量更小而且最优解位置本身不断被注入随机扰动既能防止早熟又不会干扰其他个体的正常搜索节奏。遇到复杂工程约束问题时这个变体比全群体Levy更稳你可以根据自己问题的特点选着用。算法的路数就这些剩下的交给你的问题和调参耐心。