群体优化算法:混合蛙跳算法(SFL)·综合运用
📘

群体优化算法:混合蛙跳算法(SFL)·综合运用

第 3/3 篇

局部极值判定与数组洗牌的实现细节

在青蛙算法(Frog Leaping)的 memeplex 更新循环里,frogStep 与 frogStepsToLocalMax 的关系直接决定个体是继续局部搜索还是被强制重置。当 frogStep 未超过阈值时,只要当前适应度 f 优于前次 fPrev,就把 fPrev 刷新为 f、用 ArrayCopy 把当前坐标 c 整组拷进 cPrev,并将 frogStep 归零,相当于确认了一次有效的局部前进。 一旦 frogStep 越过阈值,逻辑分两层:若再超出 1 步(即 >= frogStepsToLocalMax + 1)且 f 仍创新高,同样执行刷新并归零;否则进入 else 分支,把 f、fPrev 都置为 -DBL_MAX、frogStep 归零,等于把这个青蛙的局部记忆清空,防止它在失效区域空转。外汇与贵金属行情跳变频繁,这类重置机制能降低劣质局部解对种群的污染,但参数敏感,实盘前需在 MT5 策略测试器里跑多品种验证。 Shuffle 函数负责把整数数组 arr 做 Fisher–Yates 洗牌:从尾到头,每次用 MathRand() % (i+1) 取一个 0~i 的随机下标,与当前位交换。size 为数组长度,循环到 i=1 为止。这段代码无返回值,直接通过引用修改原数组。 别把重置阈值当常量写死 frogStepsToLocalMax 若固定取 5 或 10,在 EURUSD 的 M15 与 XAUUSD 的 H1 上表现可能分化明显。建议开 MT5 把该变量接进输入参数,用优化器扫一遍 3~15 的区间,观察种群收敛步数再定。

