群体优化算法:思维进化计算(MEC)算法·进阶篇
◍ 群体智能优化器的初始化与首轮播种
这段构造函数把思维进化算法(IDE)的种群骨架一次性搭起来:用微秒计数重置随机种子,避免回测里每次跑出同一串伪随机。种群按 ideasNumber 切成领袖组与随从组,leadIdGroupSize 取 ideasNumber/2,alteIdGroupSize 补上余数,ideasBr 则是 populationSize 除以 ideasNumber 得到的每思想分支个体数。 若 revision 为 false,说明是冷启动:fB 先压到 -DBL_MAX,按 thoughtPower 把搜索步长 vect[c] 设成各维区间跨度的比例。领袖组和随从组各自用 RNDfromCI 在 [rangeMin, rangeMax] 里撒点,再经 SeInDiSp 离散化到 rangeStep 网格,适应度统一预置 -DBL_MAX。 首轮播种后 revision 置 true,后续迭代里个体坐标会沿领袖维度做 coord = leadIdeolGroup[i].c[c] + r1 * vect[c] * pow(r2, -2.0) 的扰动——r2 的负二次方让小概率长跳变可能主导早期探索。外汇与贵金属参数空间里这套初始化直接决定收敛区,高杠杆下误设 rangeStep 可能让优化陷入局部极值。 开 MT5 把 populationSize、ideasNumber、thoughtPower 三个入参打进构造函数,断点看 leadIdGroupSize 是否如预期等于 ideasNumber/2,能省掉大半调参瞎猜。
const class="type">class="kw">double thoughtPowerP) class=class="str">"cmt">//thought power { MathSrand((class="type">int)GetMicrosecondCount()); class=class="str">"cmt">// reset of the generator fB = -DBL_MAX; revision = false; coordinatesNumber = coordinatesNumberP; populationSize = populationSizeP; ideasNumber = ideasNumberP; thoughtPower = thoughtPowerP; ideasBr = populationSize / ideasNumber; leadIdGroupSize = ideasNumber / class="num">2; alteIdGroupSize = ideasNumber - leadIdGroupSize; ArrayResize(rangeMax, coordinatesNumber); ArrayResize(rangeMin, coordinatesNumber); ArrayResize(rangeStep, coordinatesNumber); ArrayResize(vect, coordinatesNumber); ArrayResize(ind, alteIdGroupSize, alteIdGroupSize); ArrayResize(val, alteIdGroupSize, alteIdGroupSize); ArrayResize(tempIdeolGroup, alteIdGroupSize, alteIdGroupSize); ArrayResize(cB, coordinatesNumber); ArrayResize(leadIdeolGroup, leadIdGroupSize); for (class="type">int i = class="num">0; i < leadIdGroupSize; i++) leadIdeolGroup [i].Init(coordinatesNumber); ArrayResize(alteIdeolGroup, alteIdGroupSize); for (class="type">int i = class="num">0; i < alteIdGroupSize; i++) alteIdeolGroup [i].Init(coordinatesNumber); ArrayResize(idBr, populationSize); for (class="type">int i = class="num">0; i < populationSize; i++) idBr [i].Init(coordinatesNumber); } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//============================================================================ if (!revision) { fB = -DBL_MAX; class="type">int cnt = class="num">0; for (class="type">int c = class="num">0; c < coordinatesNumber; c++) vect [c] = (rangeMax [c] - rangeMin [c]) * thoughtPower; class=class="str">"cmt">//-------------------------------------------------------------------------- for (class="type">int i = class="num">0; i < leadIdGroupSize; i++) { for (class="type">int c = class="num">0; c < coordinatesNumber; c++) { coord = RNDfromCI(rangeMin [c], rangeMax [c]); leadIdeolGroup [i].c [c] = SeInDiSp(coord, rangeMin [c], rangeMax [c], rangeStep [c]); } leadIdeolGroup [i].f = -DBL_MAX; } class=class="str">"cmt">//-------------------------------------------------------------------------- for (class="type">int i = class="num">0; i < alteIdGroupSize; i++) { for (class="type">int c = class="num">0; c < coordinatesNumber; c++) { coord = RNDfromCI(rangeMin [c], rangeMax [c]); alteIdeolGroup [i].c [c] = SeInDiSp(coord, rangeMin [c], rangeMax [c], rangeStep [c]); } alteIdeolGroup [i].f = -DBL_MAX; } revision = true; } coord = leadIdeolGroup [i].c [c] + r1 * vect [c] * pow(r2, -class="num">2.0); r1 = RNDfromCI(class="num">0.0, leadIdGroupSize - class="num">1); idBr [cnt].c [c] = leadIdeolGroup [(class="type">int)r1].c [c]; class=class="str">"cmt">//---------------------------------------------------------------------------- for (class="type">int i = class="num">0; i < leadIdGroupSize; i++) { for (class="type">int b = class="num">0; b < ideasBr; b++) { if (b < ideasBr -class="num">1) {
「变异与择优的底层循环」
这段逻辑干了两件事:用随机扰动生成新解,再把更优的解回填到主导组和备选组。外汇与贵金属市场高波动,这类群体智能搜索对参数空间敏感,实盘前务必在 MT5 策略测试器跑多轮。 先看生成段:当走变异分支时,对每个坐标 c 取 [0,1] 均匀随机数 r1,大于 0.5 记为正向扰动 1.0 否则 -1.0;再取 [1,20] 的 r2,用 pow(r2,-2.0) 做长尾衰减,意味着绝大多数步长极小、偶发跳变。coord 在基准点上叠加扰动后,经 SeInDiSp 夹到 [rangeMin,rangeMax] 并按 rangeStep 离散化。 若走交叉分支,则直接随机挑一个主导组成员的坐标复制,等价于种群内的基因借用。alte 组的处理完全套用同一套变异公式,只是基准换成 alteIdeolGroup。 Revision 函数负责收割:遍历 leadIdGroupSize 个主导idea,若某条分支 idBr 的适应度 f 更高就记 pos,随后用 ArrayCopy 整体替换坐标数组;若刷新了全局最优 fB,同步存进 cB。备选组同理,保证每代只保留被击败者的更优替身。
for (class="type">int c = class="num">0; c < coordinatesNumber; 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); coord = leadIdeolGroup [i].c [c] + r1 * vect [c] * pow(r2, -class="num">2.0); idBr [cnt].c [c] = SeInDiSp(coord, rangeMin [c], rangeMax [c], rangeStep [c]); } } else { for (class="type">int c = class="num">0; c < coordinatesNumber; c++) { r1 = RNDfromCI(class="num">0.0, leadIdGroupSize - class="num">1); idBr [cnt].c [c] = leadIdeolGroup [(class="type">int)r1].c [c]; } } cnt++; } } class=class="str">"cmt">//---------------------------------------------------------------------------- for (class="type">int i = class="num">0; i < alteIdGroupSize; i++) { for (class="type">int b = class="num">0; b < ideasBr; b++) { for (class="type">int c = class="num">0; c < coordinatesNumber; 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); coord = alteIdeolGroup [i].c [c] + r1 * vect [c] * pow(r2, -class="num">2.0); idBr [cnt].c [c] = SeInDiSp(coord, rangeMin [c], rangeMax [c], rangeStep [c]); } cnt++; } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_MEC::Revision() { class="type">int cnt = class="num">0; class="type">int pos = -class="num">1; class=class="str">"cmt">//---------------------------------------------------------------------------- class=class="str">"cmt">//class="num">4. If there is a better ideological branch, replace the corresponding idea for (class="type">int i = class="num">0; i < leadIdGroupSize; i++) { pos = -class="num">1; for (class="type">int b = class="num">0; b < ideasBr; b++) { if (idBr [cnt].f > leadIdeolGroup [i].f) pos = cnt; cnt++; } if (pos != -class="num">1) { leadIdeolGroup [i].f = idBr [pos].f; ArrayCopy(leadIdeolGroup [i].c, idBr [pos].c, class="num">0, class="num">0, WHOLE_ARRAY); if (idBr [pos].f > fB) { fB = idBr [pos].f; ArrayCopy(cB, idBr [pos].c, class="num">0, class="num">0, WHOLE_ARRAY); } } } class=class="str">"cmt">//---------------------------------------------------------------------------- for (class="type">int i = class="num">0; i < alteIdGroupSize; i++) { pos = -class="num">1; for (class="type">int b = class="num">0; b < ideasBr; b++) { if (idBr [cnt].f > alteIdeolGroup [i].f) pos = cnt; cnt++; } if (pos != -class="num">1) { alteIdeolGroup [i].f = idBr [pos].f;
用主导群元素再生空想法的实现细节
这段逻辑处在群体智能优化循环的后半段:先按适应度把两个想法群排好序,再把第一群最差的那一个直接换成第二群里排第一的优解。leadIdeolGroup[leadIdGroupSize-1] = alteIdeolGroup[0] 这一行没有任何中间判断,属于硬替换,回测时若第二群首元素适应度波动大,第一群底线会被快速拉高也可能被噪声带偏。 随后用一个空出来的想法位,基于主导群已有元素随机重组出新想法。循环对每个坐标 c 调用 RNDfromCI(0.0, leadIdGroupSize-2) 取随机索引,把主导群里某个成员的同维度坐标抄给 alteIdeolGroup[0],相当于在主导群内部做随机交叉。coordinatesNumber 决定新想法的维度,leadIdGroupSize-2 意味着随机源排除自身最差位,避免退化。 在 MT5 里把这段代码接进自己的 EA 优化器时,重点看 RNDfromCI 的均匀分布质量;若它返回整数边界概率偏低,新想法的维度采样会偏向中间索引,群体多样性可能收敛过快。外汇与贵金属市场高风险,这类元启发式仅用于参数搜索辅助,不预示任何收益。
ArrayCopy(alteIdeolGroup [i].c, idBr [pos].c, class="num">0, class="num">0, WHOLE_ARRAY); if (idBr [pos].f > fB) { fB = idBr [pos].f; ArrayCopy(cB, idBr [pos].c, class="num">0, class="num">0, WHOLE_ARRAY); } } } class=class="str">"cmt">//---------------------------------------------------------------------------- class=class="str">"cmt">//class="num">5. Sort two groups of ideas. Sorting(leadIdeolGroup); Sorting(alteIdeolGroup); class=class="str">"cmt">//---------------------------------------------------------------------------- class=class="str">"cmt">//class="num">6. Replace the worst idea of the first group with the best idea from the second group leadIdeolGroup [leadIdGroupSize - class="num">1] = alteIdeolGroup [class="num">0]; class=class="str">"cmt">//---------------------------------------------------------------------------- class=class="str">"cmt">//class="num">7. Instead of an empty idea, create a new idea based on elements of the dominant group class="type">class="kw">double rnd = class="num">0.0; class="type">class="kw">double coord = class="num">0.0; for (class="type">int c = class="num">0; c < coordinatesNumber; c++) { rnd = RNDfromCI(class="num">0.0, leadIdGroupSize - class="num">2); alteIdeolGroup [class="num">0].c [c] = leadIdeolGroup [(class="type">int)rnd].c [c]; } }
◍ MEC 在三类测试函数上的实测表现
把思维进化计算(MEC)跑在测试台上,参数设为 C_AO_MEC:50;10;0.3,每组函数迭代 10000 次。Rastrigin 函数 5/25/500 维的结果分别是 78.73 / 58.55 / 42.32,综合评分 0.97553 / 0.72553 / 0.52442;Forest 函数同维度结果是 1.20 / 0.46 / 0.09,评分 0.68 / 0.26 / 0.05;Megacity 离散函数则是 5.28 / 2.05 / 0.44,评分 0.44 / 0.17 / 0.036。 观察 MEC 的代理集群动画,能明显看到个体向局部极值聚拢,这种趋同质量让它在排行里拿到了有价值的位置。横向比 18 种算法,MEC 总成绩 56.227 排第 5,落后于 SSG、HS、ACOm、IWO,但高于 COAm、FAm 等常见群智能算法。 拆开维度看,MEC 在 Rastrigin 5 维拿 0.99270、Megacity 5 维拿 0.93335,基本逼近满分;但到了 500 维(1000 参数)时,Rastrigin 掉到 0.22800、Forest 仅 0.25459。说明它在低维优化上效率更高,参数规模一大优势就缩水,这点在实盘调参时要留神。 外汇与贵金属市场高波动、高杠杆,任何优化算法给出的参数组合都只是概率倾向,不能直接当信号用。开 MT5 把上面三组函数维度和迭代次数复一遍,看你的环境是否复现 10000 次下的评分偏差。
「一点提醒」
MEC 在连续与离散混合空间里只带最小数量的外部参数,计算负载低,适合在 MT5 里做EA参数寻优;但原文实测显示它在水平“平板”类函数上容易卡住,这种局部极值倾向在离散和锯齿函数上更明显。 归档里附了 18_The_world_of_AO_MEC.zip(407.73 KB),含可跑的评级脚本,你下完能在 MT5 用文中方法复算各算法 0–100 的直方图评分。外汇与贵金属杠杆高、回测过拟合风险大,任何优化结果都只是概率倾向,上线前先跑样本外验证。