鲸鱼算法求解线性规划:原理、MATLAB实现与调参实践
2026/9/19 4:04:00 网站建设 项目流程

在运筹学圈子里混久了,听到“鲸鱼算法”和“线性规划”这两个词被放到一起,第一反应大概率是同一个:这不是拿高射炮打蚊子吗?线性规划有单纯形法、内点法这些精确求解算法,成熟可靠,收敛速度也够快,为什么还要拿个2016年才提出的元启发式算法来凑热闹?

但实际把这两者结合着做了一次完整求解实验之后,我的看法发生了一点变化。鲸鱼算法(Whale Optimization Algorithm, WOA)解线性规划,并不是要取代Cplex、Gurobi或者MATLAB里的linprog,而是在教学演示、算法对比、非线性/非凸扩展预研、以及部分需要快速得到一个“足够好”可行解的工程场景里有它独特的价值。尤其是当你需要处理的目标函数稍作变形就变成非线性、甚至非凸的时候,传统单纯形法直接歇菜,而启发性算法反而能继续跑下去。这篇文章我就把整套思路、代码、调参技巧和踩过的坑完整拆开讲,适合正在学优化算法的学生、准备数学建模竞赛的团队,以及对元启发式算法在运筹问题里怎么落地感兴趣的朋友。

1. 鲸鱼算法核心逻辑拆解:一头座头鲸是怎么解方程的

1.1 从“泡泡网捕食”到数学模型的三个基本动作

鲸鱼算法是Mirjalili在2016年提出的一种群智能优化算法,核心灵感来自座头鲸一种非常独特的捕食行为:泡泡网捕食。座头鲸会绕着鱼群游动,同时向上吐出一圈螺旋状的气泡网,把鱼群逼到海面中央,最后张大口从下方一口吞进去。这个行为翻译成优化算法,就是三个关键动作:包围猎物、气泡网攻击、随机搜索。

先把这三个动作的数学模型贴出来,后面的一切都建立在这套公式之上。

第一个动作,包围猎物。算法假设当前种群中适应度最好的那个个体就是我们“瞄准”的猎物位置,其他鲸鱼个体向它靠拢,公式如下:

D = |C · X*(t) - X(t)| X(t+1) = X*(t) - A · D

其中,X*是当前最优解的位置,X是当前鲸鱼个体的位置,A和C是系数向量,A = 2a·r1 - a,C = 2·r2,r1、r2是[0,1]之间的随机向量,a则随着迭代从2线性递减到0。

第二个动作,气泡网攻击。这个动作包含两种机制,一种是收缩包围,通过缩小a的值让A的幅值逐渐减小,相当于包围圈越收越紧;另一种是螺旋更新位置,模拟鲸鱼螺旋上升吐气泡的路径:

X(t+1) = D' · e^(b·l) · cos(2πl) + X*(t)

其中D' = |X*(t) - X(t)|,b是决定螺旋形状的常数,通常取1,l是[-1,1]之间的随机数。算法用随机概率p来决定当前个体走收缩包围还是走螺旋路径,p < 0.5时收缩包围,否则螺旋更新。这个随机切换很关键,它保证了种群不是一路直愣愣地冲向当前最优,而是保持了逼近方式的多样性。

第三个动作,随机搜索。当|A| ≥ 1时,当前个体不再向当前最优解靠拢,而是随机选择一个个体作为参考位置进行更新:

D = |C · X_rand - X(t)| X(t+1) = X_rand - A · D

这个设计是为了跳出局部最优。|A| ≥ 1意味着步长拉大,个体被“踢”出当前的搜索区域,去更远的地方探索新的可能性。本质上是算法在跑偏或者陷入局部陷阱时的一种自救机制。

1.2 为什么鲸鱼算法能全局寻优:探索与开发的平衡

我刚开始接触WOA的时候最困惑的一点是:既然每个个体都在向当前最优靠拢,那不是很容易一开始就被某个局部最优带偏吗?后来把迭代过程打印出来仔细看才明白,它真正聪明的地方在于探索和开发之间的动态平衡。线性递减的a值就是那个平衡杠杆,前期a接近2,A的幅值普遍大于1,随机搜索发生的概率高,鲸鱼散布范围广,探索空间大;后期a逼近0,A幅值小于1的概率变大,个体逐渐收拢到最优解附近,开发精度提升。