MQL5 / C++
if (mems [m].frogs [frgs].frogStep <= frogStepsToLocalMax)
{
  if (mems [m].frogs [frgs].f > mems [m].frogs [frgs].fPrev)
  {
    mems [m].frogs [frgs].fPrev = mems [m].frogs [frgs].f;
    ArrayCopy(mems [m].frogs [frgs].cPrev, mems [m].frogs [frgs].c, class="num">0, class="num">0, WHOLE_ARRAY);
    mems [m].frogs [frgs].frogStep = class="num">0;
  }
}
else
{
  if (mems [m].frogs [frgs].frogStep >= frogStepsToLocalMax + class="num">1)
  {
    if (mems [m].frogs [frgs].f > mems [m].frogs [frgs].fPrev)
    {
      mems [m].frogs [frgs].fPrev = mems [m].frogs [frgs].f;
      ArrayCopy(mems [m].frogs [frgs].cPrev, mems [m].frogs [frgs].c, class="num">0, class="num">0, WHOLE_ARRAY);
      mems [m].frogs [frgs].frogStep = class="num">0;
    }
  }
  else
  {
    mems [m].frogs [frgs].f        = -DBL_MAX;
    mems [m].frogs [frgs].fPrev    = -DBL_MAX;
    mems [m].frogs [frgs].frogStep = class="num">0;
  }
}
cnt++;
}
}
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void Shuffle(class="type">int & arr [], class="type">int size)
{
  class="type">int index, temp;
  for (class="type">int i = size - class="num">1; i > class="num">0; i--)
  {
    index = MathRand() % (i + class="num">1);
    temp = arr [i];
    arr [i] = arr [index];
    arr [index] = temp;
  }
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————

「SFL 在三类测试函数上的实测表现」

把混合蛙跳(SFL)塞进标准测试场跑了一遍:参数组设为 5 / 25 / 500 个优化变量,每种函数各跑 10000 次。Rastrigin 光滑函数上,5 变量组得分 0.82429、500 变量组掉到 0.55211;Forest 弯曲函数同组别从 0.32597 跌至 0.02810;Megacity 离散函数更弱,500 变量组仅 0.02503。三项总评分 2.84501,单看 SFL 自身不算崩,但横向一比就露怯。 观察代理点动画时,预期会看到的模因聚类完全没出现,青蛙在整个解空间里乱跳、没有可辨识的运动模式。收敛图上一截截长而平滑的线段,就是陷进局部极值的直接证据;不过随优化参数从 5 涨到 500,「步进」式跳动确实在变少,说明维度高了反而更安静——安静得不像在搜优。 拉一张 15 个算法的横评表(满分按各函数最佳归一,总评以 GWO=1.000 为基准):SSG 100.000、HS 92.329 排前,SFL 只拿 15.203,压在 MA(13.603)、FSS(12.655)、PSO(10.031)头上,比随机搜索的 5.302 好一点有限。Forest 和 Megacity 上 SFL 的 500 变量得分分别仅 0.02810 与 0.02503,原因很直白——平坦地形里跳蛙不升反回,抓不住地貌特征。 外汇与贵金属 EA 参数寻优属高风险实验,回测分数高不等于实盘能跑;SFL 这类群体算法建议只在光滑目标函数上小参数组试水,弯曲或离散约束下换 SSG / HS 更稳。

◍ 把SFL当配件而非引擎

混合蛙跳(SFL)本质上更像是一层调度逻辑,而不是能单挑全局优化的底层引擎。原始设计意图是给已有算法做质量增强,在前面几篇跑过的标准测试集里,它作为独立优化器拿到的评级分布在0到100区间,整体处于中下水平,图3的直方图能直接看出长尾偏左。 它的可玩点在「模因复合体」:你能在青蛙族群移动的步骤里塞进别的优化器,用低外部参数(仅少数几个)换来架构弹性。代价也硬——计算复杂度高,碰到平滑或离散函数容易掉链子,在过于平坦的响应面上会直接卡死不动。 附带的17_The_world_of_AO_SFL.zip(143.2 KB)里有可跑脚本,档案同时收着前几篇算法的最新版代码。想验证SFL弱点,把测试函数换成Rastrigin这类多峰离散面,MT5里跑一遍直方图就能看到评级塌方。外汇与贵金属品种参数面常带平板区,直接套SFL可能陷入局部不动点,实盘前务必离线压力测试。 下面这段工厂注册代码是SFL测试框架的骨架,把所有待测函数对象在初始化阶段一次性建好工厂: static FunctionInstance *pointers[] = {C_Function::fabric<C_Skin>(), C_Function::fabric<C_Forest>(), C_Function::fabric<C_Megacity>(), C_Function::fabric<C_Rastrigin>(), C_Function::fabric<C_Universe>()}; 逐行看:static数组在加载期分配,五个fabric模板调用分别对应Skin、Forest、Megacity、Rastrigin、Universe测试面,每个返回函数实例指针;注意工厂常驻而非按需创建,程序生命周期内类型固定,想加新面得改这行重编译。

MQL5 / C++
class="kw">static FunctionInstance *pointers[] = {C_Function::fabric<C_Skin>(), C_Function::fabric<C_Forest>(), C_Function::fabric<C_Megacity>(), C_Function::fabric<C_Rastrigin>(), C_Function::fabric<C_Universe>()};

常见问题

记录该青蛙连续多代最优值无变化即判为局部极值,触发子群重排或随机扰动来跳出。
Sphere 类收敛最快,Rastrigin 因多峰易陷局部极值需更多迭代,Griewank 介于两者之间,具体代数看原文表格。
小布可替你执行数组洗牌与极值判定流程,直接输出每代子群分布和卡点提醒,你只管调参。
洗牌打乱青蛙排序重组子群,避免近亲迭代;不洗会让信息圈固化,收敛慢且易早熟。
只用 SFL 做参数初筛或特征子集挑选,真正信号生成交给规则或统计模型,降低过拟合风险。