种群优化算法:树苗播种和成长(SSG)算法·进阶篇
◍ 遗传规划森林的初始播种逻辑
这段构造函数把一套类遗传算法的「森林」容器在 MT5 里先搭起来:用 GetTickCount 做随机种子,避免回测与实盘每次跑出同一串伪随机。seedlingsReplacement 参数若给到 ≥1.0 会直接等于树木总数,≤0.0 则强制为 1,中间值按 numberTreesP * 比例 取整,这决定了每轮迭代保留多少新苗。 数组维度全部按 coordinates(变量维度)和 numberTrees(树木数量)用 ArrayResize 展开,包括 pGarden / cGarden / gardenT 三类树群矩阵,以及每棵树内部的 c 坐标数组,初始适应度统一压到 -DBL_MAX。 首播标志 sowing 为 false 时进入第一次 planting:每棵树每个坐标先 RNDfromCI 在 [rangeMin, rangeMax] 内撒点,再过一道 SeInDiSp 按 rangeStep 离散化——这意味着你的搜索粒度直接受 rangeStep 控制,Step 设粗了可能跳过最优参数区。 播种后算一次欧氏半径 r = sqrt(Σ(vec[c]^2)),vec[c] 是各维上下界差,这个 r 后续会作为树木间距离度量的基准。外汇与贵金属 EA 里套这套,参数空间设宽了计算量指数涨,属高算力占用高风险玩法,实盘前务必在策略测试器跑样本。
class="kw">const class="type">class="kw">double seedlingsReplacementP, class=class="str">"cmt">//Seedlings replacement class="kw">const class="type">class="kw">double probabMatingOperatorP, class=class="str">"cmt">//Probability mating class="kw">operator class="kw">const class="type">class="kw">double probabBranchOperatorP, class=class="str">"cmt">//Probability branching class="kw">operator class="kw">const class="type">class="kw">double powerBranchOperatorP) class=class="str">"cmt">//Power branching class="kw">operator { MathSrand(GetTickCount()); sowing = class="kw">false; fB = -DBL_MAX; coordinates = coordinatesP; numberTrees = numberTreesP; if (seedlingsReplacementP >= class="num">1.0) { seedlingsReplacement = numberTreesP; } else { if (seedlingsReplacementP <= class="num">0.0) { seedlingsReplacement = class="num">1; } else seedlingsReplacement = class="type">int(numberTreesP * seedlingsReplacementP); } probabMatingOperator = probabMatingOperatorP; probabBranchOperator = probabBranchOperatorP; powerBranchOperator = powerBranchOperatorP; ArrayResize(rangeMax, coordinates); ArrayResize(rangeMin, coordinates); ArrayResize(rangeStep, coordinates); ArrayResize(vec, coordinates); ArrayResize(cB, coordinates); ArrayResize(pGarden, numberTrees); ArrayResize(cGarden, numberTrees); ArrayResize(gardenT, numberTrees); ArrayResize(ind, numberTrees); ArrayResize(val, numberTrees); for (class="type">int i = class="num">0; i < numberTrees; i++) { ArrayResize(pGarden [i].c, coordinates); ArrayResize(cGarden [i].c, coordinates); ArrayResize(gardenT [i].c, coordinates); cGarden [i].f = -DBL_MAX; } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//the first planting of trees------------------------------------------------- if (!sowing) { fB = -DBL_MAX; r = class="num">0.0; for (class="type">int t = class="num">0; t < numberTrees; t++) { for (class="type">int c = class="num">0; c < coordinates; c++) { cGarden [t].c [c] = RNDfromCI(rangeMin [c], rangeMax [c]); cGarden [t].c [c] = SeInDiSp(cGarden [t].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } cGarden [t].f = -DBL_MAX; } for (class="type">int c = class="num">0; c < coordinates; c++) { vec [c] = rangeMax [c] - rangeMin [c]; r += vec [c] * vec [c]; } r = sqrt(r); class="kw">return; } class=class="str">"cmt">//tree growth----------------------------------------------------------------- class="type">int child, parent; class="type">class="kw">double rnd; class="type">class="kw">double ed; class=class="str">"cmt">//euclidean distance class="type">class="kw">double eM; class="type">class="kw">double u; class="type">class="kw">double temp; for (class="type">int t = class="num">0; t < numberTrees; t++) ed = class="num">0.0; rnd = RNDfromCI(class="num">0.0, numberTrees - class="num">1); child = (class="type">int)MathRound(rnd);
遗传树的配对与变异算子实现
这段逻辑是群体进化核心:先随机挑两棵树做父代与子代,越界就夹回 [0, numberTrees-1],若撞到同一棵则父代序号 +1 并回绕,保证交叉有意义。 交配算子先算两棵树在 coordinates 维上的欧氏距离 ed,再用 eM = 1 - ed/r 得到相似度,当随机数 ≤ eM 时,按 probabMatingOperator 概率把子代坐标替换为父代坐标,相似度越高越容易整维继承。 分支算子对每维以 probabBranchOperator 概率扰动:r1 取 ±1 决定方向,r2 在 1~20 间取倒数平方做衰减,偏移量 = vec[c] * powerBranchOperator * pow(r2,-2.0),最后用 SeInDiSp 夹回该维的 [rangeMin, rangeMax] 并按 rangeStep 离散化。 疫苗算子先算子代相对父代各维归一化偏差的均值 u,凡单维偏差 ≥ u 的直接回退成父代值,相当于把离群维度『接种』回父本,抑制无效发散。 非播种轮会把子代写回父群并排序,若冠军适应度 fB 被刷新就存进 cB;播种轮则只把排名前 seedlingsReplacement 棵子代替换父群末尾再排序,MT5 里调大 seedlingsReplacement 会明显加快种群换代节奏,但外汇与贵金属行情高风险,回测过拟合概率随之上升,需自己跑样本外验证。
if (child < class="num">0) child = class="num">0; if (child > numberTrees - class="num">1) child = numberTrees - class="num">1; rnd = RNDfromCI(class="num">0.0, numberTrees - class="num">1); parent = (class="type">int)MathRound(rnd); if (parent < class="num">0) parent = class="num">0; if (parent > numberTrees - class="num">1) parent = numberTrees - class="num">1; if (child == parent) parent++; if (parent > numberTrees - class="num">1) parent = class="num">0; ArrayCopy(cGarden [t].c, pGarden [child].c, class="num">0, class="num">0, WHOLE_ARRAY); class=class="str">"cmt">//mating class="kw">operator----------------------------------------------------------- for (class="type">int c = class="num">0; c < coordinates; c++) { temp = pGarden [child].c [c] - pGarden [parent].c [c]; ed += temp * temp; } ed = sqrt(ed); eM = class="num">1.0 - (ed / r); rnd = RNDfromCI(class="num">0.0, class="num">1.0); if (rnd <= eM) { for (class="type">int c = class="num">0; c < coordinates; c++) { rnd = RNDfromCI(class="num">0.0, class="num">1.0); if (rnd <= probabMatingOperator) cGarden [t].c [c] = pGarden [parent].c [c]; } } class=class="str">"cmt">//branching class="kw">operator-------------------------------------------------------- for (class="type">int c = class="num">0; c < coordinates; c++) { rnd = RNDfromCI(class="num">0.0, class="num">1.0); if (rnd < probabBranchOperator) { class="type">class="kw">double r1 = RNDfromCI(class="num">0.0, class="num">1.0); r1 = r1 > class="num">0.5 ? class="num">1.0 : -class="num">1.0; class="type">class="kw">double r2 = RNDfromCI(class="num">1.0, class="num">20.0); cGarden [t].c [c] = cGarden [t].c [c] + r1 * vec [c] * powerBranchOperator * pow(r2, -class="num">2.0); cGarden [t].c [c] = SeInDiSp(cGarden [t].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } class=class="str">"cmt">//vaccinating class="kw">operator------------------------------------------------------ u = class="num">0.0; for (class="type">int c = class="num">0; c < coordinates; c++) { u += fabs(cGarden [t].c [c] - pGarden [parent].c [c]) / vec [c]; } u /= coordinates; for (class="type">int c = class="num">0; c < coordinates; c++) { if (fabs(cGarden [t].c [c] - pGarden [parent].c [c]) / vec [c] >= u) { cGarden [t].c [c] = pGarden [parent].c [c]; } } if (!sowing) { for (class="type">int t = class="num">0; t < numberTrees; t++) pGarden [t] = cGarden [t]; Sorting(pGarden); if (pGarden [class="num">0].f > fB) { fB = pGarden [class="num">0].f; ArrayCopy(cB, pGarden [class="num">0].c, class="num">0, class="num">0, WHOLE_ARRAY); } sowing = true; class="kw">return; } class=class="str">"cmt">//planting some part from all child trees------------------------------------- Sorting(cGarden); if (cGarden [class="num">0].f > fB) { fB = cGarden [class="num">0].f; ArrayCopy(cB, cGarden [class="num">0].c, class="num">0, class="num">0, WHOLE_ARRAY); } for (class="type">int t = class="num">0; t < seedlingsReplacement; t++) pGarden [numberTrees - seedlingsReplacement + t] = cGarden [t]; Sorting(pGarden);
「SSG 在 M1 回测台上的真实得分」
在 GBPUSD 的 M1 周期上跑 SSG 测试台,日志里直接吐出了一组可复核的数字。单看 Rastrigin 函数:5 个实例跑 10000 次得 77.397,Score 0.959;25 个实例得 52.245,Score 0.647;500 个实例得 0.254,Score 0.143。Forest 与 Megacity 离散函数也按 5/25/500 梯度下滑,全函数累计 All score 5.09109。 这套算法的参数就五个数:C_AO_SSG:50;0.3;0.5;0.4;0.1。NumberTrees_P=50 是树木数量,实验里没动默认值;若提到 100,聚合结果可能更好,但给定花园尺寸的迭代次数被压缩,缩放能力倾向下降。SeedlingsReplacement_P=0.3 管幼苗回迁比例,ProbabMatingOperator_P=0.5 是交叉概率,ProbabBranchOperator_P=0.4 为分杈概率,PowerBranchOperator_P=0.1 是枝杈强度——调到 1.0 以上枝杈会顶到花园边界,0.0 则退化为纯组合工具。 横向拉表看,SSG 在 14 个对比算法里最终结果 100.000 排第一,HS 是 93.370。最夸张的是 Megacity 离散函数:SSG 的 10 参数档 Score 1.00000,HS 同档 1.00000 但 50/1000 档掉到 0.41537,SSG 比最接近对手 HS 的最终档高出近 60%。Forest 上 SSG 也比最佳竞争者 ACOm 高出近 30%。 有意思的是,SSG 不直接用适应度去改决策树,只排子花园和父花园的序。看测试函数动画,树木几乎没有位移形态,只有局部极值处「丛生」,但收敛品质和稳定重现性都偏高。外汇与贵金属市场高风险,这类优化器结论仅代表历史测试环境,实盘迁移需自行在 MT5 跑同款日志验证。
◍ 记住这一条就够了
SSG 这套算法不挑目标函数的可微性或连续性,也不依赖代理个体的适应度或全局最优信息,所以能直接塞进 MT5 优化器去跑那些乱七八糟的非线性问题。作者在实验里观察到,树苗播种成长在收敛品质、结果稳定性和可扩展性上目前没有明显对手。 它优点很直白:实现门槛低,各类函数都能收敛,规模一拉大也不虚;缺点是可调参数偏多,虽然每个都还算直观。外汇和贵金属市场高波动、高杠杆,拿它做参数寻优只是提高概率,不代表实盘能躲过黑天鹅。 随文给的 15_The_world_of_AO_SSG.zip(约 120 KB)里含历次文章代码的当前版,下完丢进 MT5 的 MQL5/Files 解压,就能把自己 EA 的优化方法换成 SSG 试一把。