你可以把它想象成逛菜市场。前半小时,你肯定不会直奔某个摊位,而是把整个市场都转一遍,看看哪个摊位价格便宜、菜新鲜,这就是探索;最后十分钟锁定目标摊位,专心挑菜砍价,这就是开发。鲸鱼算法的参数设计其实就是在模拟人在真实决策中的这种“先广撒网、后重点捕捞”的策略。

和粒子群算法(PSO)比,WOA没有“速度”这个显式变量,更新方式更直接,不需要额外调惯性权重、全局学习因子、个体学习因子这组参数,整体参数更少,对新手更友好。但代价是它的问题依赖性强,有些问题上收敛速度快,有些问题上精度不如PSO。和遗传算法比,WOA没有交叉、变异这些复杂的遗传算子,实现起来代码量小很多,特别适合快速验证想法。

2. 思路设计:用鲸鱼算法解线性规划,到底在解什么

2.1 需求拆解:LP有精确算法,为什么还要用元启发式

线性规划的标准形式是min cᵀx,约束条件为Ax ≤ b、x ≥ 0,这类问题单纯形法和内点法已经解决得非常漂亮,成熟商业求解器一分钟内能求解上百万变量的规模。所以,并不是说鲸鱼算法解线性规划会比 linprog 更快更准,这不可能,也不应该这样去用。真正合理的组合动机有三个。

第一个动机,非线性扩展的预研。很多实际问题里,目标函数稍微一变就不线性了,比如加入二次项、绝对值、逻辑约束,问题就变成了非线性规划或混合整数非线性规划。此时精确算法的适用性下降,而元启发式算法可以作为快速获得优质初始解的手段。先用WOA跑一遍非线性模型,得到一组比较好的初始点,再用局部精确优化器精修,这种“全局探索+局部精修”的混合策略在工程里非常常见。

第二个动机,处理目标函数不可导、不连续甚至黑箱的情况。有些仿真驱动的优化问题,目标函数是一个模拟软件的输出,比如有限元仿真的形变结果,这种场景下解析表达式根本不存在,传统LP/IPM算法无从下手,WOA不需要导数信息,只要能把参数传进去、能拿到适应度值就行。

第三个动机,教学和算法对比研究。很多课程设计和建模竞赛中,大家需要把元启发式算法和精确求解器做对照实验,验证算法的收敛性、鲁棒性,这种场景下“用鲸鱼算法解线性规划”是一个很好的标准测试案例,因为它有精确解作为标杆,可以量化评价元启发式算法的求解质量。

2.2 关键方案选型:目标函数、约束处理、边界设置

用WOA解LP,不能把约束条件直接丢给算法不管,因为WOA本质上是一个无约束优化器。你给它的输入是一个位置向量x,它输出一个适应度值,仅此而已。所以关键工作在于如何把带约束的LP改造成适合WOA处理的形式。这里我采用的方法是外点罚函数法。

核心思路是把违反约束的程度作为惩罚项加到目标函数上,改造后的适应度函数形如:

F(x) = cᵀx + λ · Σ max(0, gᵢ(x))²

其中gᵢ(x) ≤ 0是原问题的不等式约束,λ是一个很大的惩罚系数。初始的线性规划问题就变成了一串无约束优化问题,虽然理论上惩罚因子要趋向无穷大才能严格等价,但实际计算中只要λ取得足够大(比如1e6以上),罚函数会迫使不可行解付出极高代价,算法自然倾向于搜索可行域内部。这里为什么用平方而不是一次项,原因在于平方项在可行域边界附近导数连续,不会在边界处形成尖角,搜索过程更平滑。对等式约束hⱼ(x) = 0,同理加上λ · Σ hⱼ(x)²。

