种群优化算法:细菌觅食优化(BFO)·进阶篇
菌群优化里的种群初始化与游动逻辑
这段 MT5 代码实现了一个细菌觅食优化(BFO)变体的核心内存布局与单步迭代。初始化时先按 populationSize 扩展细菌数组 bT,再给每个细菌的候选参数 c、步长 v 分配 parameters 维空间,适应度 f 与 fLast 统一置为 -DBL_MAX,保证首轮评估前没有虚假优解。 若 evaluation 为 false,会对全部 s<populationSize 的个体做随机撒点:用 RNDfromCI 在 [rangeMin, rangeMax] 内取初值,再用 SeInDiSp 按 rangeStep 离散化,v[k] 初始化为区间跨度,lifeCNT 清零。置 evaluation=true 后,这套随机播种只跑一次。 每步先抽 r=RNDfromCI(0,1),当 r<reproduction 时进入繁殖分支:种群前半 st=populationSize/2 作为原菌,后半复制其 c 并叠加 v 生成子代,原菌自身也刷新向量后 lifeCNT 归零。这是典型的半数克隆扩张,种群规模不变但探索方向翻倍。 生命周期由 lifeCounter 控制:lifeCNT 超限就重抽 NewVector 并复位计数;未超限则比较 f 与 fLast,变好就沿旧 v 继续游动并累加 lifeCNT,变差则重抽向量再迈一步。NewVector 返回 lambda*v*RNDfromCI(-1,1),步长受 lambda 缩放且在 [-1,1] 均匀扰动,外汇与贵金属参数寻优中 lambda 过大可能让菌群发散、过小则收敛慢,属高风险调参实验。
ArrayResize(bT, populationSize); for (class="type">int i = class="num">0; i < populationSize; i++) { ArrayResize(b [i].c, parameters); ArrayResize(b [i].v, parameters); b [i].f = -DBL_MAX; b [i].fLast = -DBL_MAX; ArrayResize(bT [i].c, parameters); ArrayResize(bT [i].v, parameters); } ArrayResize(cB, parameters); class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_BFO::Swimming() {} class=class="str">"cmt">//---------------------------------------------------------------------------- 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; } r = RNDfromCI(class="num">0.0, class="num">1.0); class=class="str">"cmt">//========================================================================== if (r < reproduction) { class="type">int st = populationSize / class="num">2; for (class="type">int s = class="num">0; s < st; s++) { class=class="str">"cmt">//bacterium original-------------------------------------------------- for (class="type">int k = class="num">0; k < parameters; k++) { b [st + s].v [k] = b [s].v [k]; b [st + s].c [k] = b [s].c [k] + b [s].v [k]; b [st + s].c [k] = SeInDiSp(b [st + s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]); b [st + s].fLast = b [s].f; b [st + s].lifeCNT++; } class=class="str">"cmt">//bacterium clone------------------------------------------------------- for (class="type">int k = class="num">0; k < parameters; k++) { b [s].v [k] = NewVector(k); b [s].c [k] = b [s].c [k] + b [s].v [k]; b [s].c [k] = SeInDiSp(b [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]); b [s].fLast = b [s].f; b [s].lifeCNT = class="num">0; } } } 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] = b [s].c [k] + b [s].v [k]; b [s].c [k] = SeInDiSp(b [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]); b [s].fLast = b [s].f; b [s].lifeCNT = class="num">0; } } else { if (b [s].f >= b [s].fLast) { for (class="type">int k = class="num">0; k < parameters; k++) { b [s].c [k] = b [s].c [k] + b [s].v [k]; b [s].c [k] = SeInDiSp(b [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]); b [s].fLast = b [s].f; b [s].lifeCNT++; } } else { for (class="type">int k = class="num">0; k < parameters; k++) { b [s].v [k] = NewVector(k); b [s].c [k] = b [s].c [k] + b [s].v [k]; b [s].c [k] = SeInDiSp(b [s].c [k], rangeMin [k], rangeMax [k], rangeStep [k]); b [s].fLast = b [s].f; b [s].lifeCNT++; } } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">class="kw">double C_AO_BFO::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">//——————————————————————————————————————————————————————————————————————————————
「AO缓冲择优的拷贝逻辑」
C_AO_BFO::Evaluation 的职责很单纯:先 Sorting() 把候选集 b 按某种适配度排好序,再只盯住排头元素。 若 b[0].f 大于当前最优 fB,说明本轮涌现了更优解,于是把 fB 刷新为 b[0].f,并用 ArrayCopy 把 b[0].c 整列拷进 cB 作为留底。 这段代码没有回测打印,但隐含一个可验证事实:cB 里存的永远是历史最优染色体,而非最新一代。开 MT5 在 Evaluation 末尾加 Print(fB) 跑几百代,能看到 fB 单调不降。 外汇与贵金属 EA 里套用此结构时记住,择优只防过拟合不防爆仓,参数空间搜得再漂亮,实盘仍是高风险。
class="type">void C_AO_BFO::Evaluation() { Sorting(); if (b [class="num">0].f > fB) { fB = b [class="num">0].f; ArrayCopy(cB, b [class="num">0].c, class="num">0, class="num">0, WHOLE_ARRAY); } }
◍ BFO 跑分与横向对比
在 US500Cash 的 M1 周期上用 Test_AO_BFO 脚本压了一遍细菌觅食优化(BFO)。Rastrigin 函数 10000 次运行的结果随维度上升而衰减:5 函数(10 参数)得分 0.90383,25 函数(50 参数)掉到 0.67848,500 函数(1000 参数)只剩 0.50391。Forest 与 Megacity 离散函数更弱,500 函数得分分别仅 0.04948 与 0.03527。 把 BFO 丢进 11 个算法的横向评分表看总体位置:它拿 55.516 分,排在中游,比 IWO(100)、ACOm(95.996)低,但明显强于 PSO(14.232)和 RND(8.142)。亮点是 10 变量 Rastrigin 上表现接近头部,而 1000 参数 Megacity 上相对不崩,说明扩展性比部分群智能算法耐打。外汇与贵金属模型优化属高风险实验,回测分数不直接等于实盘稳健性。 动画可视化印证了一个关键改动:把趋化性里的“大于”算符换成“大于或等于”,细菌能在 Forest、Megacity 的水平段继续蹭着走,即便梯度为零也不停步。菌落还会自发裂成几团贴住不同局部极值,代码里并没写亚菌落逻辑,算意外收获。 BFO 本身优点是对初值不敏感、逻辑简单、易并行;缺点是收敛慢、步长固定、缺跳出局部极值的机制。当前这版只是实验起点,控制参数自适应是后续值得在 MT5 里自己改着试的方向。
最后说句实在话
上面几节把 Linux 下用 Mingw 编跨平台 MT5 DLL、EA 自动化里的误触发坑、SQLite 管宏观事件库、以及默里级别实战都摊开了。技术路径本身不复杂,难的是你真去 Wine 里跑通一次 POSIX 线程版,再和 Win32 版比延迟。 那行 MQL5 输入只是入口参数,Test1FuncRuns_P=5 意味着单次基准测 5 个功能函数,你改这个数就能直接复测线程调度开销。外汇和贵金属杠杆高,任何 DLL 或 EA 在实盘前都先用策略测试器跑历史,亏损概率始终存在。 开 MT5 把那段 input 贴进脚本,调成 20 跑一遍,比看十篇文档都实在。
class="kw">input class="type">int Test1FuncRuns_P = class="num">5; class=class="str">"cmt">//class="num">1) 测试中的功能数量