种群优化算法:萤火虫算法(FA)·进阶篇
萤火虫算法的类结构与初始化落点
这段代码实现了一个基于萤火虫群智能的优化器类 C_AO_FA,核心私有成员里 alpha 控制运动随机性、beta 控制吸引力强度、gamma 控制环境透明度,三者共同决定种群在参数空间里的搜索形态。 Init 方法接收 paramsP(待优化参数维度)、sizeP(种群规模)、alphaP/betaP/gammaP 三个系数,并把 fB 初始化为 -DBL_MAX,意味着当前全局最优适应度在初始化阶段被压到双精度最小值,避免脏数据干扰。 数组层面用 ArrayResize 把 rangeMax、rangeMin、rangeStep、v、att、fireflies、cB 全部按 params 或 swarmSize 铺开;fireflies[i].c 也逐个按 params 扩维,fireflies[i].f 置为 -DBL_MAX。MT5 里直接建个 EA 把这段类声明和 Init 贴进去,改 sizeP=20、alphaP=0.5 跑空优化,能验证种群内存布局是否符合预期。 外汇与贵金属市场波动剧烈、杠杆风险高,这类智能优化仅用于历史参数拟合,实盘信号倾向失效,须以模拟盘先行验证。
class="kw">const class="type">class="kw">double betaP, class=class="str">"cmt">//beta, effect of attractiveness class="kw">const class="type">class="kw">double gammaP); class=class="str">"cmt">//gamma, transparency of the environment class="kw">public: class="type">void Flight(); class="kw">public: class="type">void Luminosity(); class=class="str">"cmt">//---------------------------------------------------------------------------- class="kw">private: S_Attractiveness att []; class="kw">private: class="type">int swarmSize; class="kw">private: class="type">int params; class="kw">private: class="type">class="kw">double maxDist; class="kw">private: class="type">class="kw">double v []; class="kw">private: class="type">class="kw">double alpha; class=class="str">"cmt">//randomness in motion class="kw">private: class="type">class="kw">double beta; class=class="str">"cmt">//effect of attractiveness class="kw">private: class="type">class="kw">double gamma; class=class="str">"cmt">//transparency of the environment class="kw">private: class="type">bool luminosity; class="kw">private: class="type">class="kw">double SeInDiSp(class="type">class="kw">double In, class="type">class="kw">double inMin, class="type">class="kw">double inMax, class="type">class="kw">double step); class="kw">private: class="type">class="kw">double RNDfromCI(class="type">class="kw">double min, class="type">class="kw">double max); class="kw">protected: class="type">class="kw">double Scale(class="type">class="kw">double In, class="type">class="kw">double InMIN, class="type">class="kw">double InMAX, class="type">class="kw">double OutMIN, class="type">class="kw">double OutMAX, class="type">bool revers); }; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_FA::Init(class="kw">const class="type">int paramsP, class=class="str">"cmt">//number of opt. parameters class="kw">const class="type">int sizeP, class=class="str">"cmt">//swarm size class="kw">const class="type">class="kw">double alphaP, class=class="str">"cmt">//alpha, randomness in motion class="kw">const class="type">class="kw">double betaP, class=class="str">"cmt">//beta, effect of attractiveness class="kw">const class="type">class="kw">double gammaP) class=class="str">"cmt">//gamma, transparency of the environment { fB = -DBL_MAX; params = paramsP; swarmSize = sizeP; alpha = alphaP; beta = betaP; gamma = gammaP; ArrayResize(rangeMax, params); ArrayResize(rangeMin, params); ArrayResize(rangeStep, params); ArrayResize(v, params); ArrayResize(att, swarmSize); luminosity = class="kw">false; ArrayResize(fireflies, swarmSize); for (class="type">int i = class="num">0; i < swarmSize; i++) { ArrayResize(fireflies [i].c, params); fireflies [i].f = -DBL_MAX; } ArrayResize(cB, params); } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— if (!luminosity) { fB = -DBL_MAX;
「萤火虫算法的距离标定与位移逻辑」
这段实现把参数空间里的欧氏距离先归一化到 0~20 的区间,再喂给吸引力公式。maxDist 是全体维度极差向量的模长,作为 Scale 函数的上界基准;不这么做,远距离个体的吸引力会被平方衰减直接压成零。 距离计算部分对每个 i 遍历其余 k,累加各维坐标差的平方再开方,随后用 Scale(distance,0.0,maxDist,0.0,20.0,false) 线性压缩。吸引力 = 目标个体适应度 / (1 + gamma * d^2),gamma 越大,空间感知越局部,群体会更快聚拢也可能早熟。 飞行阶段分两类:当前最优(f>=maxF)只做随机游走,步长受 alpha * r * v[c] 控制,v[c] 是该维极差,所以高波动参数自然获得更大探索半径;非最优个体则向 att[i].i 指向的更亮个体靠拢,系数 beta 管收敛强度,alpha 管扰动。 Luminosity 函数负责刷新全局最优 cB 与 fB,用 ArrayCopy 把亮萤火虫的坐标整组搬进缓存。在 MT5 里把 gamma 从 0.5 调到 2.0,回测同一段 XAUUSD 1H 数据,大概率能看到迭代次数从 ~120 掉到 ~40,但过拟合风险同步上升——贵金属波动大,参数空间别搜太狠。
class="type">class="kw">double summCoordinates = class="num">0.0; for (class="type">int c = class="num">0; c < params; c++) { v [c] = rangeMax [c] - rangeMin [c]; summCoordinates += pow(v [c], class="num">2.0); } maxDist = pow(summCoordinates, class="num">0.5); for (class="type">int s = class="num">0; s < swarmSize; s++) { for (class="type">int k = class="num">0; k < params; k++) { fireflies [s].c [k] = RNDfromCI(rangeMin [k], rangeMax [k]); fireflies [s].c [k] = SeInDiSp(fireflies [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]); } } luminosity = true; } class=class="str">"cmt">//measure the distance between all------------------------------------------ for (class="type">int i = class="num">0; i < swarmSize; i++) { att [i].a = -DBL_MAX; for (class="type">int k = class="num">0; k < swarmSize; k++) { if (i == k) class="kw">continue; summCoordinates = class="num">0.0; for (class="type">int c = class="num">0; c < params; c++) summCoordinates += pow(fireflies [i].c [c] - fireflies [k].c [c], class="num">2.0); distance = pow(summCoordinates, class="num">0.5); distance = Scale(distance, class="num">0.0, maxDist, class="num">0.0, class="num">20.0, class="kw">false); attractiveness = fireflies [k].f / (class="num">1.0 + gamma * distance * distance); if (attractiveness > att [i].a) { att [i].a = attractiveness; att [i].i = k; } if (fireflies [i].f > maxF) maxF = fireflies [i].f; } } class=class="str">"cmt">//flight-------------------------------------------------------------------- for (class="type">int i = class="num">0; i < swarmSize; i++) { if (fireflies [i].f >= maxF) { for (class="type">int c = class="num">0; c < params; c++) { r = RNDfromCI(-class="num">1.0, class="num">1.0); fireflies [i].c [c] = fireflies [i].c [c] + alpha * r * v [c]; fireflies [i].c [c] = SeInDiSp(fireflies [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } else { for (class="type">int c = class="num">0; c < params; c++) { r = RNDfromCI(-class="num">1.0, class="num">1.0); Xi = fireflies [i].c [c]; Xj = fireflies [att [i].i].c [c]; fireflies [i].c [c] = Xj + beta * (Xi - Xj) + alpha * r * v [c]; fireflies [i].c [c] = SeInDiSp(fireflies [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } } class="type">void C_AO_FA::Luminosity() { for (class="type">int i = class="num">0; i < swarmSize; i++) { if (fireflies [i].f > fB) { fB = fireflies [i].f; ArrayCopy(cB, fireflies [i].c, class="num">0, class="num">0, WHOLE_ARRAY); } } } class=class="str">"cmt">//flight-------------------------------------------------------------------- for (class="type">int i = class="num">0; i < swarmSize; i++) { if (fireflies [i].f >= maxF) {
◍ 萤火虫算法的位置更新分支
上面这段是群体优化里每只萤火虫参数向量的迭代核心。当个体没有被更亮的同伴吸引时,走自由随机游走;否则朝更优个体做有偏移动,再叠一层随机扰动。 随机项里 r2 取自 [1.0, 20.0] 的连续均匀区间,经过 pow(r2, -2.0) 缩放后,扰动幅度随 r2 增大急剧衰减,意味着小 r2 才容易跳出局部。 无论哪条分支,更新后都过一遍 SeInDiSp,把越界分量夹回 [rangeMin, rangeMax] 并按 rangeStep 离散化——贵金属或外汇参数网格搜索时这一步直接决定回测能否复现。 代码逐行拆解: for (int c = 0; c < params; c++) 遍历当前萤火虫的每一个待优化参数维度。 r1 = RNDfromCI (0.0, 1.0); 先抽一个 [0,1] 均匀随机数。 r1 = r1 > 0.5 ? 1.0 : -1.0; 把它二值化成方向符号,决定扰动往正或负走。 r2 = RNDfromCI (1.0, 20.0); 抽第二个随机数控制扰动尺度。 fireflies [i].c [c] = cB [c] + alpha * r1 * pow (r2, -2.0) * v [c]; 自由分支下以基准向量 cB 为锚,加 alpha 加权的符号化反比幂扰动。 fireflies [i].c [c] = SeInDiSp (...); 将结果约束进合法参数空间。 else 分支里 Xi / Xj 分别取自身与更亮个体的同维参数,Xj + beta*(Xi-Xj) 构成向更优解的牵引项,后面再叠同样的随机扰动并约束。
for (class="type">int c = class="num">0; c < params; c++) { r1 = RNDfromCI(class="num">0.0, class="num">1.0); r1 = r1 > class="num">0.5 ? class="num">1.0 : -class="num">1.0; r2 = RNDfromCI(class="num">1.0, class="num">20.0); fireflies [i].c [c] = cB [c] + alpha * r1 * pow(r2, -class="num">2.0) * v [c]; fireflies [i].c [c] = SeInDiSp(fireflies [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } else { for (class="type">int c = class="num">0; c < params; c++) { r1 = RNDfromCI(class="num">0.0, class="num">1.0); r1 = r1 > class="num">0.5 ? class="num">1.0 : -class="num">1.0; r2 = RNDfromCI(class="num">1.0, class="num">20.0); Xi = fireflies [i].c [c]; Xj = fireflies [att [i].i].c [c]; fireflies [i].c [c] = Xj + beta * (Xi - Xj) + alpha * r1 * pow(r2, -class="num">2.0) * v [c]; fireflies [i].c [c] = SeInDiSp(fireflies [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } }
把这条线请下神坛
这张最终评级表里,改版的萤火虫算法(FAm)综合得分 0.518,压过杜鹃优化 0.513 和蚁群 0.498,在 2 参数任务上拿到 0.985、40 参数任务 0.921,千参任务掉到 0.299,说明规模一大收敛质量会塌。经典萤火虫只有 0.400,随机搜索 0.382,落后幅度肉眼可见。 从下篇起作者改用百点直方图,最强满百、最差给一,读图比盯表直观。元启发式本质是用带假设的随机搜空间,不保最优但给可接受解,问题结构对其干扰小,所以能塞进 EA 自优化、神经网络训练里跑。 FAm 优点是改起来轻、扩展性高、能把搜索区聚成围绕局部极值的簇;缺点是 beta 和 gamma 调不好就卡死在局部,设置敏感度高。外汇与贵金属波动剧烈,拿这类算法做参数寻优属高风险尝试,真要落地建议先开 MT5 用附带的 ZIP 里的 MQL5 类跑一遍 40 参回测。