种群优化算法:萤火虫算法(FA)·进阶篇
📘

种群优化算法:萤火虫算法(FA)·进阶篇

第 2/2 篇

萤火虫算法的类结构与初始化落点

这段代码实现了一个基于萤火虫群智能的优化器类 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 跑空优化,能验证种群内存布局是否符合预期。 外汇与贵金属市场波动剧烈、杠杆风险高,这类智能优化仅用于历史参数拟合,实盘信号倾向失效,须以模拟盘先行验证。

MQL5 / C++
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,但过拟合风险同步上升——贵金属波动大,参数空间别搜太狠。

MQL5 / C++
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) 构成向更优解的牵引项,后面再叠同样的随机扰动并约束。

MQL5 / C++
  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 参回测。

常见问题

均匀铺满容易漏掉局部洼地,随机扰动能让初始萤火虫更分散,提升后期寻优覆盖;代码里用 (MathRand()/32767.0) 做归一化扰动即可。
欧氏距离在连续参数空间更常用,曼哈顿距离在高维稀疏空间可能更抗噪;建议先跑欧氏,回测胜率掉再换。
可以。小布盯盘的 AIGC 已内置种群优化诊断,打开对应品种页就能让它替你跑初始化与位移分支,你只看收敛曲线。
更亮分支主导吸引移动,更暗分支加随机游走防早熟;实盘参数少时只留亮分支也行,维度高了必须开暗分支。
未必。样本外它容易过拟合,概率上只比网格省时间;真上线前请用 walk-forward 验证,别直接信回测尖峰。