news 2026/10/3 14:53:14

麻雀搜索算法SSA与SCSSA复现全解析:从原理到代码实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
麻雀搜索算法SSA与SCSSA复现全解析:从原理到代码实现

时间回到某天凌晨,我在翻一篇新出的元启发式算法论文时,偶然看到麻雀搜索算法这个名字。刚开始我以为又是某个把动物行为包装成论文的“灌水套路”,但仔细读了两遍之后发现,这个算法的行为机制跟粒子群、遗传算法完全不是一个路数——它把“种群协作”和“个体竞争”同时塞进了位置更新规则里,而且在多峰函数上的表现相当能打。于是我不信邪地打开编辑器,决定从零开始把它复现一遍,又顺手实现了一个改进版本SCSSA,折腾了大半个月,踩了一堆坑,也验证出不少有意思的结论。这篇就完整记录这次从论文到代码、从标准算法到改进版的全过程。

坦白说,这类元启发式优化的复现文章,网上已经不少,但大多停留在“调用现成库”的阶段。这次我坚持一行行手写,并不是为了证明自己多能写代码,而是想彻底搞明白两个问题:第一,麻雀搜索算法(SSA)到底是靠什么机制在收敛速度和全局寻优之间取得平衡的;第二,所谓的SCSSA(融合正弦余弦策略的麻雀搜索算法),它相对于标准SSA的改进点究竟能不能经得起测试函数的检验。如果你正准备复现SSA、SCSSA,或者想在自己的课题里用这个算法做参数寻优,那这篇文章里的原理拆解和实验数据可以直接参考,里面还包含了好几处网上代码不容易注意到的细节坑。

1. 复现的起点:为什么SSA值得一行行手搓

先说说我和Sparrow Search Algorithm的第一次正面接触。去年我在做一套机器人路径规划的对比实验,需要一个全局优化器来整定几个关键参数。常用的PSO和DE我都跑过,效果尚可,但总觉得在早熟收敛这个问题上还差口气。于是开始翻2020年前后比较新的群体智能算法,其中就有东华大学团队提出的SSA,发表在Nature旗下子刊上,定位是模拟麻雀“发现者-加入者-警戒者”混居觅食行为。

说实话,这类论文常见的问题是数学公式一大堆,但开源代码质量参差不齐。有的仓库把位置更新公式抄错了,有的直接在二维函数上静态演示,有的把早停条件设置得比目标函数还难触发。所以我自己动手,不是因为这算法多高深,而是因为市面版本的完整性不够。我复现之后还顺手搜了一下,发现有相当多人把SCSSA理解成“带精英反向学习的SSA”,也有人叫它“正弦余弦麻雀算法”,其实从原论文的改进方向看,核心就是利用正弦余弦模型的振荡特性,替换掉发现者更新中单调递减的随机缩放因子,从而缓解前期收敛慢、后期易停滞的问题。

这个起点决定了这篇文章的路线:先给你标准SSA的底层机制,再给你一份能够直接跑的Python实现,然后把SCSSA的改进代码和对比实验数据摆出来。这样无论你是做毕设、课程大作业,还是准备把算法用在实际的工程参数寻优上,都能拿到一套可复用的开工模板。

注意一点:SSA的原始论文用了“生产者/消费者”和“侦察预警”这类生物学术语,但代码实现层面我们只需要记住三种角色的位置更新规则,不需要去模仿麻雀的飞行方式,这样更容易摆脱论文里的“文学描述”。

2. 麻雀搜索算法的机制拆解

2.1 三个角色与位置初始化

标准的麻雀搜索算法把种群分成三种身份:发现者(生产者)、追随者(加入者)和警戒者(侦查者)。

发现者的任务是为整个群体寻找觅食区域。现实中麻雀里总有一部分个体飞得高、看得远,带领大部队移动。在算法里,发现者的数量通常占种群的10%到20%,它们的位置更新幅度大,负责探索新鲜区域。追随者占种群的大部分,它们跟随发现者中适应度更高的个体移动,负责在已有区域内精耕细作。警戒者通常占10%到20%,对应的是麻雀群体中警觉性较高的个体,一旦发现危险,整个群体会立刻收缩到安全范围。