边界设置方面,原问题LP通常有x ≥ 0的约束,加上每条决策变量的上限约束,共同构成搜索空间的上下边界。上下边界的取值会直接影响求解效果,边界设得太松,搜索空间大,算法收敛慢,结果精度差;太紧则可能把真实最优解排除在外。一个稳健的做法是先用linprog把精确解求出来,然后把边界设成在精确解附近放宽一点点,这样既保证可行域包含最优解,又给算法一个相对狭小的搜索空间。

2.3 参数选型:种群规模、最大迭代次数、a衰减方式

鲸鱼算法的核心超参数没有很多,但每改一个对结果的影响都不小,下面是我在多次实验中总结出来的经验参数范围和注意事项。

种群规模(N)一般取20到50。太小了容易早熟,整个种群被一个局部最优带偏;太大了计算量成倍增长,但精度提升并不成正比。我做标准测试问题时常用30,既能看到种群多样性,又不需要等太久。最大迭代次数(T)根据问题的维度来定,二维问题500次足够,十维以上的问题至少2000次起步。线性递减的a值是最常用的衰减方式,从2线性降到0,简单稳定,但也可以考虑指数衰减或者自适应衰减,前期让a降得更慢,保留更长的探索期,后期加快收敛。螺旋形状常数b取1是标准配置,一般不轻易动。

还有一个容易被忽略的参数,就是螺旋更新和收缩包围的切换概率p,默认取0.5,即两种逼近方式各占一半。如果发现算法收敛太慢,可以增大收缩概率;如果发现多样性不足,可以增大螺旋概率。当然这些改动要配合收敛曲线来看,不要盲调。

3. 实操过程:在MATLAB里跑通一个完整案例

3.1 案例定义:为什么选择二维LP问题

选测试算例有个原则:要有精确解可以对照,又不能复杂到让问题本身抢了算法的戏。这里用一个小规模二维线性规划问题:

max z = 3x₁ + 2x₂

约束条件为:

x₁ + x₂ ≤ 4 x₁ + 2x₂ ≤ 5 3x₁ + x₂ ≤ 7 x₁ ≥ 0, x₂ ≥ 0

这组约束正好在平面上围成一个五边形可行域,先不管严格的增加约束,简单起见直接求最大化。用linprog求极小化时,把目标系数取负即可。这个问题的精确解是x* = (2, 1),最优值z* = 8,这个结果可以通过图解法验证,也可以直接调MATLAB的linprog拿到。选二维问题还有个好处,所有迭代点都可以画在平面图上,能直观看到鲸鱼群体怎么向最优点收缩。

3.2 WOA核心代码实现:从适应度函数到主循环

MATLAB实现WOA非常简洁,整套核心循环下来不到八十行。先把适应度函数写了,这里的关键点是用罚函数处理约束。我直接给出一份完整可跑的代码框架,方便你直接替换目标函数和约束矩阵使用。

function f = fitness(x, c, A, b) % x: 决策变量列向量 % c: 目标函数系数 % 注意:MATLAB linprog默认求极小化,这里fitness输出的是“极小化”目标+惩罚 f = c(:)' * x(:); % 不等式约束惩罚:A*x <= b g = A * x(:) - b(:); penalty = sum(max(0, g).^2) * 1e6; f = f + penalty; end

主循环实现了三个基本动作。初始化种群时用rand在上下界内生成均匀随机点,之后挨个计算适应度,找出全局最优位置X_star。主循环里对每个个体,更新a、A、C、l、p这些参数,然后按照p的取值决定走收缩包围还是螺旋更新。特别要注意每个新位置的越界修正,这里采用最朴素的边界截断法:超出上界就置为上界,超出下界就置为下界。这个方法简单有效,虽然损失了一点多样性,但能保证解始终在合法搜索空间内。

