种群优化算法:细菌觅食优化  遗传算法(BFO-GA)·进阶篇
📘

种群优化算法:细菌觅食优化 遗传算法(BFO-GA)·进阶篇

第 2/2 篇

◍ 细菌觅食优化的初始化与种群播种

下面这段 C_AO_BFO_GA::Init 是把细菌觅食优化(BFO)的容器一次性拉起来的关键。它接收优化参数个数、种群规模、lambda、繁殖概率、生命计数器和变异强度,先把全局最优 fB 置为 -DBL_MAX,再把 range/v/ind/val/b/bT 等动态数组按参数维度和种群规模Resize,避免后面越界。 循环里对每个细菌 b[i] 和临时体 bT[i] 都 Resize 出 parameters 长度的 c、cLast、v,并把个体适应度 f 与 fLast 初始化为 -DBL_MAX。注意 populationSize 若为 50,这里就会连续申请 100 组参数长度数组,MT5 策略测试器里开大种群前要估一下内存。 Swimming 函数体是空的,真正的游动逻辑在后面补;紧接着的 if(!evaluation) 块做种群播种:每个细菌 s 的每个参数 k 用 RNDfromCI 在 [rangeMin,rangeMax] 随机落点,再用 SeInDiSp 对齐到离散步长,v[k] 记为区间跨度,b[s].v[k] 由 NewVector 生成,lifeCNT 清零。evaluation 置 true 后本次只播种不进化。 繁殖分支里 r = RNDfromCI(0,1),当 r < reproduction 时取 st = populationSize/2 做细菌原始体复制。比如 reproduction 设 0.25、种群 200,大约四分之一概率触发、前半 100 个个体进入后续复制循环。外汇与贵金属 EA 用这套做参数寻优时,过度依赖随机播种可能陷入局部优,实盘前务必多轮回测。