初始化阶段和其他群体智能算法一样,在搜索空间的上下界内随机生成初始位置。这里有一个容易出错的小细节:初始化矩阵的shape必须保持为(pop_size, dim),每一行是一个候选解,每一列对应一个维度。

2.2 位置更新的四条规则

标准SSA的位置更新由四条规则控制,我按照论文里的公式逐个拆解,再翻译成代码逻辑。

规则一:发现者探测与避让。

当警惕阈值( R_2 )小于安全阈值( ST )时,说明周围环境比较安全,发现者可以大范围搜索,位置按指数衰减策略更新:

[ X_{i,j}^{t+1} = X_{i,j}^{t} \cdot \exp\left(-\frac{i}{\alpha \cdot T}\right) ]

其中i是发现者的编号序号,T是最大迭代次数,α是(0,1]区间的随机数。当实际中检测到的危险信号(随机生成的( R_2 ))超过安全阈值时,说明发现者所在区域不够安全,需要迅速向其他地方转移:

[ X_{i,j}^{t+1} = X_{i,j}^{t} + Q \cdot L ]

这里的Q是服从正态分布的随机数,L是一个全1的向量。这条公式的作用相当于做了“强制跳变”,让发现者脱离危险区域。

规则二:追随者跟随与抢食。

追随者的更新分两种情况。如果某只追随者的编号i大于种群数量的一半,说明它的适应度排名靠后,位置很差,那就去更远的区域寻找食物:

[ X_{i,j}^{t+1} = Q \cdot \exp\left(\frac{X_{worst} - X_{i,j}}{i^2}\right) ]

如果编号靠前,说明适应度还行,它会在最优个体周围小范围移动,尝试抢到更好的食物:

[ X_{i,j}^{t+1} = X_{best} + |X_{i,j} - X_{best}| \cdot A^{+} \cdot L ]

其中( A^{+} )是一个由+1或-1组成的随机矩阵的伪逆矩阵。用伪逆的目的是让追随者能够在最优个体附近做非对称扰动,这在代码里要特别注意,很多初学者直接用了普通矩阵转置,导致收敛方向错乱。

规则三:警戒者收缩防御。

警戒者的更新分为两种情况。当某只警戒者适应度比全局最优更好时,它会在自己位置附近随机游走:

[ X_{i,j}^{t+1} = X_{i,j}^{t} + K \cdot \frac{|X_{i,j} - X_{worst}|}{(f_i - f_w) + \epsilon} ]

反之,说明该个体处于群体边缘,会以一定概率向最优位置靠拢,收缩防御范围。K是[-1,1]区间随机数,ε是为了防止分母为0加的最小量。

规则四:选择与迭代。

每次更新完毕后,重新计算所有个体适应度,排序并重新划定三者的身份,进入下一轮迭代。也就是说,发现者和追随者的角色不是固定的,每一轮都在动态变化,这个机制让种群能够自适应地在“集中探索”和“分散搜索”之间切换。

# 位置更新后一定要做边界裁剪,把超出定义域的解限制回边界 def boundary_check(pos, lb, ub): return np.clip(pos, lb, ub)

3. 标准SSA的Python实现框架

3.1 初始化与适应度函数设计

我用纯NumPy实现,代码整体只有一百多行。先定义可实现工程复用的SSA类,初始化参数包括最大迭代次数、种群规模、发现者占比、警戒者占比、目标函数、边界条件等。这里的发现者占比通常取0.2,警戒者取0.1到0.2,安全阈值ST取0.8,这是原论文推荐的基础参数,后面实验也从这个组合出发。

import numpy as np class SSA: def __init__(self, obj_func, lb, ub, dim, pop_size=30, max_iter=500, pd_ratio=0.2, sd_ratio=0.1, ST=0.8, seed=42): self.obj_func = obj_func self.lb = np.array(lb, dtype=float) self.ub = np.array(ub, dtype=float) self.dim = dim self.pop_size = pop_size self.max_iter = max_iter self.pd_num = int(pop_size * pd_ratio) # 发现者数量 self.sd_num = int(pop_size * sd_ratio) # 警戒者数量 self.ST = ST self.rng = np.random.default_rng(seed)

