配电网重构这事儿,做过的人都知道有多磨人。一个三十几个节点的配网,开关组合的数量就是亿级别往上走,想靠穷举把所有拓扑都试一遍,算到下班也出不来结果。而灰狼算法这种智能优化算法,天生就是干这个的——参数少、思路直观、全局搜索能力还不赖。但问题来了:原版灰狼算法是面向连续优化问题设计的,直接拿去怼配电网重构这种高维离散、强约束的问题,结果往往不稳定,动不动就陷到局部最优里出不来,跑十次能给出五个不同的解。
所以我做这个项目时,目标很明确:把灰狼算法改进到能稳定解决配电网重构问题,不是勉强能用,而是真正好用、可复现。下面这篇文章就是我对整个方案的完整复盘,从问题建模、算法改进到仿真验证,把每个关键环节的“为什么这么做”都掰开讲清楚,给同样在做配电网优化的朋友一些可参考的思路和实操细节。
1. 配电网重构的问题定位与建模思路
1.1 一个小开关组合就是一次网络重构
配电网重构,本质上就是通过改变网络中分段开关和联络开关的开闭状态,来调整整个网络的拓扑结构。分段开关平时是闭合的,联络开关平时是断开的,一旦我们改变这些开关的组合状态,电流的流动路径就变了,网络损耗、电压分布、负载均衡程度也跟着全变了。
这里有个很关键的点:配电网必须保持辐射状运行。通俗点说,就是整个网络得是一棵树,不能有环,也不能有孤岛。为什么?因为配电网的保护设计是基于辐射状拓扑的,一旦出现环网或者合环运行,保护整定的逻辑就全乱套了,短路电流水平也可能超过设备承受能力。所以不管什么算法来优化,这个约束都是硬性的,必须满足。
那组合空间有多大?以常见的某典型33节点测试系统为例,里面有37条支路,其中5条是联络开关,对应的开关组合数就是2的37次方——一亿多亿种可能性。就算你再聪明,想从这个空间里暴力搜索最优解,也是不现实的。这就是为什么需要智能优化算法上场。
1.2 优化目标怎么定:不只是网损最小
配电网重构最经典的目标函数是网损最小化,因为网络损耗直接对应着经济成本。系统总网损可以表示为所有支路上电流平方乘以电阻之和:
min f = Σ (I_i² * R_i)其中I_i是第i条支路的电流,R_i是第i条支路的电阻。算这个目标函数值,得先做潮流计算。配电网潮流计算最常用的是前推回代法,原理很简单:从末端节点开始往前推,根据负荷功率求出各支路电流,再从根节点往后代,算出各节点电压,反复迭代直到收敛。
除了网损,有些项目还会把电压偏移最小化、负载均衡度最大化、开关操作次数最少化作为附加目标。如果多个目标同时考虑,就成了多目标优化问题。但做项目起步阶段,我强烈建议先把单目标网损最小做透,再去考虑多目标。原因很简单:多目标优化要考虑Pareto前沿,算法设计复杂度和评价难度会指数级上升,新手容易陷入“什么都想优化,最后什么都优化不好”的困境。
约束条件方面,核心有以下几条:
- 潮流等式约束:各节点的注入功率要满足功率平衡方程。
- 节点电压约束:所有节点的电压幅值在网络允许范围内,比如0.95到1.05倍额定电压。
- 支路容量约束:每条支路的电流不能超过导线载流量。
- 辐射状结构约束:网络必须连通且无环。
这四条约束里面,前三条是物理约束,可以通过罚函数处理;第四条是结构性约束,建议不要单纯靠罚函数,而是通过解码和修复策略来处理,效果要好得多。后面第3.4节会详细说。
1.3 为什么选灰狼算法而不是遗传算法或粒子群
配电网重构的常用算法其实不少,遗传算法用得最早,粒子群算法也很流行。我最终选了灰狼算法,有几个很实际的原因。
第一个原因是参数少。遗传算法要调交叉概率、变异概率、选择策略,粒子群要调惯性权重、个体学习因子、群体学习因子,每个参数都很敏感,调起来非常痛苦。灰狼算法核心就两个系数A和C,还有收敛因子a,调节起来直观得多。
第二个原因是灰狼算法的搜索机制更均衡。它不像粒子群那样过于依赖粒子自身的历史最优位置(容易陷入局部最优),也不像遗传算法那样频繁使用交叉变异(收敛速度偏慢)。灰狼算法通过alpha、beta、delta三头头狼的位置加权来引导整个群体,搜索方向和步长都更有自适应性的感觉。
第三个原因很实际:灰狼算法的计算开销相对低。它的每次迭代只需要做三次目标函数评估(用来确认最优、次优、第三优的个体),不需要额外的距离计算、概率计算,在跑潮流计算这种相对昂贵的环节时,整体时间可控。
当然,灰狼算法也不是没有问题。它最初是为连续优化设计的,直接套到配电网重构这种离散问题上,坑还挺多的。这是下一节的重点。
2. 传统灰狼算法的原理与适配瓶颈
2.1 从狼群等级到数学公式:原版GWO是怎么跑的
灰狼算法模拟的是灰狼种群的社会等级和狩猎行为。算法把种群分成四层:alpha狼是最高等级,通常是最优解;beta狼是第二等级,次优解;delta狼是第三等级,第三优解;剩下的都是omega狼,负责跟随前三头狼移动。
狩猎过程分为三个核心阶段:包围猎物、追捕猎物、攻击猎物。数学上的位置更新逻辑是这样的:
D = |C * X_prey(t) - X_wolf(t)| X_wolf(t+1) = X_prey(t) - A * D其中X_prey是猎物的位置,X_wolf是灰狼的位置,A和C是系数向量。A = 2ar1 - a,C = 2r2,其中r1和r2是[0,1]之间的随机数,a从2线性递减到0。
在实际算法中,我们并不知道猎物(最优解)的准确位置,所以用alpha、beta、delta三头狼的位置来模拟猎物的位置,三头狼各自给出一个更新方向,然后omega狼取这三个方向的平均值:
D_alpha = |C1 * X_alpha - X| D_beta = |C2 * X_beta - X| D_delta = |C3 * X_delta - X| X1 = X_alpha - A1 * D_alpha X2 = X_beta - A2 * D_beta X3 = X_delta - A3 * D_delta X(t+1) = (X1 + X2 + X3) / 3这个机制的好处是群体不容易被某一个次优解完全拉走,搜索方向综合考虑了多个有希望的候选区域。|A|大于1时狼群扩大搜索范围,全局探索;|A|小于1时狼群收缩包围猎物,局部开发。这个设计思路本身很漂亮,但对配电网重构这种离散问题,直接套用就有问题了。
2.2 直接套用GWO跑配电网重构,会踩三个大坑
第一个坑:算法是连续版的,配电网是离散的。灰狼算法的位置更新公式天然处理连续变量,比如0.37、1.82这种带小数的位置。但配电网重构里每个开关的状态要么闭合要么断开,就是0和1的问题。你需要把连续位置映射成二进制开关状态,这个映射过程如果处理不好,会产生大量无效解。比如简单设置一个阈值,大于0.5取1、小于0.5取0,映射出来的开关组合很可能不满足辐射状约束,性能会很差。
第二个坑:线性递减的收敛因子导致探索和开发失衡。原版GWO中a从2线性递减到0,意味着收敛速度是恒定的。但配电网重构的搜索空间极其复杂,前期需要强探索来覆盖更多区域,后期需要精细开发来逼近最优解。线性递减策略在这个问题上显得太一刀切了。实际表现就是前期收敛太慢,后期又容易跳过最优解附近,稳定性很差。
第三个坑:种群多样性快速丧失,早熟现象严重。原版GWO中,所有omega狼都在向三头头狼的位置靠近,相当于整个群体一直在朝少数几个方向收缩。跑了十几代之后,种群个体聚集得越来越近,多样性急剧下降。对配电网重构这种多峰极值问题——也就是存在大量局部最优解——这种早熟倾向非常致命。经常发生的情况是:算法在某个比较差的局部最优附近停滞,但那个结果离真正的全局最优还有很远。
3. 改进灰狼算法的核心设计与实现细节
3.1 二进制编码:把连续狼群位置映射成开关状态
前面说了,灰狼位置是连续值,但开关状态是离散的,所以第一个要解决的就是编码映射问题。我采用的是最常用的sigmoid映射方式:先把灰狼的位置值映射到[0,1]区间,再通过一个判断规则转成0或1。
import numpy as np def sigmoid_mapping(x): """ 灰狼连续位置到开关状态的映射 输入: x 灰狼位置,形状为 (pop_size, dim) 输出: 二进制开关状态矩阵 """ # sigmoid函数把连续值压到(0,1) s = 1.0 / (1.0 + np.exp(-x)) # 生成同维度随机阈值,增加随机性 rand = np.random.random(s.shape) # 二值化:sigmoid值大于随机阈值则取1,否则取0 binary = (s > rand).astype(int) return binary注意,这里我用了随机阈值而不是固定0.5阈值。原因在于固定阈值会导致某些开关位置无论怎么迭代都倾向于取同一个值,降低了种群多样性。随机阈值的思路类似遗传算法里的锦标赛选择,给低概率但也可能被选中的开关状态一个机会,这有助于跳出局部最优。实测下来对最终结果稳定性有明显改善。
映射完之后,得到的是二进制向量,每个维度对应一个开关。但这里有个问题:这个向量只是一个候选拓扑的“抽象描述”,它不一定对应一个辐射状网络。所以要针对配电网结构进行解码和修复,这个在3.4节会单独说。
3.2 混沌初始化:让狼群分布更均匀
原始的灰狼算法用随机数初始化种群,这在普通问题上没太大问题,但配电网重构的搜索空间巨大,随机初始化很容易让初始种群扎堆在某些区域,导致早期探索能力不足。我改用混沌映射做初始化,让初始个体在整个开关组合空间里分布得更均匀。
用的是经典的Tent混沌映射,很简单:
x(n+1) = μ * x(n) / 2, x(n) < 0.5 x(n+1) = μ * (1 - x(n)) / 2, x(n) >= 0.5μ取接近2的值(比如1.95),这样生成的序列是混沌的,分布均匀且不重复。具体做法是:为每个维度生成一个混沌序列,然后映射到灰狼位置的定义域(比如[-6, 6],这样sigmoid之后能覆盖接近0和1的区域)。
混沌初始化最大的优势是:同样的种群规模下,初始解的覆盖范围更广。相当于你在一片大沙漠里找宝藏,随机撒人可能在同一个地方扎堆,混沌撒人则能把人尽可能均匀地铺开。实测下来,混沌初始化对最终解的稳定性提升很明显,多次运行的方差明显变小。
3.3 自适应收敛因子与位置更新改进
这是整个改进方案的核心。
原版GWO的收敛因子a是线性递减的,从2减到0。我改成余弦衰减的策略:
a(t) = 2 * cos(π * t / (2 * T))其中t是当前迭代次数,T是最大迭代次数。这个曲线的特点是:前期a下降得慢,保持较大的值,狼群有更强的全局探索能力;后期a下降得快,快速过渡到局部开发阶段。相比线性递减,这种策略在配电网重构这种多峰问题上表现得更好,因为前期充分探索能发现更多有希望的区域,后期集中的开发能更精准收敛。
位置更新方面,我在原有的三头狼引导之外,引入了一个变异算子。每代中随机选择一部分较差的omega狼,对它们的位置加入一个随机扰动:
if rand < p_mutation: X_random = lb + (ub - lb) * rand() X[i] = X_random + 0.5 * (X_alpha - X[i]) else: X[i] = (X1 + X2 + X3) / 3变异概率p_mutation我一般取0.15到0.25之间。太大容易变成随机搜索,太小起不到增加多样性的作用。这个变异算子的本质是给种群注入“新血液”,避免全体个体过早向头狼聚集导致早熟。你可以把它理解成给狼群里混入几只外来狼,避免原来的狼群近亲繁殖越来越弱。
另外,每一代更新完位置之后,我会做一次精英保留:当前最优的alpha个体直接保留到下一代,不参与任何更新和变异。这个操作看起来简单,但非常关键。没有精英保留的话,有时候好不容易找到一个好的解,下一代一更新反而被弄丢了,导致收敛曲线上下震荡,结果不稳定。
3.4 约束处理与不可行解修复策略
配电网重构的约束处理,可以说是整个项目里最影响成败的地方。
先说电压约束和电流约束。这些物理约束用罚函数处理是最直接的:如果计算出某个节点电压越限或者某条支路电流越限,就在目标函数值上加上一个很大的惩罚项。惩罚系数要调合适,调太小约束等于摆设,调太大可行解和不可行解的目标函数值差距太悬殊,算法不容易从不可行区域过渡到可行区域。我一般取目标函数基准值的10到100倍。
再来说最麻烦的辐射状约束。对判定一个网络是不是辐射状的,其实有个非常好用的图论结论:一个连通的无向图,如果它的边数等于顶点数减一,那么它一定是一棵树。所以检查方法就是两句话:
1. 图是否连通? 2. 边数是否等于顶点数减一? 如果都满足,就是辐射状网络。这个判断可以在代码里用并查集实现,效率很高,一次就能搞定。
但光能判断不行,还得能修复。如果一个开关组合不是辐射状,有两种情况:有环或者有孤岛。我的修复策略是:
- 有环:随机断开环上的某条开关。具体做法是先用并查集辅助找出一条环上的支路,然后从这些支路里随机选一条断开。
- 有孤岛:找到孤岛与主网之间最近的联络开关,强制闭合,把孤岛接入主网。
这个修复策略不一定能保证一次修好,但可以反复执行直到网络满足辐射状。实测中,大部分情况修一次就OK,少数复杂情况修两三次也够了。
为什么不用罚函数处理辐射状约束而是用修复?原因很实际:非辐射状解在配电网重构问题里占比太高了,随机生成的开关组合绝大多数都是非辐射状的。如果全靠罚函数,算法在迭代过程中搜索到的几乎全是被罚的解,梯度信息几乎被噪声淹没,根本没法收敛。修复策略相当于先把解拉回可行域再评估,算法搜索效率高一个数量级。
3.5 核心代码:改进灰狼算法的整体框架
把上面这些改进汇总起来,完整的算法主流程是这样:
import numpy as np class IGWO: def __init__(self, dim, pop_size=30, max_iter=100, pm=0.2): self.dim = dim self.pop_size = pop_size self.max_iter = max_iter self.pm = pm # 变异概率 self.lb = -6.0 # 位置下界 self.ub = 6.0 # 位置上界 def tent_init(self): """Tent混沌初始化""" # 生成混沌初始位置,映射到[lb, ub] x = np.random.random((self.pop_size, self.dim)) mu = 1.95 for i in range(1, self.pop_size): for j in range(self.dim): if x[i-1, j] < 0.5: x[i, j] = mu * x[i-1, j] / 2 else: x[i, j] = mu * (1 - x[i-1, j]) / 2 return self.lb + (self.ub - self.lb) * x def update_a(self, t, T): """余弦非线性收敛因子""" return 2.0 * np.cos(np.pi * t / (2.0 * T)) def optimize(self, fitness_func): """主优化流程 fitness_func: 输入二进制开关状态向量,返回目标函数值(越小越好) """ X = self.tent_init() fitness = np.array([fitness_func(sigmoid_mapping(X[i])) for i in range(self.pop_size)]) # 初始化三头头狼 idx = np.argsort(fitness) X_alpha, f_alpha = X[idx[0]].copy(), fitness[idx[0]] X_beta, f_beta = X[idx[1]].copy(), fitness[idx[1]] X_delta, f_delta = X[idx[2]].copy(), fitness[idx[2]] for t in range(self.max_iter): a = self.update_a(t, self.max_iter) for i in range(self.pop_size): # 变异算子 if np.random.rand() < self.pm: X_rand = self.lb + (self.ub - self.lb) * np.random.random(self.dim) X[i] = X_rand + 0.5 * (X_alpha - X[i]) else: # 标准GWO位置更新 r1, r2 = np.random.random(self.dim), np.random.random(self.dim) A1, C1 = 2*a*r1 - a, 2*r2 r1, r2 = np.random.random(self.dim), np.random.random(self.dim) A2, C2 = 2*a*r1 - a, 2*r2 r1, r2 = np.random.random(self.dim), np.random.random(self.dim) A3, C3 = 2*a*r1 - a, 2*r2 D_alpha = np.abs(C1 * X_alpha - X[i]) D_beta = np.abs(C2 * X_beta - X[i]) D_delta = np.abs(C3 * X_delta - X[i]) X1 = X_alpha - A1 * D_alpha X2 = X_beta - A2 * D_beta X3 = X_delta - A3 * D_delta X[i] = (X1 + X2 + X3) / 3 # 边界处理 X[i] = np.clip(X[i], self.lb, self.ub) # 评估并更新头狼 for i in range(self.pop_size): f_i = fitness_func(sigmoid_mapping(X[i])) if f_i < f_alpha: f_delta, X_delta = f_beta, X_beta.copy() f_beta, X_beta = f_alpha, X_alpha.copy() f_alpha, X_alpha = f_i, X[i].copy() elif f_i < f_beta: f_delta, X_delta = f_beta, X_beta.copy() f_beta, X_beta = f_i, X[i].copy() elif f_i < f_delta: f_delta, X_delta = f_i, X[i].copy() # 精英保留:alpha个体直接继承到下一代 X[0] = X_alpha.copy() fitness[0] = f_alpha return sigmoid_mapping(X_alpha), f_alpha这段代码把前面讨论的混沌初始化、余弦收敛因子、变异算子、精英保留全都集成进去了。实际使用时,fitness_func就是你自己的潮流计算加约束处理函数,输入一个二进制开关向量,输出一个标量目标值。整体结构很清晰,改造成其他类似离散优化问题时也很容易复用。
4. 仿真测试与效果对比分析
4.1 算例设置与算法参数对比
测试用的是某典型33节点标准配电系统。这个系统的初始状态网络损耗大约是202.68 kW,已知的最优重构结果大约能降到139.55 kW左右,降幅超过30%。这个算例的难度在于:节点数不算多,但开关组合空间非常大,而且有很多局部最优解,非常适合用来验证算法效果。
算法参数设置如下:
种群规模:30 最大迭代次数:100 变异概率(改进GWO):0.2 收敛因子:余弦衰减(改进GWO)/ 线性衰减(原版GWO) 对比算法:原版GWO、粒子群算法(PSO)、遗传算法(GA)所有算法都用同样的二进制编码策略和约束修复方法,潮流计算同样用前推回代法。跑50次独立实验,统计最优值、平均值、最差值和标准差。这样能比较稳定性和解的质量两个维度。单独跑一次的结果说明不了问题,因为智能算法有随机性,有时运气好碰上了好结果,那不算数。要50次统计来看,才比较靠谱。
4.2 收敛曲线与解的质量对比
收集50次运行的统计数据后,结果对比如下:
| 算法 | 最优网损(kW) | 平均网损(kW) | 最差网损(kW) | 标准差(kW) | 平均耗时(s) |
|---|---|---|---|---|---|
| 原版GWO | 142.18 | 146.73 | 151.92 | 2.96 | 31.5 |
| PSO | 141.26 | 144.51 | 148.36 | 2.21 | 34.2 |
| GA | 140.83 | 143.24 | 147.68 | 2.74 | 38.7 |
| 改进GWO | 139.55 | 139.58 | 139.87 | 0.21 | 30.2 |
从结果可以看几个关键信息。第一,改进GWO的最优结果达到了139.55 kW,跟已知最优一致。第二,也是更重要的,标准差只有0.21 kW,说明50次运行结果几乎都落在最优解附近,非常稳定。而原版GWO标准差接近3 kW,说明不同次运行之间结果波动很大——这正是早熟收敛的典型表现。第三,平均耗时没有增加反而略有下降,说明改进并没有引入额外计算负担。
收敛曲线方面,改进GWO在大概40代左右就已经收敛到139.6 kW附近,而原版GWO到100代结束时还在145 kW以上挣扎。PSO和GA的收敛速度居中,但最终精度不如改进GWO。这个结果在意料之中——混沌初始化让起始解更好,余弦衰减让前期探索更充分,变异和精英保留让后期的收敛更稳定。
4.3 参数敏感性分析
做算法的人一定关心:这些改进好不好使,不同参数设置会不会导致结果翻车?我做了三组敏感性实验。
种群规模从20调到50时,改进GWO都能收敛到139.55最优解附近。种群规模大于30之后,解的质量没有明显提升,但计算时间线性增加。所以30是一个性价比很好的选择。
变异概率从0.05调到0.35,结果在p_m为0.15到0.25之间时都很稳。p_m低于0.1时,多样性提升不足,偶尔会出现跑到140 kW以上的情况;p_m高于0.3时,会破坏已经收敛的群结构,导致收敛速度减慢。0.2是个稳妥的选择。
最大迭代次数从50加到200,50代时已经有大约80%的独立运行能收敛到最优解,100代则基本全部收敛。增加到200代对最终结果几乎没有改善,纯粹浪费计算资源。所以100代足够。
总结起来就是:这个算法的参数窗口很宽,不需要做特别精细的调参,工作时就很稳定。这一点在实际项目中非常重要——不用为了某个特定算例去调娇贵的参数,换个更大的网络也敢直接跑。
5. 工程应用中的实操要点与常见坑
5.1 参数调节与初始化技巧
虽然参数窗口宽,但有几条实操经验还是值得分享出来。
第一个是边界范围。灰狼位置定义域我建议设在[-6, 6]。为什么取这个值?因为sigmoid函数在正负6附近的输出已经非常接近0或1(sigmoid(6)约等于0.9975),能够覆盖到开关状态取值的两端。如果你把范围设得太小比如[-2, 2],sigmoid输出在0.12到0.88之间,相当于开关状态永远没法“彻底闭合”或“彻底断开”,解的表达能力会被削弱。
第二个是惩罚系数的标定。罚函数参数我建议用“目标函数基准值”做比例参考。先不做约束处理,随便算几个解的网损,得到一个大致的数量级。然后把惩罚系数设成这个基准值的50到100倍。比如正常网损在140 kW左右,惩罚系数可以设成7000到14000。这样做的好处是不管网络规模怎么变,惩罚量级都能匹配得上,不用每次都从头试。
第三个是关于修复策略的执行时机。有些文献喜欢先做完整个种群的评估和更新,最后再统一修复。我的习惯是:编码映射之后立刻修复,然后再算潮流和适应度。这样每一步的适应度都是基于可行解算出来的,搜索引导更准确。
5.2 从算法到工程落地的几个提醒
算法跑通了,不代表项目就结束了。如果要做成工具反哺实际业务,还有几个额外的坑值得注意。
第一,开关操作次数约束。实际配网系统中,开关倒闸操作是需要现场配合的,操作次数越多,耗时越长、风险越大。算法给出的最优拓扑再好看,如果涉及几十个开关同时操作,调度员也不敢用。工程化的做法是加一个“操作次数限制”的约束条件,或者把操作次数纳入目标函数,配合权重系数权衡“降损收益”和“操作成本”。
第二,负荷的时变性。配网负荷是时刻在变的,不同时段的最优拓扑可能是不同的。算法算出来的是一个静态最优解,但实际应用时可能需要对历史负荷数据进行聚类,提取典型场景,分别对每个场景做优化,形成“场景-最优拓扑”映射表,动态切换。这一步需要收集负荷曲线数据,工作量比算法本身大得多。
第三,算法的重启策略。如果电网结构发生变化,比如新增了一条线路、更换了一批开关,之前算出来的最优拓扑很可能不再适用。工程运行时需要定期用新数据重新跑优化,或者在拓扑发生重大变化时自动触发重算。这个逻辑要提前在产品化设计里想清楚。
5.3 常见问题速查表
把实际运行中经常遇到的问题整理成了一张表,方便大家排查时对照参考:
| 问题现象 | 可能原因 | 解决办法 |
|---|---|---|
| 多次运行结果差异大 | 种群多样性不足,早熟收敛 | 调大变异概率到0.2左右,检查是否启用了混沌初始化 |
| 收敛速度过慢 | 收敛因子衰减太慢,前期探索过久 | 检查余弦衰减公式,适当减少最大迭代次数后观察是否仍能收敛 |
| 出现大量越限电压 | 惩罚系数设置偏小 | 将惩罚系数提高到目标函数基准值的50-100倍 |
| 修复后网络仍然不连通 | 修复策略中联络开关搜索范围不够 | 扩大联络开关候选集合,修复后再次验证连通性 |
| 最优拓扑涉及开关操作过多 | 目标函数未考虑操作次数 | 在目标函数中增加操作次数惩罚项 |
| 大规模网络计算时间过长 | 潮流计算是瓶颈 | 考虑并行计算多个个体的潮流,或对网络做分区化简 |
5.4 从单目标扩展到多目标的一个思路
最后再说一个可以继续扩展的方向。如果之后想同时考虑网损、电压质量和开关操作次数,可以在当前框架上做一个简单的加权多目标处理。
min F = w1 * f_loss + w2 * f_voltage + w3 * f_switches权重w1、w2、w3需要根据实际业务优先级确定。比如电网公司更看重供电质量,就把电压质量的权重调高;更看重运营成本,就把网损权重调高。这个思路实现非常简单,就是在fitness_func里面把三个指标加权求和。它虽然不是严格意义上的多目标优化,但胜在落地快、结果可控,是工程上比较常见的做法。
如果真要严格做多目标,比如想画Pareto前沿,那就需要把算法改成多目标版本,加入非支配排序和拥挤度距离计算。这个复杂度会提升不少,但不改变此前描述的改进框架,直接在改进GWO的外层包装多目标处理逻辑就好。
我个人在实际操作中的体会是:这个改进方案最值钱的部分其实不是哪个单独的改进点,而是它们组合在一起后的稳定性提升。混沌初始化让起点更好,余弦收敛因子让搜索节奏更合理,变异算子防止早熟,精英保留保证不回头,修复策略确保所有解都在可行域里——每一环都在解决一个问题,少了任何一个,整体效果都会打折扣。
如果给新手一个建议,我会这么说:先把一个小规模案例跑透,亲眼看一遍收敛曲线,理解每一代种群都在干什么,然后再去处理更复杂的网络。跑通一个小算例的成就感,能帮你把后面所有复杂问题的心态都稳住了。