简介这是一份面向PSO粒子群算法学习者与研究者的改进算法实例源码包覆盖基本PSO、惯性权重动态调整、学习因子调整、约束处理以及混沌/遗传操作引入五类典型改进方向适合用于理解算法变体、对比不同策略的寻优性能也可作为课程设计或论文实验的参考实现。压缩包共6个文件全部为Matlab的m脚本/函数含测试函数与不同改进版本的主程序整体仅3KB结构小巧清晰便于逐个运行调试。目前已有618人学习下载。通过运行源码可直观观察各改进版在测试函数上的收敛行为对比自适应权重、压缩因子、线性递减权重与随机权重等策略的效果并可直接改写目标函数迁移到自己的优化场景中快速搭建算法对比平台。1. 粒子群改进算法源码包这5种改进到底在改什么值不值得跑如果你手上刚拿到一份「PSO粒子群5种改进算法实例源码.rar」大概率遇到过这种场景解压出来五六个文件每个文件名都带着 Weight、Factor、Adaptive、Topology 之类的词跑一遍发现有的比标准 PSO 收敛快有的反而更差。这不是源码有 bug而是每种改进都在针对性解决标准粒子群的某一类病——早熟、震荡、后期不收敛。这个标题里的 rar 包本质是五套可对照的改进模板不是拿来就能用的银弹。这篇笔记我会先立住标准 PSO 的基准实现再把 5 种改进逐行拆开讲清楚每处修改的数学动机、参数取值和失效边界最后给你一套避坑清单和复现验证的方法。适合正在做优化算法对比实验、或者想把 PSO 用到调度、特征选择、路径规划这类实际问题上的开发者。2. 先立基准手写标准 PSO才看得懂改进在哪2.1 标准 PSO 的速度-位置更新公式与三个必调参数粒子群优化Particle Swarm OptimizationPSO的核心只有两条公式。第 t 代时第 i 个粒子的速度 v 和位置 x 按下面的方式更新v_i(t1) w * v_i(t) c1 * r1 * (pbest_i - x_i(t)) c2 * r2 * (gbest - x_i(t))x_i(t1) x_i(t) v_i(t1)其中 pbest 是粒子自己历史最优位置gbest 是整个种群当前最优位置。三个参数决定算法性格惯性权重 w 控制上一代速度保留多少w 大则全局搜索强c1 是自我认知系数拉着粒子往回历史最优c2 是社会认知系数拉着粒子往全局最优靠。r1 和 r2 是 [0,1] 均匀随机数给搜索引入随机性。这三者的配合一旦失衡要么粒子飞出去再也拉不回来要么种群迅速挤到一点然后停滞。标准 PSO 的使用门槛不高但调参没有一次性公式。一组最常用、最不容易翻车的起步配置是 w0.8c1c22.0种群规模 30迭代 500 代。这三个参数不是拍脑袋定的w 取 0.8 能在探索和收敛之间留出缓冲c1c22.0 让两个吸引力大致均衡。你拿到改进源码后第一步不是看它改了什么而是先把标准 PSO 跑熟否则后面所有对比都没有参照系。2.2 用 25 行 Python 跑通标准 PSO 的最小复现import numpy as np def sphere(x): return np.sum(x ** 2) # 测试函数球函数全局最优在原点 def standard_pso(func, dim30, n_particles30, w0.8, c12.0, c22.0, max_iter500, bound10.0): # 位置和速度初始化速度范围取边界值的 20%避免一开始就飞太远 x np.random.uniform(-bound, bound, (n_particles, dim)) v np.random.uniform(-0.2 * bound, 0.2 * bound, (n_particles, dim)) pbest x.copy() pbest_val np.array([func(p) for p in x]) gbest pbest[np.argmin(pbest_val)] gbest_val pbest_val.min() history [] # 记录每代最优值画收敛曲线用 for _ in range(max_iter): r1, r2 np.random.rand(n_particles, dim), np.random.rand(n_particles, dim) v w * v c1 * r1 * (pbest - x) c2 * r2 * (gbest - x) x x v x np.clip(x, -bound, bound) # 越界粒子拉回边界 vals np.array([func(p) for p in x]) for i in range(n_particles): if vals[i] pbest_val[i]: pbest_val[i] vals[i] pbest[i] x[i].copy() if pbest_val.min() gbest_val: gbest_val pbest_val.min() gbest pbest[np.argmin(pbest_val)].copy() history.append(gbest_val) return gbest_val, gbest, history best_val, best_x, hist standard_pso(sphere) print(最优值:, best_val)这段实现了标准 PSO 的完整流程。逻辑说明先初始化位置和速度然后进入迭代循环先用随机数 r1、r2 更新速度再更新位置越界后拉回边界随后逐个粒子和 pbest 比较、选出新的 gbest整个过程中把每代最优值存进 history。参数说明dim 是问题维度测试函数用 30 维bound 是搜索空间边界Sphere 函数自变量范围通常定在 [-10, 10]n_particles30、max_iter500 是控制计算量的总预算改动时要注意总评估次数等于粒子数乘迭代次数。2.3 基准跑出来的三种经典毛病早熟、震荡、收敛慢标准 PSO 跑 Sphere 函数没问题但换到 Rastrigin、Ackley 这类多峰函数三个典型毛病立刻现形。第一个是早熟种群在迭代初期就被某个局部最优吸引所有粒子挤过去后速度趋近于零gbest 再也变不了。第二个是震荡w 或 c2 设置偏大时粒子在最优解附近来回穿越收敛曲线变成锯齿状经常是最后 200 代都在同一个数量级波动。第三个是收敛慢粒子数太少或初始化范围太窄前期探索不充分后期只能在一个小区域里反复修修补补。这三个毛病不是互相独立的。早熟往往伴随多样性丢失而多样性丢失又会加剧收敛慢震荡则多半是速度更新没有阻尼机制。你会发现标题里那 5 种改进本质上就是针对这三类病开药方——惯性权重策略治震荡和收敛慢收缩因子治速度失控学习因子自适应治早熟拓扑结构治多样性缺失混合策略则是综合治疗。这就是为什么一定要先跑基准你知道基线长什么样才看得出来改进算法到底改了哪里、有没有效。3. 拆解 5 种改进算法惯性权重、收缩因子、自适应学习因子与两种结构变体3.1 改进一惯性权重线性递减w 从 0.9 降到 0.4 的代码姿势标准 PSO 的 w 是常数但实际搜索中前期希望 w 大一些保持探索后期希望 w 小一些精细收敛。惯性权重线性递减Linearly Decreasing Weight的做法是让 w 随迭代代数线性从 w_max 降到 w_mindef w_decreasing_pso(func, dim30, n_particles30, w_max0.9, w_min0.4, c12.0, c22.0, max_iter500, bound10.0): x np.random.uniform(-bound, bound, (n_particles, dim)) v np.random.uniform(-0.2 * bound, 0.2 * bound, (n_particles, dim)) pbest x.copy() pbest_val np.array([func(p) for p in x]) gbest pbest[np.argmin(pbest_val)] gbest_val pbest_val.min() history [] for t in range(max_iter): w_t w_max - (w_max - w_min) * t / max_iter # 当前代数对应的权重 r1, r2 np.random.rand(n_particles, dim), np.random.rand(n_particles, dim) v w_t * v c1 * r1 * (pbest - x) c2 * r2 * (gbest - x) x np.clip(x v, -bound, bound) vals np.array([func(p) for p in x]) improve vals pbest_val pbest[improve] x[improve] pbest_val[improve] vals[improve] if pbest_val.min() gbest_val: gbest_val pbest_val.min() gbest pbest[np.argmin(pbest_val)].copy() history.append(gbest_val) return gbest_val, gbest, history逻辑说明相比标准 PSO唯一的改动就是每代先计算 w_t随着迭代进行w_t 从 0.9 线性衰减到 0.4其他逻辑完全复用。参数说明w_max0.9 和 w_min0.4 是经过大量实验沉淀的经典区间0.9 保证前期探索力度0.4 是保证粒子不至于完全失去速度的下限低于 0.3 会让粒子在后期几乎没有位移能力。实际使用时如果你的迭代次数只有 100 代递减的斜率会非常陡这种情况下直接固定 w0.7 可能表现更稳。3.2 改进二收缩因子法用 χ 压住速度震荡Clerc 提出的收缩因子法Constriction Factor不走调参路线而是从数学上约束速度更新方程。它在速度更新前乘以一个收缩系数 χ让速度序列在理论上保证收敛不再依赖人工调 wdef constriction_pso(func, dim30, n_particles30, c12.05, c22.05, max_iter500, bound10.0): phi c1 c2 # 收缩因子计算公式要求 phi 4 才有实数解 chi 2.0 / abs(2.0 - phi - np.sqrt(phi**2 - 4.0 * phi)) x np.random.uniform(-bound, bound, (n_particles, dim)) v np.random.uniform(-0.2 * bound, 0.2 * bound, (n_particles, dim)) pbest x.copy() pbest_val np.array([func(p) for p in x]) gbest pbest[np.argmin(pbest_val)] gbest_val pbest_val.min() history [] for _ in range(max_iter): r1, r2 np.random.rand(n_particles, dim), np.random.rand(n_particles, dim) # 速度更新后整体乘以收缩因子等效于同时缩小惯性项和吸引项 v chi * (v c1 * r1 * (pbest - x) c2 * r2 * (gbest - x)) x np.clip(x v, -bound, bound) vals np.array([func(p) for p in x]) improve vals pbest_val pbest[improve] x[improve] pbest_val[improve] vals[improve] if pbest_val.min() gbest_val: gbest_val pbest_val.min() gbest pbest[np.argmin(pbest_val)].copy() history.append(gbest_val) return gbest_val, gbest, history逻辑说明收缩因子法把标准 PSO 的惯性项、个体项和社会项一并乘以 χχ 的取值取决于 c1 和 c2 的和。当 c1c22.05 时phi4.1χ 约等于 0.729这意味着每代速度会被压掉约 27%从源头防止粒子飞出边界或来回震荡。参数说明这个改进里 c1 和 c2 不再是你随手填的数phi 必须大于 4否则根号内为负、χ 是复数速度更新完全失效——这是初学者最容易踩的硬坑。一般建议直接使用 c1c22.05 的经典配置不要为了模仿标准 PSO 改成 2.0因为 2.02.04.0恰好不满足条件。3.3 改进三学习因子自适应前期全局搜索后期局部精修自适应学习因子的思路是把 c1 和 c2 从常数改成随时间变化的变量。前期加大 c1 让粒子更多地向自己的历史最优学习相当于鼓励探索后期加大 c2 让粒子更多地向全局最优靠拢加快收敛def adaptive_c_pso(func, dim30, n_particles30, w0.8, max_iter500, bound10.0): c1_start, c1_end 2.5, 0.5 # c1 从大变小 c2_start, c2_end 0.5, 2.5 # c2 从小变大 x np.random.uniform(-bound, bound, (n_particles, dim)) v np.random.uniform(-0.2 * bound, 0.2 * bound, (n_particles, dim)) pbest x.copy() pbest_val np.array([func(p) for p in x]) gbest pbest[np.argmin(pbest_val)] gbest_val pbest_val.min() history [] for t in range(max_iter): ratio t / max_iter c1_t c1_start (c1_end - c1_start) * ratio c2_t c2_start (c2_end - c2_start) * ratio r1, r2 np.random.rand(n_particles, dim), np.random.rand(n_particles, dim) v w * v c1_t * r1 * (pbest - x) c2_t * r2 * (gbest - x) x np.clip(x v, -bound, bound) vals np.array([func(p) for p in x]) improve vals pbest_val pbest[improve] x[improve] pbest_val[improve] vals[improve] if pbest_val.min() gbest_val: gbest_val pbest_val.min() gbest pbest[np.argmin(pbest_val)].copy() history.append(gbest_val) return gbest_val, gbest, history逻辑说明每一代根据当前进度 ratio 线性插值出这一代的 c1_t 和 c2_t这个变化过程与惯性权重递减的曲线类似但作用对象不同——它不是压速度而是改变粒子的行为偏好。参数说明这里把 w 固定为常数 0.8这样能单独观察 c1、c2 动态变化的效果避免多个变量同时改动导致归因困难。c1 从 2.5 降到 0.5、c2 从 0.5 升到 2.5 是一组常用配置你可以在工程里把这四个端点值都暴露出来做参数实验。这个改进对多峰函数效果明显但要小心后期 c2 过大导致粒子被 gbest 吸死早熟问题在最后几十代可能卷土重来。3.4 改进四环形拓扑 LPSO用局部邻居防早熟标准 PSO 用的是全局拓扑每个粒子都朝向整个种群的最优 gbest信息传递快但容易早熟。局部拓扑Local PSOLPSO改用环形结构每个粒子只看自己相邻的几个粒子不直接看全局最优def local_pso(func, dim30, n_particles30, w0.8, c12.0, c22.0, max_iter500, bound10.0, neighbor_radius1): x np.random.uniform(-bound, bound, (n_particles, dim)) v np.random.uniform(-0.2 * bound, 0.2 * bound, (n_particles, dim)) pbest x.copy() pbest_val np.array([func(p) for p in x]) history [] for _ in range(max_iter): for i in range(n_particles): # 取环形拓扑上左右各 neighbor_radius 个邻居加上自己 neighbor_idx [(i delta) % n_particles for delta in range(-neighbor_radius, neighbor_radius 1)] # 邻居中的个体最优最优值作为当前粒子的社会引导 nb_vals [pbest_val[j] for j in neighbor_idx] leader neighbor_idx[np.argmin(nb_vals)] r1, r2 np.random.rand(dim), np.random.rand(dim) v[i] w * v[i] c1 * r1 * (pbest[i] - x[i]) c2 * r2 * (pbest[leader] - x[i]) x[i] np.clip(x[i] v[i], -bound, bound) val func(x[i]) if val pbest_val[i]: pbest_val[i] val pbest[i] x[i].copy() history.append(min(pbest_val)) return min(pbest_val), pbest[np.argmin(pbest_val)], history逻辑说明速度更新中原来的 gbest 换成了 pbest[leader]leader 是从环形邻居里选出的适应度最好那个粒子。注意这里的 j 索引是粒子编号而不是坐标邻居关系按粒子下标首尾相连形成闭环。参数说明neighbor_radius 是邻居半径取 1 时每个粒子只和左右两个邻居比种群规模 30 时收敛慢但探索强取 15 时邻居覆盖整个种群退化为标准全局 PSO。实际调参时可以从邻居半径为 2 或 3 开始如果陷入局部最优再调小半径。3.5 改进五混合策略引入变异与重启跳出局部最优第五种改进的方向是把 PSO 和其他搜索机制混合最轻量的做法是加变异算子。遗传算法里的小概率变异思想移植到 PSO 中每代以一定概率把粒子的某个维度重新随机赋值相当于给种群打一针多样性强心剂def mutation_pso(func, dim30, n_particles30, w0.8, c12.0, c22.0, max_iter500, bound10.0, mutation_rate0.05): x np.random.uniform(-bound, bound, (n_particles, dim)) v np.random.uniform(-0.2 * bound, 0.2 * bound, (n_particles, dim)) pbest x.copy() pbest_val np.array([func(p) for p in x]) gbest pbest[np.argmin(pbest_val)] gbest_val pbest_val.min() history [] for _ in range(max_iter): r1, r2 np.random.rand(n_particles, dim), np.random.rand(n_particles, dim) v w * v c1 * r1 * (pbest - x) c2 * r2 * (gbest - x) x x v # 变异阶段每个维度的每一维独立判断是否重新随机初始化 mask np.random.rand(n_particles, dim) mutation_rate x[mask] np.random.uniform(-bound, bound, np.count_nonzero(mask)) x np.clip(x, -bound, bound) vals np.array([func(p) for p in x]) improve vals pbest_val pbest[improve] x[improve] pbest_val[improve] vals[improve] if pbest_val.min() gbest_val: gbest_val pbest_val.min() gbest pbest[np.argmin(pbest_val)].copy() history.append(gbest_val) return gbest_val, gbest, history逻辑说明变异发生在速度更新和边界处理之后逐维度判断是否触发变异触发就把该维度重新初始化为搜索空间内的随机值然后继续正常评估。参数说明mutation_rate 通常取 0.01 到 0.1 之间30 维下取 0.05 意味着每个粒子平均有 1.5 个维度被重置。低于 0.01 几乎没作用高于 0.1 会破坏 PSO 已有的收敛趋势导致后期最优值反复跳动。这个改进的优势在于不引入额外计算量适合嵌入到任何一个前四种改进的基础上叠加使用。4. 参数怎么设5 种改进算法的调参顺序与验证方法4.1 一张表看懂五组核心参数范围、推荐值、失效表现给参数做实验之前先明确每个参数的有效区间和失效边界。我把 5 种改进涉及的核心参数整理成一张表你在调参时对照着看能少走很多弯路参数有效范围推荐起步值失效表现惯性权重 w常数0.3 ~ 1.00.8小于 0.3 前期探索极弱大于 1.0 速度发散w_max / w_min线性递减w_max 0.8~1.0w_min 0.3~0.50.9 / 0.4w_min 过小导致后期完全停滞收缩因子 χ随 c1、c2 变化phi 需大于 4c1c22.05phi 小于等于 4 时计算失败学习因子端点c1 下降端 2.5→0.5c2 上升端 0.5→2.5与推荐一致两端相距太小则失去自适应效果邻居半径1 ~ 种群数/22半径太大退化为全局 PSO变异率0.01 ~ 0.10.05大于 0.1 破坏收敛小于 0.01 无效果这张表的判断标准是收敛曲线形状和最终最优值。失效表现那一列是经验值比数值边界更有参考价值——你看到收敛曲线不下降、或者出现周期性的突然跳变第一时间就该怀疑对应的参数越界了。4.2 调参顺序先固定什么再动什么看什么指标调参最忌讳一上来五个参数一起改最后出了问题都不知道怪谁。我一般按三段式来调第一步固定基础配置种群 30、迭代 500、w0.8、c1c22.0用标准 PSO 在目标函数上跑出基线收敛曲线和最优值第二步做单因素试验比如只把固定 w 换成线性递减其余不动记录对比曲线确认这一项改进是否有效第三步再叠加下一项改进每次只加一个变量。如果你拿到的源码里本身是复合改进比如既改了权重又加了变异先用控制变量法把每一项单独跑一遍再做组合可以看清楚每个模块各自的贡献。判断改进效果看的指标不是最后那一个数而是三个维度最终最优值的数量级、收敛到该值的迭代代数、以及多次运行的成功率。只看最终值容易被单次随机性误导一只特别幸运的粒子可能掩盖算法的真实水平只看收敛曲线则看不出算法的稳定性。我通常跑 20 次独立的实验统计平均最优值、标准差和最优命中次数三个指标综合评判。4.3 验证改进是否有效函数值对比、收敛曲线、成功率一个可复现的验证流程长这样选择 3 个测试函数包括单峰的 Sphere、多峰的 Rastrigin 和带大量局部最优的 Ackley每个算法在每个函数上独立跑 20 次每次运行记录每代 gbest 值最后画出平均收敛曲线输出三组统计指标即平均最优值、最好值、标准差。三个测试函数必须分开看因为一种改进可能在 Rastrigin 上效果显著、在 Ackley 上反而变差。比较收敛曲线时有个容易犯的错横轴不要用迭代代数要用评估次数粒子数乘以代数。因为改进算法每代内部计算量可能不同而评估函数才是最贵的操作。统一评估次数之后对比才公平。如果改进后的曲线整体在改进前曲线下方且最终值明显更优才算有效改进如果只是前面快、后面被反超说明改进引入了过强的探索或过弱的收敛需要回调参数。5. 粒子群改进算法源码实战避坑5 个高频翻车点5.1 越界处理方式不同结果差一个数量级现象同一份改进算法把越界粒子拉回边界和把越界粒子反弹回搜索空间最终最优值差了十倍以上甚至一个收敛一个发散。原因拉回边界clip会在边界附近堆积粒子改变种群分布反弹或随机重置则让粒子分布更分散但让一部分计算量浪费在无效区域。源码里不同的越界策略连带着参数含义也变了不统一这个前提算法之间没有可比性。解决在改进前先约定好全局统一的越界策略。最稳妥的做法是使用 clip 拉回边界但同时把速度置零避免下一个位置又快速飞出边界。如果你发现越界率超过 5%优先检查初始化范围或 Vmax 设置而不是换越界策略。5.2 固定随机种子才能对比否则结果全看运气现象同一份源码、同一组参数连续跑三次得到三个差距明显的结果有人因此判定算法不稳定其实是忘了固定随机种子。原因PSO 用 r1、r2 以及初始化采样两处随机源。不固定种子每次实验的初始种群不同相当于从不同的起点开始搜索结果自然有波动。改进算法内部如果加了变异或混沌扰动随机源更多波动更明显。解决在每次实验前设置固定随机种子Python 里是 numpy.random.seed()如果要统计成功率也务必要在同一批种子集合上跑比如预先生成 20 个种子文件所有算法共用同一套种子这样才算严格的控制变量。这个习惯也是给自己留后悔药——几个月后想复现某个结果种子一变就再也回不去了。5.3 Vmax 没设置或设置不当收敛曲线像心电图现象收敛曲线不是平滑下降而是呈锯齿形每代最优值上下剧烈跳动但平均值还在缓慢下降。原因PSO 的速度没有上限约束如果边界范围大、速度更新值也大粒子每次位移会跳过最优区域在两侧来回振荡。这个问题在加了惯性权重递减的改进里尤其常见因为前期 w 大速度积累很容易失控。解决加上 Vmax 约束通常设为边界宽度的 10% 到 20%例如边界是 [-10, 10] 则 Vmax 取 2 到 4。在代码里对 v 调用 clip而不是只对 x 做边界处理。如果改进算法本身带收缩因子Vmax 可以放宽一些因为 χ 已经约束了速度量级。5.4 评估次数不一致对比结果虚高现象某改进算法画的收敛曲线特别好看一算总评估次数却翻了一倍等于用双倍计算量赢了标准 PSO这个对比没有意义。原因改进算法在循环里额外调用了目标函数比如变异后重新评估、局部拓扑里多算邻居适应度或者在初始化阶段预计算了一部分导致单代成本变高。用横轴迭代代数画曲线时这种额外成本完全看不出来。解决把收敛曲线的横轴统一改成评估次数即 func 被调用的总次数。改进算法如果单代计算量更大在评估次数维度上不再占优。比较成功率和平均最优值时也统一在相同评估次数预算下进行这是做算法对比实验的基本前提。5.5 局部最优看似解决实际是多样性彻底丢失现象算法在迭代中期就稳定在一个值上改进前后最优值仿佛进步了但种群分布检查发现所有粒子聚在同一点没有任何探索能力。原因部分改进比如增大 c2、收缩因子会压缩粒子运动范围导致种群多样性在几十代内快速归零。从表面看收敛快实际上只是提前掉进了最近的坑里。解决在代码里加一个多样性监控每 50 代计算粒子位置的标准差或平均两两距离低于阈值时触发重置——把一部分粒子重新初始化到随机位置保留当前 gbest 和 pbest。这个机制可以和变异率叠加使用也是很多工程版源码自带的功能如果手头这份没有自己补上去也就十行代码。6. 把改进算法用到自己项目统一评估次数与存档最优粒子的验证技巧拿到这份源码之后先用一个固定套路把它变成自己的工具而不是直接跑到目标问题上。首先在任何验证代码里加上一个评估次数计数器每次调用目标函数就加一所有算法的比较都基于这个计数。然后改掉直接用 history 存 gbest 的习惯改成存两个数组每代的 gbest 值和该代已用的评估次数画图时横轴取后者这样不管是标准 PSO、收缩因子还是混合变异一到同一张图上就比较公平。第二个习惯是每代把全局最优粒子单独存下来包括它的位置向量和适应度值整个迭代结束后再对其做一次小规模的精细搜索。粒子群本身不保证收敛到精确极值尤其是在低精度要求下比如调度问题里误差允许在 5% 以内那直接输出 gbest 就行但如果你的场景需要高精度结果最后一步用本地搜索在这个存档位置附近做二次优化往往能再提升一个数量级。我在做特征选择实验时遇到过多次 PSO 跑到的值和枚举法给出的最优解就差最后一点点加上局部精修后就能对上。还有一个值得记住的教训任何改进算法第一次跑出来的结果都不作数先跑一遍检查收敛曲线形状和评估次数消耗再决定是不是真的要用它。源码包里的五种改进不是互相排斥的最常见的工程组合是惯性权重递减加环形拓扑再加低概率变异三者各自解决一个维度的问题。你也不需要五选一可以从这条路径开始自己组合。希望帮到你。本文还有配套的精品资源点击获取