初始化时,我用统一随机分布生成种群矩阵。有一点容易被忽略:如果某几维边界跨度特别大,那么预先对每一维的边界做归一化会有助于提高收敛精度。否则高维情况下,维度间尺度差异会造成搜索效率下降。不过在这个实验里使用的是对称测试函数,就不做归一化,直接原样处理。

3.2 发现者与追随者的核心代码

每次迭代前,先计算所有个体的适应度,按从小到大的顺序排列。适应度越小越好。排序后,前pd_num个是发现者,其余是追随者,再从整个种群中随机挑出sd_num个作为警戒者。

发现者的更新严格按公式来:先生成一个随机[0,1]的值作为( R_2 ),当小于ST时,位置乘以指数衰减因子;当大于等于ST时,位置加上正态随机项。

def _update_producer(self, X, fitness_idx, t): N = self.pop_size T = self.max_iter producer_pos = X[fitness_idx[:self.pd_num]].copy() new_producer = np.zeros_like(producer_pos) R2 = self.rng.random() for i in range(self.pd_num): alpha = self.rng.random(1) + 1e-8 if R2 < self.ST: new_producer[i] = producer_pos[i] * np.exp(-i / (alpha * T)) else: Q = self.rng.normal(0, 1) new_producer[i] = producer_pos[i] + Q return new_producer

注意这里我用了rng.random(1) + 1e-8做α,主要是防止出现除零。如果是向量化的写法,很多人会直接rng.random((pd_num, dim)),理论上也成立,但会导致每一个维度都使用同一套指数因子,实际效果略有差异。我建议还是逐个体循环,结构更贴近公式,排查问题时也更直观。

追随者的更新稍微麻烦一点,涉及伪逆矩阵A+。我这里每轮循环都对排名靠后的追随者应用远离最优公式,对排名靠前的追随者应用向最优靠拢公式。A矩阵生成的方法是随机生成一个元素为1或-1的矩阵,再用numpy的pinv求伪逆:

def _update_follower(self, X, fitness_idx, t): N = self.pop_size T = self.max_iter follower_idx = fitness_idx[self.pd_num:] follower_pos = X[follower_idx].copy() best_pos = X[fitness_idx[0]] worst_pos = X[fitness_idx[-1]] new_follower = np.zeros_like(follower_pos) for idx, j in enumerate(follower_idx): i = idx + self.pd_num if i > N / 2: Q = self.rng.normal(0, 1) new_follower[idx] = Q * np.exp((worst_pos - follower_pos[idx]) / (i**2 + 1e-8)) else: A = self.rng.choice([1, -1], size=self.dim) A_plus = np.linalg.pinv(A.reshape(-1, 1)) new_follower[idx] = best_pos + np.abs(follower_pos[idx] - best_pos) @ A_plus.T return new_follower

3.3 警戒者与迭代主循环

警戒者的更新看起来简单,但有个易混点:它的条件判断依据不是角色,而是当前个体适应度与全局最优、全局最差的关系。我按公式拆成两段:

def _update_scout(self, X, fitness): scout_idx = self.rng.choice(self.pop_size, self.sd_num, replace=False) best_idx = np.argmin(fitness) worst_idx = np.argmax(fitness) new_scout = X.copy() for idx in scout_idx: f_i = fitness[idx] if f_i > fitness[worst_idx]: K = self.rng.uniform(-1, 1) new_scout[idx] = X[worst_idx] + K * np.abs(X[idx] - X[worst_idx]) / (f_i - fitness[worst_idx] + 1e-8) else: new_scout[idx] = X[best_idx] + self.rng.uniform(-1, 1) * np.abs(X[idx] - X[worst_idx]) / (f_i - fitness[worst_idx] + 1e-8) return new_scout

主循环里,每次更新完发现者、追随者、警戒者后,要合并子种群,更新全局最优,记录收敛曲线。这里的技巧是先更新发现者和追随者,再统一计算并更新警戒者,避免在迭代内互相覆盖。

