人工喷淋算法(ASHA)·进阶篇
◍ ASHA 代理类的初始化与游走逻辑
这段代码定义了一个基于水体渗透隐喻的优化代理类 C_AO_ASHA,核心参数藏在类成员里:F 是水流速度,δ 是渗透阻力阈值,β 控制概率变化率,ρ0 是初始概率。它们共同决定代理在参数空间里的探索强度,外汇或贵金属策略寻参时这类随机搜索对过拟合有缓冲作用,但实盘仍属高风险。 Init 函数接收四个引用数组:rangeMinP、rangeMaxP、rangeStepP 分别约束搜索下界、上界与步长,epochsP 设定总迭代轮数(默认 0)。内部先调 StandardInit 做范围合法性校验,再把 epochs 与 epochNow 归零,并按 popSize 用 ArrayResize 给代理数组 aT 分配内存。 Moving 是每轮迭代的驱动函数。首轮若 revision 为 false,就对每个代理的 coords 维坐标在 [rangeMin, rangeMax] 内做均匀随机(u.RNDfromCI)再对齐到步长网格(u.SeInDiSp),之后置 revision 为 true 并返回。 后续轮次进入真实游走:先算动态概率 ρ = MathMax(β*(epochs-epochNow)/epochs, ρ0),随 epochNow 逼近 epochs,ρ 倾向线性衰减但保底 ρ0。xOld、xNew、xLower、xLowest、inf 等局部变量预留给后续渗透判断,开 MT5 把这段抄进 EA 框架就能观察代理随 epoch 收缩的分布。
ρ0 = params [class="num">4].val; } class="type">bool Init(class="kw">const class="type">class="kw">double &rangeMinP [], class=class="str">"cmt">//minimum search range class="kw">const class="type">class="kw">double &rangeMaxP [], class=class="str">"cmt">//maximum search range class="kw">const class="type">class="kw">double &rangeStepP [], class=class="str">"cmt">//step search class="kw">const class="type">int epochsP = class="num">0); class=class="str">"cmt">//number of epochs class="type">void Moving(); class="type">void Revision(); class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">class="kw">double F; class=class="str">"cmt">//water flow velocity class="type">int δ; class=class="str">"cmt">//resistance level(infiltration threshold) class="type">class="kw">double β; class=class="str">"cmt">//parameter that controls the rate of change in probability class="type">class="kw">double ρ0; class=class="str">"cmt">//initial probability class="kw">private: class=class="str">"cmt">//------------------------------------------------------------------- S_AO_Agent aT []; class="type">int epochs; class="type">int epochNow; }; class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">bool C_AO_ASHA::Init(class="kw">const class="type">class="kw">double &rangeMinP [], class="kw">const class="type">class="kw">double &rangeMaxP [], class="kw">const class="type">class="kw">double &rangeStepP [], class="kw">const class="type">int epochsP = class="num">0) { if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return class="kw">false; class=class="str">"cmt">//---------------------------------------------------------------------------- epochs = epochsP; epochNow = class="num">0; ArrayResize(aT, popSize); class="kw">return true; } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_ASHA::Moving() { epochNow++; class=class="str">"cmt">//---------------------------------------------------------------------------- if (!revision) { for (class="type">int i = class="num">0; i < popSize; i++) { for (class="type">int c = class="num">0; c < coords; c++) { a [i].c [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]); a [i].c [c] = u.SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; class="kw">return; } class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">class="kw">double xOld = class="num">0.0; class="type">class="kw">double xNew = class="num">0.0; class="type">class="kw">double xLower = class="num">0.0; class="type">class="kw">double xLowest = class="num">0.0; class="type">class="kw">double ρ = MathMax(β * (epochs - epochNow) / epochs, ρ0); class="type">class="kw">double inf = class="num">0.0;
变异与精英留存的两段式实现
这段逻辑分两块:前半段在种群迭代里做坐标扰动,后半段 Revision() 负责把更优解固化下来。外汇与贵金属市场高波动,这类自适应搜索只解决参数空间探索,不承诺任何收益。 先看变异循环。inf 由 u.Scale 把个体停滞计数 cnt 映射到 [0,1],再四次方放大差异;rnd 是均匀随机概率。对每个坐标 c,若 rnd<inf 则按高斯分布重采样,否则以概率 ρ 参考随机旧个体、或以 1-ρ 参考全局最优 cB 做差分偏移,最后用 SeInDiSp 把值夹回步长网格。 Revision() 逐个体比较适应度 f 与历史最优 fB:若刷新则拷贝坐标到 cB 并清零 cnt,否则 cnt 自增。循环结束若发现新全局最优 ind,就把该个体坐标写回 cB;最后 u.Sorting_fB 按 fB 降序重排种群,保证下一代从好的基线出发。 在 MT5 里把 ρ 和 F 调小(如 ρ=0.1、F=0.5)会明显降低扰动幅度,适合点差较大的 XAUUSD 时段;调大则探索更激进,回测中易在震荡市过拟合。
class="type">int ind = class="num">0; class="type">class="kw">double rnd = class="num">0.0; for (class="type">int i = class="num">0; i < popSize; i++) { inf = u.Scale(a [i].cnt, class="num">0, δ, class="num">0, class="num">1); inf = inf * inf * inf * inf; rnd = u.RNDprobab(); for (class="type">int c = class="num">0; c < coords; c++) { ind = (class="type">int)u.RNDintInRange(class="num">0, i - class="num">1); if (i < class="num">1) { if (rnd < inf) { a [i].c [c] = u.GaussDistribution(cB [c], rangeMin [c], rangeMax [c], class="num">8); } } else { if (rnd < inf) { a [i].c [c] = u.GaussDistribution(a [ind].cB [c], rangeMin [c], rangeMax [c], class="num">8); } else { xOld = a [i].c [c]; if (u.RNDprobab() < ρ) { xLower = a [ind].cB [c]; xNew = xOld + F * (u.RNDprobab() * (xLower - xOld)); } else { xLowest = cB [c]; xNew = xOld + F * (u.RNDprobab() * (xLowest - xOld)); } a [i].c [c] = xNew; } } a [i].c [c] = u.SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_ASHA::Revision() { class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">int ind = -class="num">1; for (class="type">int i = class="num">0; i < popSize; i++) { if (a [i].f > fB) { fB = a [i].f; ind = i; } if (a [i].f > a [i].fB) { a [i].fB = a [i].f; ArrayCopy(a [i].cB, a [i].c, class="num">0, class="num">0, WHOLE_ARRAY); a [i].cnt = class="num">0; } else { a [i].cnt++; } } if (ind != -class="num">1) ArrayCopy(cB, a [ind].c, class="num">0, class="num">0, WHOLE_ARRAY); class=class="str">"cmt">//---------------------------------------------------------------------------- u.Sorting_fB(a, aT, popSize); }
「ASHA 跑分实测与排名落点」
把 ASHA(人工喷淋算法)塞进 Hilly、Forest、Megacity 三组测试函数里各跑 10000 次,维度梯度取 5 / 25 / 500。Hilly 上得分 0.897(5维)、0.404(25维)、0.256(500维);Forest 对应 0.804、0.355、0.192;Megacity 离散场景为 0.477、0.181、0.098。综合 All score 3.664,折算百分比 40.71%。 横向拉一张 32 个优化器的评级表,ASHA 排第 28 位,仅略优于 ASBO(40.63%)、MEC(38.55%)、IWO(37.81%)、Micro-AIS(37.54%)。榜首 ANS 跨邻里搜索拿到 68.15%,差距接近 28 个百分点。 观察 ASHA 的搜索轨迹,很难识别出它对某些区域有偏向性聚焦——没有发现对搜索空间中有前景领域的孤立深挖。这意味着在 MT5 里拿它做参数寻优,可能对平坦或多峰面容易过早摊平,外汇与贵金属 EA 调参属高风险操作,实盘前建议用历史数据多跑几轮验证收敛倾向。
◍ 优化器尾部梯队的实际表现
把排名 33 到 45 的优化器单独拉出来看,结论和头部完全不同。COAm 布谷鸟优化 M 的综合得分还有 3.349、收敛率 37.21%,但到了 PSO 粒子群只剩 2.230 和 24.77%,Boids 算法更是 2.229 与 24.77%,几乎贴地。 这组数字说明:在 MT5 参数寻优任务里,传统仿生类优化器(萤火虫、细菌觅食、人工蜂群、蝙蝠)普遍掉到 2.4–3.1 分区间,重力搜索和模拟退火也未能超过 2.3。若你正用 PSO 跑 EA 调参,大概率在浪费算力。 外汇与贵金属市场高波动、高杠杆,这类弱收敛优化结果直接上实盘可能放大过拟合风险。建议开 MT5 的优化报告,核对你自己 EA 的优化器得分是否落在这段区间,若是就换算法重跑。
最后说句实在话
ASHA 这套群体优化思路胜在快、代码轻,但实测收敛精度偏低,评分表满分为 100 时多数跑分落在中下段,仅少数组合能蹭到 0.99 以上的白色高亮区。它附带的 ASHA.zip(35.93 KB)里是改过几处函数的当前版 MQL5 实现,作者明确不对规范描述的绝对准确负责,结论只基于实验。 对外汇与贵金属这类高波动品种做参数寻优时,低精度收敛意味着过拟合风险偏高,拿去跑 EA 调参前最好先在小样本上复算评级脚本。 算法本身简单到有想象空间,渗透比怎么解释全看研究者自己,这既是漏洞也是入口。下一篇若换更稳的群体算法,对比跑分才有意义。