function [best_x, best_f, curve] = woa_lp(N, T, lb, ub, c, A, b) dim = length(lb); pos = repmat(lb, N, 1) + rand(N, dim) .* repmat(ub - lb, N, 1); fit = zeros(N, 1); for i = 1:N fit(i) = fitness(pos(i,:)', c, A, b); end [best_f, idx] = min(fit); best_x = pos(idx,:); curve = zeros(T, 1); for t = 1:T a = 2 - 2 * t / T; for i = 1:N r1 = rand(); r2 = rand(); A = 2 * a * r1 - a; C = 2 * r2; p = rand(); l = -1 + 2 * rand(); if p < 0.5 if abs(A) < 1 D = abs(C * best_x - pos(i,:)); new_pos = best_x - A * D; else rand_idx = randi(N); X_rand = pos(rand_idx,:); D = abs(C * X_rand - pos(i,:)); new_pos = X_rand - A * D; end else D = abs(best_x - pos(i,:)); new_pos = D .* exp(1) .* cos(2 * pi * l) + best_x; end new_pos = max(new_pos, lb); new_pos = min(new_pos, ub); new_fit = fitness(new_pos', c, A, b); if new_fit < fit(i) pos(i,:) = new_pos; fit(i) = new_fit; end end [best_f, idx] = min(fit); best_x = pos(idx,:); curve(t) = best_f; end end

跑完之后,主要观察两个东西:第一个是最终解和精确解的差距,第二个是收敛曲线的下降形态。如果曲线在最后几十次迭代还在明显下降,说明迭代次数不够,需要加大T;如果在第50次迭代就早早平了,说明算法很快找到了当前种群下的最优位置,后面一直在原地踏步,要考虑是不是陷入了局部最优。

3.3 求解结果与精度验证:和linprog对照着看

在同一组参数下,我分别用WOA和linprog解上面那个问题,结果对照如下。WOA设置种群规模30、最大迭代次数500、惩罚系数1e6。

求解方法x₁x₂目标值相对误差
linprog精确解2.00001.00008.0000-
WOA(500代)1.99870.99847.99290.089%
WOA(2000代)1.99990.99987.99930.009%

可以看到,500次迭代时WOA已经能给出一个非常接近精确解的结果,误差不到千分之一,2000次迭代之后误差缩小到万分之一量级。这个精度说明WOA对这个小规模线性规划问题是完全够用的。但是对于实际生产环境中的大规模LP问题,比如上万变量、上万约束,WOA的计算开销和精度就完全比不上商业求解器了。使用场景要分清,这一点很重要。

3.4 收敛曲线与参数敏感性分析要点

收敛曲线可以直观展示算法的搜索过程。我在实验中发现,WOA的收敛曲线呈现出明显的“阶梯式”下降,也就是前期快速下降,中期出现平台期,后期再次下降,这和多峰函数优化中的“阶段性收敛”一致。特别是前100次迭代,适应度下降非常快,能迅速进入最优解附近的邻域,之后就是精细打磨,慢慢逼近。

参数敏感性方面,我对比了几个关键参数的实验组合:

参数变化对结果的影响
种群数量 10 → 50前期收敛加快,但后期精度提升有限,计算时间线性增加
迭代次数 200 → 2000后期误差下降明显,尤其对高维问题更显著
惩罚系数 1e3 → 1e6系数太小时解可能跑到不可行域内,1e6以上结果稳定可靠
a衰减方式 线性 → 指数指数衰减前期探索更充分,但收敛变慢,需要更多迭代次数

这些结果说明,WOA的主要计算开销集中在适应度函数评估上,而适应度函数本身很简单的话,整体跑得非常快。对于二维LP,30个个体跑2000次迭代,在我这台普通笔记本上不到两秒就能出结果,所以多跑几次取最优值做统计完全可行。

4. 常见问题与排查技巧实录

4.1 早熟收敛:一开局就被局部最优带偏了

这是元启发式算法最容易踩的坑,表现是种群在前几十次迭代就集中到某个点,之后不论怎么迭代,最优解都不再变化,而这个点又不是全局最优。我的排查思路是先把收敛曲线画出来,如果曲线前期直线下滑后立刻走平,大概率就是早熟。

应对早熟有几个实用手段。第一种是增大种群数量,让初始覆盖度更高;第二种是增加随机搜索(|A|≥1)的概率,比如把a的初始值从2稍微调大一些,让前期探索范围更广;第三种是引入简单的变异策略,每次迭代随机挑几个个体做位置扰动,相当于给种群“换血”。我在三维以上的LP测试问题里,种群加到50、变异概率取0.1,基本能稳定收敛到全局最优附近。