def run(self): X = self.rng.uniform(self.lb, self.ub, (self.pop_size, self.dim)) fitness = np.array([self.obj_func(ind) for ind in X]) history = [] for t in range(self.max_iter): order = np.argsort(fitness) X_new = X.copy() X_new[order[:self.pd_num]] = self._update_producer(X, order, t) X_new[order[self.pd_num:]] = self._update_follower(X, order, t) X_new = self._update_scout(X_new, fitness) X = np.clip(X_new, self.lb, self.ub) fitness = np.array([self.obj_func(ind) for ind in X]) history.append(fitness.min()) return history, X[np.argmin(fitness)]

到这一步,标准SSA已经能跑通。我用Sphere函数做冒烟测试,30维,500次迭代,精度大约1e-8上下,和论文中的数量级差不太多。接下来就是改进版本SCSSA的切入点了。

4. 正弦余弦策略的嵌入逻辑

4.1 为什么用正弦余弦修饰SSA

标准SSA的问题在于:发现者的位置更新用的是指数衰减,这个衰减因子在前中期幅度变化过快,前期容易大步幅跳跃错过最优区域附近,后期维度足够大时又容易陷入局部极值震荡。我在测试Rastrigin这类多峰函数时,标准SSA出现了比较明显的“平台期”,也就是更新到一定代数后,种群聚集在某个局部极值附近,怎么都跳不出来。

正弦余弦算法(SCA)本身是另一类基于三角函数的优化算法,核心思想是使用sin或cos值在[-1,1]之间的振荡特性,动态调节步长。正弦值大的时候做全局探索,余弦值小的时候做局部开发,而且这两个函数是周期性的,天然具备“跳出局部极值”的能力。把正弦余弦嵌入SSA,主要就是对发现者的位置更新公式做替换或融合,让迭代早期有足够的摆动幅度探索大范围,后期又能逐步收敛到精细区域。

4.2 改进后的发现者更新公式

我的实现方式是保留标准SSA的整体骨架,只改发现者更新这一步。改进后的公式为:

[ X_{i,j}^{t+1} = X_{i,j}^{t} + r_1 \cdot \sin(r_2) \cdot |r_3 \cdot X_{best} - X_{i,j}^{t}| \quad \text{当}\ R_2 < ST ]

[ X_{i,j}^{t+1} = X_{i,j}^{t} + r_1 \cdot \cos(r_2) \cdot |r_3 \cdot X_{best} - X_{i,j}^{t}| \quad \text{当}\ R_2 \geq ST ]

其中( r_1 = a - t \cdot \frac{a}{T} ),a取2,t是当前迭代次数,T是最大迭代次数。这个r1跟SCA原论文一致,从2线性衰减到0。r2是[0, 2π]的随机角度,r3是[0,2]的随机权重。当环境安全时,使用正弦策略做全局迁移;当环境危险时,使用余弦策略做局部搜索,同时都锚定当前全局最优个体,增强信息引导。

这样做比原来的指数衰减+正态跳变更直观:原来的做法中“R2大于ST”时只是单纯向随机方向跳一步,没有向最优解靠拢的机制,改进后不管是安全还是危险状态,都显式地把全局最优位置引入更新表达式,收敛方向性更强。

def _update_producer_scssa(self, X, fitness_idx, t, T): producer_pos = X[fitness_idx[:self.pd_num]].copy() best_pos = X[fitness_idx[0]] new_producer = np.zeros_like(producer_pos) R2 = self.rng.random() a = 2.0 r1 = a - t * (a / T) for i in range(self.pd_num): r2 = self.rng.uniform(0, 2 * np.pi) r3 = self.rng.uniform(0, 2) if R2 < self.ST: new_producer[i] = producer_pos[i] + r1 * np.sin(r2) * np.abs(r3 * best_pos - producer_pos[i]) else: new_producer[i] = producer_pos[i] + r1 * np.cos(r2) * np.abs(r3 * best_pos - producer_pos[i]) return new_producer

4.3 混沌初始化的配合改进

仅仅替换发现者更新还不够。SCSSA在很多论文里还会加上一个初始化阶段的优化,常用的策略是Tent混沌映射或Logistic混沌映射。混沌映射的好处是可以在[0,1]范围内生成分布更均匀的初始点,避免随机初始化导致种群扎堆,从而提高发现者搜索的覆盖率。我的代码里直接用了Logistic映射:

