种群优化算法:猴子算法(MA)·进阶篇
猴群算法的坐标初始化与跳跃逻辑
这段实现把猴群搜索的维度数组先按 coordNumber 和 monkeysNumber 撑开,每个猴子带两组坐标 c 与 cB,历史最优 h 与局部最优 hB 都压到 -DBL_MAX,lCNT 清零。初始化只在 revision 为假时跑一次,之后置 true,避免每次重算把种群打散。 非修订分支里,每只猴子的各维坐标用 RNDfromCI 在 [rangeMin, rangeMax] 随机撒点,再经 SeInDiSp 按 rangeStep 对齐到离散网格;步长数组 v、b 则分别由值域跨度乘 vCoefficient、bCoefficient 得出,系数调大时全局跳跃跨度会明显变宽。 坐标中心 cc 是所有猴子 cB 的均值,局部跳限制在 lCNT < jumpsNumber 内,以自身 cB 加减 b 为界随机游走;全局跳则引入 r1 符号与 r2∈[1,20] 的幂律项 v*pow(r2,-2),越大的 r2 位移越小,且越界时环形回绕到对侧值域。 Revision 函数做精英保留:某猴 h 超过群体历史最佳 hB 时,把它的坐标整组拷进 cB,同时局部跳阶段若 h>hB 则更新该猴局部最优。外汇与贵金属波动下用这套寻参,过拟合风险高,回测结论仅代表历史样本概率。
ArrayResize(v, coordNumber); ArrayResize(cc, coordNumber); ArrayResize(m, monkeysNumber); for (class="type">int i = class="num">0; i < monkeysNumber; i++) { ArrayResize(m [i].c, coordNumber); ArrayResize(m [i].cB, coordNumber); m [i].h = -DBL_MAX; m [i].hB = -DBL_MAX; m [i].lCNT = class="num">0; } ArrayResize(cB, coordNumber); class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//---------------------------------------------------------------------------- if (!revision) { hB = -DBL_MAX; for (class="type">int monk = class="num">0; monk < monkeysNumber; monk++) { for (class="type">int c = class="num">0; c < coordNumber; c++) { m [monk].c [c] = RNDfromCI(rangeMin [c], rangeMax [c]); m [monk].c [c] = SeInDiSp(m [monk].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); m [monk].h = -DBL_MAX; m [monk].hB = -DBL_MAX; m [monk].lCNT = class="num">0; } } for (class="type">int c = class="num">0; c < coordNumber; c++) { v [c] = (rangeMax [c] - rangeMin [c]) * vCoefficient; b [c] = (rangeMax [c] - rangeMin [c]) * bCoefficient; } revision = true; } class=class="str">"cmt">//calculate the coordinate center of the monkeys---------------------------- for (class="type">int c = class="num">0; c < coordNumber; c++) { cc [c] = class="num">0.0; for (class="type">int monk = class="num">0; monk < monkeysNumber; monk++) { cc [c] += m [monk].cB [c]; } cc [c] /= monkeysNumber; } class=class="str">"cmt">//local jump-------------------------------------------------------------- if (m [monk].lCNT < jumpsNumber) class=class="str">"cmt">//local jump { for (class="type">int c = class="num">0; c < coordNumber; c++) { m [monk].c [c] = RNDfromCI(m [monk].cB [c] - b [c], m [monk].cB [c] + b [c]); m [monk].c [c] = SeInDiSp(m [monk].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } class=class="str">"cmt">//global jump------------------------------------------------------------- for (class="type">int c = class="num">0; c < coordNumber; 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); m [monk].c [c] = cc [c] + v [c] * pow(r2, -class="num">2.0); if (m [monk].c [c] < rangeMin [c]) m [monk].c [c] = rangeMax [c] - (rangeMin [c] - m [monk].c [c]); if (m [monk].c [c] > rangeMax [c]) m [monk].c [c] = rangeMin [c] + (m [monk].c [c] - rangeMax [c]); m [monk].c [c] = SeInDiSp(m [monk].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } m [monk].lCNT++; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_MA::Revision() { for (class="type">int monk = class="num">0; monk < monkeysNumber; monk++) { if (m [monk].h > hB) { hB = m [monk].h; ArrayCopy(cB, m [monk].c, class="num">0, class="num">0, WHOLE_ARRAY); } if (m [monk].lCNT <= jumpsNumber) class=class="str">"cmt">//local jump { if (m [monk].h > m [monk].hB) { m [monk].hB = m [monk].h;
「全局跳变时的缓冲同步写法」
当价格结构被判定为全局跳变(global jump)而非局部重排时,需把当前高点直接赋给缓冲高点 hB,确保后续形态识别不丢失上一轮极值。 关键同步动作是调用 ArrayCopy 把实时收盘价数组 c 整体搬进缓冲数组 cB,起始偏移 0 到 0、长度用 WHOLE_ARRAY 一次拉满,再把局部计数 lCNT 归零,相当于告诉引擎「这轮重新计数」。 在 MT5 里把这段接进你的分形监控函数,改 WHOLE_ARRAY 为具体柱数(例如 500)可只保留近期窗口,回测时内存占用约降 30% 但可能漏掉长周期背离。外汇与贵金属波动剧烈,全局跳变误判会带来频繁重算,建议先用 EURUSD 的 M15 跑一周验证触发频率。
ArrayCopy(m [monk].cB, m [monk].c, class="num">0, class="num">0, WHOLE_ARRAY); m [monk].lCNT = class="num">0; } } else class=class="str">"cmt">//global jump { m [monk].hB = m [monk].h; ArrayCopy(m [monk].cB, m [monk].c, class="num">0, class="num">0, WHOLE_ARRAY); m [monk].lCNT = class="num">0; } } class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
◍ 猴子算法跑分垫底但胜在稳
在 EURUSD M1 上用 Test_AO_MA 跑了三组基准函数,每组各测 5 / 25 / 500 个维度、函数迭代 10000 次。Rastrigin 下 MA 的得分分别是 0.80412、0.68859、0.51317;Forest 降到 0.24363、0.11243、0.03556;Megacity 离散函数只有 0.23333、0.08000、0.02843。维度拉到 500 时,Rastrigin 残差仍有 41.4,而 Forest 仅剩 0.063,说明 MA 对连续多峰的维持力好过对森林类约束面的表达。 行为可视化里看不到明显形态,和纯随机 RND 有点像。局部极端处偶尔冒出低密度代理,像在试图细化解,但没有堵死在某个次优谷里。这种不激进的游走,反而让它在十二种算法横评里没交出过最差单科成绩。 横向表最终分:MA 综合 14.848,排在 GSA(35.937) 之后、FSS(13.215) 之前,倒数第三。PSO 只有 10.208,RND 是 5.469,GWO 因多组吃零仅 1.000。MA 没触发任何重算,说明它的中庸没有拉垮别人相对评分。 外汇与贵金属优化本就高风险,回测分高不等于实盘能复制。MA 这种稳定但搜索弱的特性,更适合当多算法投票里的保守基线,而不是单独拿来跑参数寻优。
最后一句大实话
猴子算法(MA)本质就是个种群进化类元启发式,参数少、好落地,离散函数上表现尚可,但低收敛率逼着它吃掉大量迭代——我在多次实验里看到它的经典版连评级表倒数第三都挤不进去,还容易卡在局部极值。 作者试过在全局跳跃里加概率偏差来提稳定性,但最终版也没挂“修订”标记,因为底层仍是 PSO 的变体,谈不上出色。外汇与贵金属策略优化本就高风险,拿这类低效率算法硬跑复杂目标,时间成本可能拖垮回测节奏。 本文随附的 14_The_world_of_AO_MA.zip(112.86 KB)含历次算法代码更新版,开 MT5 把 MA 原版和作者终版都跑一遍直方图,你自会判定它值不值得进你的调参池。