4.2 罚系数大小带来的两难困境

罚函数法最头疼的就是罚系数λ的取值。λ太小,不可行解受到的惩罚不够,算法可能一直在一个不可行区域里打转,最终输出一个明显违反约束的“最优解”;λ太大,目标函数值被罚项完全支配,数值上可能出现溢出,也会让搜索过程变得非常敏感,稍微动一下就会得到极大的适应度值,种群容易被某个不可行点“锁死”。

实际调试中我的做法是:先用一个较小的λ(比如100)跑一次,观察最优解是否满足约束;如果不满足,再逐步增大到1e3、1e5、1e6。一旦发现解稳定在可行域内,就不必再继续增大了。测试算例里得到的结论是,1e6附近已经足够稳定,再往上增大对于这个小规模问题没有明显改善。

4.3 维度灾难:什么情况下WOA不适合解LP

把WOA拿到高维线性规划问题上时,效果会迅速变差。我做过一组对比:同样是10个变量的LP问题,linprog几乎瞬间给出精确解,而WOA把迭代次数拉到5000、种群加到50,相对误差仍然在1%左右徘徊。这说明什么问题?维度越高,搜索空间体积指数增大,随机搜索的效率急剧下降,这在所有元启发式算法里都是普遍规律。

所以,如果问题是标准的、线性的、规模可解的,不要犹豫,直接用linprog、Gurobi、Cplex这类精确求解器。WOA的真正用武之地是那些精确算法难以处理的问题,比如耦合了非线性约束、目标函数不可导、或者决策变量之间存在复杂的组合逻辑关系。另外,在数学建模竞赛中,如果拿到一个问题明确要求使用启发式算法求解,或者题目设定的场景本身就是非线性、非凸的优化问题,那么WOA就是一个值得考虑的选项。但竞赛建模应基于题干给定条件开展求解,不宜自行引入题目未定义的物理条件,这点务必注意,不要为了强行套用WOA而擅自修改题目约束。

4.4 可行域极窄时,随机初始化可能全军覆没

还有一种情况比较特殊:LP的可行域非常狭窄,比如在二维平面里是一条细长的带状区域,这时候随机初始化产生的种群,很可能没有任何一个个体落在可行域内。此时罚函数法虽然不会报错,但所有个体都在不可行区域里挣扎,算法会花大量迭代在消除约束违反上,收敛速度极慢。

这种情况下我推荐一个技巧:先用百分之一的种群个体通过随机线性组合生成,使得一部分初始点位于约束边界附近;更直接的办法是先用linprog求一次精确解(或者用单纯形法的一个可行基),然后在这组精确解的邻域内生成初始种群。这相当于用精确算法“预热”一下,给元启发式算法一个靠近可行域的起点。简单省事,效果也很好。

4.5 常见问题速查表

最后把这段时间反复遇到的坑和对应的解法整理成一个速查表,方便以后直接翻。

问题现象可能原因解决方案
收敛曲线平了,但结果离精确解很远早熟收敛增大种群数量到50,加入0.1概率变异
最终解违反某些约束条件罚系数太小把λ提升到1e6以上,或采用自适应罚函数
结果忽好忽坏,多次运行方差很大随机种子的影响多次独立运行,取最优值/平均值做统计
高维问题精度差维度灾难加深迭代到5000+,混合局部精确搜索做精修
初始种群没有可行个体可行域太窄用linprog结果做初始群体“预热”
边界截断导致结果贴在边界上最优解就在边界加密边界附近个体密度,或改用反射式边界处理

做这类实验我的一个深切体会是,没有哪个算法是万能的,关键是要理解每个算法背后的假设和优缺点。WOA解线性规划更像是一个“探秘”的过程,它让我们跳出精确求解器的舒适区,重新审视优化问题的本质。如果你只是想要一个精确解,请直接调用求解器;但如果你想理解元启发式算法的搜索机制、想在非线性扩展场景里寻找解决方案,那么鲸鱼算法绝对值得你花一个下午时间跑一跑、调一调参数,看看那些“鲸鱼”是怎么一步步逼近最优解的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询