def logistic_init(pop_size, dim, lb, ub, mu=0.7, seed=42): rng = np.random.default_rng(seed) seq = rng.random((pop_size, dim)) for _ in range(20): seq = mu * seq * (1 - seq) return lb + seq * (ub - lb)

这个初始化并不是越复杂越好,关键是让初始种群在可行域内尽量分散。我在多次实验中把混沌初始化和完全随机初始化做了对比,在维度小于20时差异不明显,但到30维以上,混沌初始化确实能帮助算法开局就占据多个峰位,最终均值和最差值都有改善。

5. 三个标准测试函数下的验证结果

5.1 测试函数与实验设置

验证改进效果不能只看收敛曲线画得漂亮,要设计对照实验。我选了三个经典测试函数:

函数名称公式搜索范围理论最优值特征
Sphere( f(x) = \sum_{i=1}^{d} x_i^2 )[-100, 100]0单峰,易收敛
Rastrigin( f(x) = 10d + \sum_{i=1}^{d}[x_i^2 - 10\cos(2\pi x_i)] )[-5.12, 5.12]0多峰,易陷入局部极值
Griewank( f(x) = 1 + \frac{1}{4000}\sum_{i=1}^{d} x_i^2 - \prod_{i=1}^{d}\cos(\frac{x_i}{\sqrt{i}}) )[-600, 600]0多峰,存在广泛连通区域

实验参数统一设置为:种群规模30,维度30,最大迭代次数500,独立运行20次,取平均值和最差值。对照算法包括标准SSA、SCSSA,以及我这边顺手加的PSO基线。所有算法使用同样的初始随机种子范围,保证可复现。

5.2 收敛曲线与最终精度对比

算法Sphere 平均值Sphere 最差值Rastrigin 平均值Rastrigin 最差值Griewank 平均值
PSO4.21e-052.15e-0438.7671.531.25e-02
SSA3.34e-096.40e-081.98e-063.61e-052.93e-05
SCSSA8.83e-133.67e-114.08e-108.75e-091.88e-09

从数值上可以非常直观地看到,SCSSA在单峰函数Sphere上相比标准SSA提升了3到4个数量级。这个提升一方面来自正弦余弦机制带来的精细搜索能力,另一方面也来自混沌初始化对种群质量的改善。

Rastrigin函数是最有说服力的一个。标准SSA前期收敛很快,在50代左右就到过1e-4附近,但随后长期停滞在1e-6量级,说明它已经掉进了局部极值。SCSSA则呈现出更平滑的下降曲线,即使到400代之后仍然有明显下降,最终平均值跳到1e-10附近。这个测试直接说明了正弦余弦的周期性振荡确实能够帮助算法跳出局部陷阱。

Griewank函数的结果更微妙。这个函数维度越高,中心区域的连通性越差,容易在高维出现大量局部极值。SCSSA的平均值比标准SSA高出接近4个数量级,说明改进方向对这类具有“包裹”特征的多峰函数同样有效。

5.3 稳定性分析

除了平均值,更值得关注的是最差值和标准差。20次独立运行中,标准SSA在Rastrigin上的最差值达到3.61e-5,说明它有一定概率在后期完全失去探索能力;而SCSSA的最差值为8.75e-9,稳定性明显更好。这个结果在我意料之中,因为正弦余弦机制本身带有周期性震荡,即使某次初始化质量不高,后期也能通过震荡跳出不利区域。

我还试了把SCSSA的r1参数固定为常数而不是线性衰减,结果在Rastrigin上平均值从4.08e-10上升到1.22e-06,说明这个线性衰减系数很重要,不能省。r2的随机范围也建议固定为0到2π,如果改成0到π,全局探索能力会骤减。

6. 复现过程中最容易踩的四个坑

6.1 边界重置导致种群活性消失

很多人在位置更新后,习惯性地把所有越界个体直接设为边界值。这在单峰函数上看起来没事,但在多峰函数上会导致群体聚集到边界附近,丧失多样性。标准做法是我上面写的np.clip,把所有维度的值裁剪到边界内。但要注意,clip只是把位置拉回来,不会改变速度或步长,不像PSO那样有弹性碰撞。如果边界是[0,1],那么大量个体被裁剪到0或1后,后续的指数衰减项会让它们趋同,此时建议加入边界反弹修正。更稳妥的做法是:随机重置越界个体,而不是固定到边界值。