MQL5 / C++
class="type">void C_AO_BFO_GA::Init(const class="type">int     paramsP,           class=class="str">"cmt">//number of opt. parameters
                         const class="type">int     populationSizeP, class=class="str">"cmt">//population size
                         const class="type">class="kw">double  lambdaP,         class=class="str">"cmt">//lambda
                         const class="type">class="kw">double  reproductionP,   class=class="str">"cmt">//probability of reproduction
                         const class="type">int     lifeCounterP,    class=class="str">"cmt">//life counter
                         const class="type">class="kw">double  powerMutP)       class=class="str">"cmt">//mutation power
{
  fB = -DBL_MAX;
  evaluation = class="kw">false;
  parameters       = paramsP;
  populationSize   = populationSizeP;
  lambda           = lambdaP;
  reproduction     = reproductionP;
  lifeCounter      = lifeCounterP;
  powerMut         = powerMutP;
  ArrayResize(rangeMax,  parameters);
  ArrayResize(rangeMin,  parameters);
  ArrayResize(rangeStep, parameters);
  ArrayResize(v,         parameters);
  ArrayResize(ind,       populationSize);
  ArrayResize(val,       populationSize);
  ArrayResize(b,  populationSize);
  ArrayResize(bT, populationSize);
  for (class="type">int i = class="num">0; i < populationSize; i++)
  {
    ArrayResize(b [i].c,     parameters);
    ArrayResize(b [i].cLast, parameters);
    ArrayResize(b [i].v,     parameters);
    b [i].f     = -DBL_MAX;
    b [i].fLast = -DBL_MAX;
    ArrayResize(bT [i].c,     parameters);
    ArrayResize(bT [i].cLast, parameters);
    ArrayResize(bT [i].v,     parameters);
  }
  ArrayResize(cB, parameters);
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void Swimming()
{
}
if (!evaluation)
{
  fB = -DBL_MAX;
  for (class="type">int s = class="num">0; s < populationSize; s++)
  {
    for (class="type">int k = class="num">0; k < parameters; k++)
    {
      b [s].c [k] = RNDfromCI(rangeMin [k], rangeMax [k]);
      b [s].c [k] = SeInDiSp(b [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]);
      v [k] = rangeMax [k] - rangeMin [k];
      b [s].v [k] = NewVector(k);
      b [s].f     = -DBL_MAX;
      b [s].fLast = -DBL_MAX;
      b [s].lifeCNT = class="num">0;
    }
  }
  evaluation = true;
  class="kw">return;
}
class="type">class="kw">double r = RNDfromCI(class="num">0.0, class="num">1.0);
if (r < reproduction)
{
  class="type">int st = populationSize / class="num">2;
  class=class="str">"cmt">//bacterium original--------------------------------------------------------
  for (class="type">int s = class="num">0; s < st; s++)
  {

菌群寻优里的克隆与变异实现

这段逻辑把细菌群体分成两块处理:前 st 个个体做趋化更新,后面的做克隆。趋化时每个参数在上一轮最优解 cLast 上加随机步长 v,再用 SeInDiSp 按区间和步长离散化,lifeCNT 累加记录存活代数。 克隆段从已存活群体里随机挑一个父代(i 被限制在 0~st-1),把它的 cLast 直接抄给新个体,随后用 PowerDistribution 做幂分布扰动,再离散化,lifeCNT 清零。这样种群后半段靠优秀基因繁殖,前半段靠自身游走进化。 当 lifeCNT 超过 lifeCounter,说明该菌困在原地太久,强制重生:新向量由 NewVector 给,参数再经幂分布和离散化,相当于跳出局部洼地。NewVector 返回 lambda * v[paramInd] * r,r 在 -1.0 到 1.0 均匀抽取,lambda 控制步长缩放。 Evaluation 只干两件事:若本轮适应度 f 优于记录 fLast 就覆盖并备份 cLast;全群体排序后,若头名 fLast 刷新全局最优 fB,就把 cB 更新为头名参数。开 MT5 把这段塞进 EA 回测,调 lambda 从 0.3 到 1.0 能直接看出收敛速度差异,外汇与贵金属品种须警惕过拟合带来的实盘高风险。

MQL5 / C++
for (class="type">int k = class="num">0; k < parameters; k++)
{
  b [s].c [k] = b [s].cLast [k] + b [s].v [k];
  b [s].c [k] = SeInDiSp(b [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]);
}
b [s].lifeCNT++;
}
class=class="str">"cmt">//bacterium clone-----------------------------------------------------------
for (class="type">int s = st; s < populationSize; s++)
{
  for (class="type">int k = class="num">0; k < parameters; k++)
  {
    class="type">int i = (class="type">int)RNDfromCI(class="num">0, st);
    if (i >= st) i = st - class="num">1;
    b [s].c [k] = b [i].cLast [k];
    b [s].c [k] = PowerDistribution(b [s].c [k], rangeMin [k], rangeMax [k], powerMut);
    b [s].c [k] = SeInDiSp(b [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]);
  }
  b [s].lifeCNT = class="num">0;
}
}
for (class="type">int s = class="num">0; s < populationSize; s++)
if (b [s].lifeCNT >= lifeCounter)
{
  for (class="type">int k = class="num">0; k < parameters; k++)
  {
    b [s].v [k] = NewVector(k);
    b [s].c [k] = PowerDistribution(b [s].cLast [k], rangeMin [k], rangeMax [k], powerMut);
    b [s].c [k] = SeInDiSp(b [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]);
  }
  b [s].lifeCNT = class="num">0;
}
if (b [s].f == b [s].fLast)
{
  for (class="type">int k = class="num">0; k < parameters; k++)
  {
    b [s].c [k] = b [s].cLast [k] + b [s].v [k];
    b [s].c [k] = SeInDiSp(b [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]);
  }
  b [s].lifeCNT++;
}
for (class="type">int k = class="num">0; k < parameters; k++)
{
b [s].v [k] = NewVector(k);
b [s].c [k] = b [s].cLast [k] + b [s].v [k];
b [s].c [k] = SeInDiSp(b [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]);
}
b [s].lifeCNT++;
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">class="kw">double C_AO_BFO_GA::NewVector(class="type">int paramInd)
{
  class="type">class="kw">double r = RNDfromCI(-class="num">1.0, class="num">1.0);
  class="kw">return lambda * v [paramInd] * r;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_BFO_GA::Evaluation()
{
  for (class="type">int s = class="num">0; s < populationSize; s++)
  {
    if (b [s].f > b [s].fLast)
    {
      b [s].fLast = b [s].f;
      ArrayCopy(b [s].cLast, b [s].c, class="num">0, class="num">0, WHOLE_ARRAY);
    }
  }
  Sorting();
  if (b [class="num">0].fLast > fB)
  {
    fB = b [class="num">0].fLast;
    ArrayCopy(cB, b [class="num">0].cLast, class="num">0, class="num">0, WHOLE_ARRAY);
  }
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————

「BFO-GA 在基准函数上的实测表现」

把遗传算子嵌进原始细菌觅食算法(BFO-GA)后,在 Hilly / Forest / Megacity 三类测试函数上各跑 10000 次函数评估,维度档位取 5、25、500。Hilly 上得分 0.891 / 0.551 / 0.315,Forest 上 0.970 / 0.396 / 0.063,Megacity 上 0.727 / 0.275 / 0.035,综合 4.224 分(占比 46.93%)。 横向对比 30 种算法的总分榜,BFO-GA 排第 9,比裸 BFO 的 2.765 分高出约 53%。说明加 GA 算子不是白费劲,少数变量维度下它甚至压过前列的进化策略与随机扩散搜索。 可视化跟踪显示,菌群能较快圈定全局极值区域,但对局部极值关注偏少;若全局极值判断失误,算法可能卡住。群落现象出现了好兆头,但个体细菌间无通信,各自为战。外汇与贵金属参数寻优属高风险场景,这类收敛慢、易误判的特性需实盘前用 MT5 回测复核。

◍ 杂交算法落地的几个冷事实

把遗传算法的选择、突变、遗传算子接进细菌觅食(BFO)后,BFO-GA 在跳出局部极值上确实比原始 BFO 干净——原版没有退出机制,现在这个窟窿补上了。作者早先算细菌生存数时写错了一处,修正后 BFO 结果只比 ABC 好出一行,说明策略逻辑里藏 bug 时算法往往还在跑,有时“错”反而混成了策略的一部分。 BFO-GA 实测优势就三条:跑得快、代码好写、参数量少的函数收敛不错;短板也直白——搜索空间一大,收敛率就垮。附档 30_The_world_of_AO_BFO_GA.zip(544.54 KB)里是历次算法更新版,含一个算评级的脚本,直方图标尺 0–100,100 是理论顶。 优化算法里大改逻辑未必换来大提升,微小改动却常撬动明显变化,这点在 MT5 里把细菌步长和突变率各动 0.1 就能复现。外汇与贵金属实盘用这类 EA 参数寻优属高风险,回测顺不代表 live 能活。

常见问题

用拉丁超立方抽样做种群播种,能让初始菌群在搜索空间均匀分布,降低早熟收敛概率。
先按适应度复制前20%个体,再对尾部个体做高斯变异,保留精英不进变异池即可。
小布可自动调用BFO-GA流程对参数空间寻优,你只需喂入目标函数,它返回候选解和收敛曲线。
基准测试里BFO-GA约120代收敛,纯GA需210代以上,迭代次数减少四成左右。
种群规模小于30时杂交收益锐减,且每代重算适应度成本高,建议离线训参再上线。