人工协作搜索算法 (ACS)·进阶篇
「ACS算法里的种群与相位初始化」
这段实现把 ACS(人工猫群)优化器的内存布局先铺开:A、B、Predator、Prey、M 五个结构数组都按 popSize 扩容,后续逐个体调 Init(coords) 把坐标维度撑起来。注意 YA/YB/Ypred 三个适应度数组用 ArrayInitialize 填了 -DBL_MAX,等于先给「还没算过」标个底,避免脏数据混进选优。 初始化坐标时只给 A 和 B 填了随机值:每个维度 j 在 [rangeMin[j], rangeMax[j]] 内乘 u.RNDprobab() 撒点,Prey 和 Predator 此时还没动,留到 Moving() 里再拷贝。phase 置 0 后返回 true,代表种群冷启动完成。 Moving() 用 phase 当状态机:phase 0 把 A 拷进工作数组 a 并自增;phase 1 把 a 的适应度存 YA、再换 B 进 a;phase 2 存 YB。之后以 50% 概率(u.RNDprobab() < 0.5)选 A 或 B 的群体当 Predator,Key 记 1 或 2——这套切换直接决定下一轮捕食者朝哪边追。外汇与贵金属参数寻优用这类群体算法波动大,实盘前务必在 MT5 策略测试器跑多 seed 验证。
ArrayResize(A, popSize); ArrayResize(B, popSize); ArrayResize(Predator, popSize); ArrayResize(Prey, popSize); ArrayResize(M, popSize); for (class="type">int i = class="num">0; i < popSize; i++) { A [i].Init(coords); B [i].Init(coords); Predator[i].Init(coords); Prey [i].Init(coords); M [i].Init(coords); } ArrayResize(YA, popSize); ArrayResize(YB, popSize); ArrayResize(Ypred, popSize); ArrayInitialize(YA, -DBL_MAX); ArrayInitialize(YB, -DBL_MAX); ArrayInitialize(Ypred, -DBL_MAX); class=class="str">"cmt">// Initialization for (class="type">int i = class="num">0; i < popSize; i++) { for (class="type">int j = class="num">0; j < coords; j++) { A [i].c [j] = rangeMin [j] + (rangeMax [j] - rangeMin [j]) * u.RNDprobab(); B [i].c [j] = rangeMin [j] + (rangeMax [j] - rangeMin [j]) * u.RNDprobab(); } } phase = class="num">0; class="kw">return true; } class="type">void C_AO_ACS::Moving() { if (phase == class="num">0) { for (class="type">int i = class="num">0; i < popSize; i++) ArrayCopy(a [i].c, A [i].c); phase++; class="kw">return; } if (phase == class="num">1) { for (class="type">int i = class="num">0; i < popSize; i++) YA [i] = a [i].f; for (class="type">int i = class="num">0; i < popSize; i++) ArrayCopy(a [i].c, B [i].c); phase++; class="kw">return; } if (phase == class="num">2) { for (class="type">int i = class="num">0; i < popSize; i++) YB [i] = a [i].f; phase++; } class=class="str">"cmt">// Selection if (u.RNDprobab() < class="num">0.5) { for (class="type">int i = class="num">0; i < popSize; i++) { ArrayCopy(Predator [i].c, A [i].c); } ArrayCopy(Ypred, YA); Key = class="num">1; } else { for (class="type">int i = class="num">0; i < popSize; i++) { ArrayCopy(Predator [i].c, B [i].c); } ArrayCopy(Ypred, YB); Key = class="num">2; } if (u.RNDprobab() < class="num">0.5) {
捕食者-猎物混合算子的矩阵实现
这段逻辑处在群体智能寻优的内层迭代里:先按条件把 A 或 B 群体复制进 Prey,再对每只猎物做 ArrayShuffle 打乱坐标顺序,避免后续交叉陷入固定维度配对。 随机系数 R 的取法分两路:当均匀分布随机数小于 0.5 时,R = 4 * rand * CI(-1,1),否则走 R = 1 / exp(4 * MathRand()/32767),后者把整数随机量归一进 (0,1] 区间,两种路径都会显著影响变异步长。 二进制矩阵 M 先全置 1,再以 bioProbab 概率翻 0 或重写 1;若某行 sum 等于 coords(全 1),随机挑一列置 0,保证每行至少留一个交叉缺口。 真正落点的循环里,a[i].c[j] = Predator[j] + R*(Prey[j]-Predator[j]) 做连续变异;当 M 对应位 >0 时回退成 Predator 原值(交叉),越界则重投到 [rangeMin,rangeMax] 内。开 MT5 把 bioProbab 从 0.1 调到 0.4,能直观看到 M 稀疏度变化与收敛轨迹抖动。
for (class="type">int i = class="num">0; i < popSize; i++) { ArrayCopy(Prey [i].c, A [i].c); } } else { for (class="type">int i = class="num">0; i < popSize; i++) { ArrayCopy(Prey [i].c, B [i].c); } } class=class="str">"cmt">// Permutation of Prey for (class="type">int i = class="num">0; i < popSize; i++) { ArrayShuffle(Prey [i].c); } class="type">class="kw">double R; if (u.RNDprobab() < class="num">0.5) { R = class="num">4 * u.RNDprobab() * u.RNDfromCI(-class="num">1.0, class="num">1.0); } else R = class="num">1 / MathExp(class="num">4 * MathRand() / class="num">32767.0); class=class="str">"cmt">// Fill binary matrix M with 1s for (class="type">int i = class="num">0; i < popSize; i++) { for (class="type">int j = class="num">0; j < coords; j++) { M [i].c [j] = class="num">1; } } class=class="str">"cmt">// Additional operations with matrix M for (class="type">int i = class="num">0; i < popSize; i++) { for (class="type">int j = class="num">0; j < coords; j++) { if (u.RNDprobab() < bioProbab) { M [i].c [j] = class="num">0; } } } for (class="type">int i = class="num">0; i < popSize; i++) { for (class="type">int j = class="num">0; j < coords; j++) { if (u.RNDprobab() < bioProbab) { M [i].c [j] = class="num">1; } else { M [i].c [j] = class="num">0; } } } for (class="type">int i = class="num">0; i < popSize; i++) { class="type">int sum = class="num">0; for (class="type">int c = class="num">0; c < coords; c++) sum += M [i].c [c]; if (sum == coords) { class="type">int j = MathRand() % coords; M [i].c [j] = class="num">0; } } class=class="str">"cmt">// Mutation for (class="type">int i = class="num">0; i < popSize; i++) { for (class="type">int j = class="num">0; j < coords; j++) { a [i].c [j] = Predator [i].c [j] + R * (Prey [i].c [j] - Predator [i].c [j]); class=class="str">"cmt">// Crossover if (M [i].c [j] > class="num">0) { a [i].c [j] = Predator [i].c [j]; } class=class="str">"cmt">// Boundary control if (a [i].c [j] < rangeMin [j] || a [i].c [j] > rangeMax [j]) { a [i].c [j] = rangeMin [j] + (rangeMax [j] - rangeMin [j]) * u.RNDprobab(); } } } for (class="type">int i = class="num">0; i < popSize; i++) { for (class="type">int j = class="num">0; j < coords; j++) {
◍ 进化迭代里的捕食者留存与乱序
Revision 函数在 phase 小于 3 时直接 return,意味着前两个阶段不更新选择结果,这是为了避免初始种群未稳定就过早固化最优解。 遍历种群时,若当前个体适应度 a[i].f 大于历史预测值 Ypred[i],就把该个体的参数数组拷贝进 Predator[i].c 并更新 Ypred[i],相当于只保留变得更优的捕食者。 Key 等于 1 时把 Predator 拷给 A 系并记录 YA,否则拷给 B 系记录 YB,两套记忆池交替承载搜索轨迹。随后对 Ypred 排序并反转,取 Ypred[0] 为本次最优,若优于全局 fB 则回写 cB 与对应个体。 ArrayShuffle 用 MathRand 做从尾到头的 Fisher–Yates 交换,ArraySize 拿到长度后每层随机抽一个前置下标互换,给后续交叉提供无序性。外汇与贵金属杠杆高,这类参数搜索仅作策略原型验证,实盘前须在 MT5 用历史数据回测观察过拟合倾向。
a [i].c [j] = u.SeInDiSp(a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]); } } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_ACS::Revision() { if (phase < class="num">3) class="kw">return; class=class="str">"cmt">// Selection update for (class="type">int i = class="num">0; i < popSize; i++) { class="type">class="kw">double d = a [i].f; if (d > Ypred [i]) { ArrayCopy(Predator [i].c, a [i].c); Ypred [i] = d; } } if (Key == class="num">1) { for (class="type">int i = class="num">0; i < popSize; i++) { ArrayCopy(A [i].c, Predator [i].c); } ArrayCopy(YA, Ypred); } else { for (class="type">int i = class="num">0; i < popSize; i++) { ArrayCopy(B [i].c, Predator [i].c); } ArrayCopy(YB, Ypred); } ArraySort(Ypred); ArrayReverse(Ypred, class="num">0, WHOLE_ARRAY); class="type">class="kw">double Ybest = Ypred [class="num">0]; class="type">int Ibest = ArrayMaximum(Ypred); if (Ybest > fB) { fB = Ybest; ArrayCopy(a [Ibest].c, Predator [Ibest].c); ArrayCopy(cB, Predator [Ibest].c); } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_ACS::ArrayShuffle(class="type">class="kw">double &arr []) { class="type">int n = ArraySize(arr); for (class="type">int i = n - class="num">1; i > class="num">0; i--) { class="type">int j = MathRand() % (i + class="num">1); class="type">class="kw">double tmp = arr [i]; arr [i] = arr [j]; arr [j] = tmp; } } class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
「极小种群下反而更稳的协同搜索」
人工协同搜索(ACS)最反直觉的一点是:种群缩到极小,综合得分不降反升。在适应度函数各跑 10,000 次的设定下,种群 10 个个体时 All score 为 4.49701(49.97%),降到 3 个个体变成 4.97110(55.23%),仅留 1 个个体时进一步到 5.22555(58.06%)。 这背后的机制是算法内部用了五个子种群,个体间跨群交互维持了多样性,没有出现“瓶颈”效应。可视化里看不到聚类,智能体在平坦区(如 Forest、Megacity 的大片低变区)也保持混乱游走,避开了局部陷阱。 横向排位上,ACS 在 23 个对比算法里列第 8,总得分 5.226、折算 58.06%。细分看,Forest 函数上 5/25/500 规模得分分别 1.00000 / 0.88861 / 0.22413,是它最亮眼的项;Megacity 离散函数得分偏弱,500 规模仅 0.13322。外汇或贵金属模型若引用此类优化器做参数寻优,需注意极小种群可能倾向过拟合平坦目标面,实盘属高风险验证。 想自己复现,可直接把下面这段日志结构当校验基准:种群=1、协作系数=0.9、单函数 10,000 次时,Hilly 三项应为 0.75547 / 0.74744 / 0.30407。
ACS|Artificial Cooperative Search|class="num">1.0|class="num">0.9| ============================= class="num">5 Hilly&class="macro">#x27;s; Func runs: class="num">10000; result: class="num">0.7554725186591347 class="num">25 Hilly&class="macro">#x27;s; Func runs: class="num">10000; result: class="num">0.7474431551529281 class="num">500 Hilly&class="macro">#x27;s; Func runs: class="num">10000; result: class="num">0.3040669213089683 ============================= class="num">5 Forest&class="macro">#x27;s; Func runs: class="num">10000; result: class="num">0.9999999999993218 class="num">25 Forest&class="macro">#x27;s; Func runs: class="num">10000; result: class="num">0.888607840003743 class="num">500 Forest&class="macro">#x27;s; Func runs: class="num">10000; result: class="num">0.2241289484506152 ============================= class="num">5 Megacity&class="macro">#x27;s; Func runs: class="num">10000; result: class="num">0.6907692307692308 class="num">25 Megacity&class="macro">#x27;s; Func runs: class="num">10000; result: class="num">0.4818461538461539 class="num">500 Megacity&class="macro">#x27;s; Func runs: class="num">10000; result: class="num">0.1332153846153859 ============================= All score: class="num">5.22555 (class="num">58.06%)
优化器横向跑分:从萤火虫到类电磁
上面这张表把 24 到 40 号共 17 种参数优化算法在同一测试环境下的原始指标摊开了。NMm(Nelder-Mead)和 FAm(萤火虫算法)标注了 M,表示其某列结果带有约束修正,其余未标 M 的算法按无约束处理。 看综合加权值(倒数第二列),NMm 拿到 3.233 最高,FAm 3.048 次之,GSA 2.911 排第三;而 EM(类电磁算法)只有 1.786,CSS(电荷系统搜索)1.842,落在末端。换算成百分制(末列),NMm 35.92、FAm 33.87,EM 仅 19.85。 对外汇与贵金属 EA 调参来说,这组数字说明:传统直接搜索类(NMm)和群智能类(FAm、GSA)在收敛质量上可能明显优于末段算法。但外汇/贵金属市场高杠杆、高波动,回测优势不等于实盘胜率,切换优化器前建议在 MT5 策略测试器里用同一品种周期复跑验证。 标 M 的算法在部分子项(如第 4、8 列)数值偏高,可能是约束处理把边界解也算了进来,实盘若遇报价跳空,这类解可能直接失效。
◍ 记住这一条就够了
ACS 把五个单一个体种群丢进互相洗牌与二进制变异的回路里,实测在种群总规模仅 5 的情况下,颜色渐变图里仍有结果 ≥0.99 的白色高亮区,说明小种群抗退化不是嘴上说说。 它外部参数只有一个,跨类型函数收敛表现稳,但在低维函数上离散度偏大,跑之前得先想清楚维度适配。 真要上手,去 GitHub 拉 Population-optimization-algorithms-MQL5 的 ACS.zip(26.11 KB),MT5 里挂上作者给的评级脚本,自己跑一遍直方图,比看任何结论都实在。