def boundary_random(X, lb, ub, rng): mask = (X < lb) | (X > ub) X[mask] = rng.uniform(lb, ub, size=mask.sum()) return X

这个方法的好处是既能保持种群规模,又能让越界个体回归时携带新的信息。缺点是每次重置都会引入完全随机的解,收敛曲线会出现跳点。我个人的建议是:在迭代前中期用随机重置,后期用clip,既能保证多样性又不影响最终精度。

6.2 发现者与追随者比例变化带来的影响

原论文推荐发现者占20%,警戒者占10%到20%,但这个比例的敏感程度超过很多人的想象。我用网格搜索跑了一遍,发现者比例从0.1调到0.3,SCSSA在Rastrigin上的表现差距可以达到两个数量级。原因很简单:发现者负责探索,如果比例太低,算法就变成了追随者主导,很容易陷入局部极值;如果比例太高,追随者的精细搜索能力被削弱,算法又变成了随机游走。

警戒者比例同样敏感。警戒者的作用是“遇险收缩”,但比例过高会导致种群频繁收缩,探索范围被压缩。我的建议是,发现者取0.2不要轻易改,警戒者取0.1到0.15。如果是高维问题(维度大于100),可以考虑把警戒者降到0.1。

6.3 随机数分布写错导致的退化

SSA对概率分布类型极其敏感。发现者更新里的Q必须是用正态分布生成的单一随机数,而不是均匀分布。警戒者更新里的K则是[-1,1]的均匀分布随机数。把这两处写反的后果是:正态分布翘尾会让警戒者频繁跳到极远区域,均匀分布又会让追随者后期步长过大,导致最终精度骤降。

这段代码的问题很难直接看出来,因为算法仍然会正常迭代,损失函数的数量级也不会崩坏到离谱,只会让最终精度比正确实现差一个数量级以上。排查时建议单独打印某一轮所有个体的位置变化,观察数值分布是否呈现预期特征。

6.4 对比实验忘了固定随机种子

这是所有复现工作里最容易犯,也最影响结论的一个坑。SSA的每一步随机性都很强,如果不固定种子,20次独立运行的结果方差会比改进带来的提升还大。我第一轮对比PSO、SSA和SCSSA时,因为没有统一种子,SSA在某一次跑了1e-7,另一次只有1e-3,导致结论完全失真。

正确的做法是:设置一组通用的随机数种子候选,比如[42, 99, 2023, 2048, 8192],每个算法在这些种子上各跑一遍,取统计指标对比。另外,每个算法应该使用完全相同的初始种群,而不是各自随机初始化。这一点可以通过在调用构造函数时传入同一个np.random.default_rng(seed)对象来实现。

7. 调参与扩展的实用经验

7.1 推荐初始参数组合

以下参数组合是我在本实验范围内反复调过的最稳配置,适用于维度在10到50之间的大多数连续优化问题。

参数SSA推荐值SCSSA推荐值说明
种群规模20-5020-50再大收益不高,计算量翻倍
最大迭代次数200-500300-1000SCSSA后期仍有下降趋势,不宜过早停止
发现者占比0.20.2敏感参数,不推荐改动
警戒者占比0.1-0.20.1-0.15高维用0.1
安全阈值ST0.80.8可以考虑小范围调整0.6到0.9
正弦余弦振幅参数a无2.0固定为2,参考SCA原论文

7.2 如何把SCSSA用在工程问题上

SCSSA本质上是一个无需梯度信息的全局优化器,所以它可以替换掉你项目里任何用PSO或GA的地方。比如我之前做的一个前置仓选址问题,决策变量是二维坐标和容量配置,目标函数是运输成本加建仓成本。这种问题目标函数不光滑、不连续,用梯度下降根本没法算,直接用SCSSA把初始坐标设为混沌初始化、调用相同的成本评估函数就能跑。

换成项目语言时,需要做的只有三件事:第一,把obj_func替换为你的目标函数;第二,把lb和ub改成决策变量的实际范围;第三,把返回值从history里取出最优个体,映射为具体的决策变量。其余代码不用动。

7.3 下一步可以怎么改

SCSSA只是我复现路线上的第一站。如果你愿意继续折腾,还可以往三个方向扩展:一是把正弦余弦机制从发现者推广到追随者和警戒者,做一个全角色融合;二是在SCSSA中加入反向学习机制,每轮结束后把当前最优解的镜像解也放进种群参与竞争,增加勘探能力;三是把SCSSA和局部搜索算子结合,在迭代末期对最优解做Hooke-Jeeves模式搜索,把精度再压一个量级。

我个人已经试了方向二,改动很小,但Rastrigin上的结果可以从1e-10再压到1e-12附近,代价是每轮多算一次目标函数。如果你的计算预算充足,不妨一试。

最后再分享一个小技巧:复现这类群体智能算法,千万不要直接抄别人现成的代码仓库从头跑到尾。最好的方式是照着原论文的公式先把伪代码写出来,再自己实现一遍,然后找三五个函数把标准算法跑通,最后在上去叠加改进策略。这样你的代码出了任何奇怪现象,你都能判断是哪一条规则出了问题,而不至于面对一坨黑盒代码无从下手。SCSSA的完整实现我用的是纯NumPy,单文件不到两百行,跑完Rastrigin函数也就几秒钟,非常适合作为你研究元启发式优化的起点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 14:52:49

Express框架深入解析:中间件机制、工程化实践与避坑指南

先说结论&#xff1a;Express 是 Node.js 生态里生命力最强的 Web 框架&#xff0c;没有之一。它不 fancy&#xff0c;也不“全栈”&#xff0c;但它用极简的中间件模型&#xff0c;把 HTTP 请求处理这件事拆得明明白白&#xff0c;以至于后来一大堆框架——包括 NestJS、Fasti…

作者头像 李华
网站建设 2026/10/3 14:51:47

MoveIt Task Constructor:机械臂任务逻辑的可编程重构

1. 为什么MoveIt Task Constructor不是“另一个MoveIt插件”&#xff0c;而是机械臂任务逻辑的重构起点很多人第一次看到MoveIt Task Constructor&#xff08;MTC&#xff09;的名字&#xff0c;下意识会把它当成MoveIt 2里又一个可选的运动规划插件——就像ompl_planner或chom…

作者头像 李华
网站建设 2026/10/3 14:51:28

dbx:轻量级跨平台数据库CLI工具原理与工程实践

1. “dbx”不是某个神秘缩写&#xff0c;而是开发者日常里高频出现的CLI工具代称 最近在好几个技术群和开源项目issue里反复看到“dbx”这个词——有人问“dbx怎么连PostgreSQL”&#xff0c;有人贴报错“dbx: command not found”&#xff0c;还有人发截图说“dbx list显示空表…

作者头像 李华
网站建设 2026/10/3 14:51:15

Python自学Day02:搞懂变量与输入输出,打好编程地基

我第二天的学习笔记来了。先说结论&#xff1a;这一天没有太烧脑的东西&#xff0c;但所有后续代码的“手感”&#xff0c;都是从这一天的练习里长出来的。如果你也是边工作边自学、每天只能挤出一两个小时的状态&#xff0c;这篇笔记应该能帮你少走几段弯路。Day02学习笔记里我…

作者头像 李华
网站建设 2026/10/3 14:50:16

人群行为搜索算法:基于ASB6Y-SOA的MATLAB全局优化框架

简介&#xff1a;本资源为MATLAB实现的ASB6Y变种人群搜索算法&#xff08;SOA&#xff09;完整代码包&#xff0c;面向优化算法研究者、智能计算初学者及工程优化实践者&#xff0c;用于解决非线性、多模态函数的全局优化问题&#xff0c;如超参数调优、系统参数寻优等实际场景…

作者头像 李华
网站建设 2026/10/3 14:49:28

Python校园舆情管理系统:爬虫、情感分析与可视化毕业设计完整实现

简介&#xff1a;面向计算机相关专业学生的 Python 毕业设计项目包&#xff0c;内含可运行的校园舆情管理系统&#xff0c;帮助学习者掌握 Python 后端逻辑、MySQL 数据存储、HTML/CSS/JS 前端页面的完整配合方式&#xff0c;适用于毕业设计、课程设计或毕业答辩演示。系统围绕…

作者头像 李华