种群优化算法:微人工免疫系统(Micro-AIS)·进阶篇
「克隆种群的等差数列切分」
克隆算法里,总种群规模 popSize 不是平均分给每一代克隆体,而是按等差数列分配:首项 minClonesNumber、公差 cloneStep,逐代递增克隆数,直到累加和逼近总规模。 下面这段构造函数把参数落进类成员,并用 MathSrand 以微秒计数重置随机种子,避免每次回测得到同一组伪随机序列。 核心循环用求和公式 Ssum = n*(a1+an)/2 反推需要多少代(n)才能吃完 popSize。若某一代累加刚好相等就收尾;不够就继续扩数组;超了则回退一代,把剩余差额 diff 塞进已有代数的末尾。 在 MT5 里把 minClonesNumber 设 5、cloneStep 设 3、popSize 设 50,循环会在第 5 代左右停下,最后一代往往吃掉余数而非严格等差——这是调参时容易忽略的 truncation 效应。外汇与贵金属策略测试请认清高风险,差分分配只影响搜索广度,不预示任何收益。
const class="type">int minClonesNumberP, class=class="str">"cmt">//minimum number of clones const class="type">int cloneStepP, class=class="str">"cmt">//clone step const class="type">class="kw">double mutationP, class=class="str">"cmt">//mutation const class="type">int epochP) class=class="str">"cmt">//total epochs { MathSrand((class="type">int)GetMicrosecondCount()); class=class="str">"cmt">// reset of the generator fB = -DBL_MAX; revision = class="kw">false; coords = coordsP; popSize = popSizeP; minClonesNumber = minClonesNumberP; cloneStep = cloneStepP; mutation = mutationP; epochs = epochP; epochsCNT = class="num">1; class=class="str">"cmt">//---------------------------------------------------------------------------- class="type">int Sn = popSize; class=class="str">"cmt">//sum class="type">int a1 = minClonesNumber; class=class="str">"cmt">//first member of progression class="type">int d = cloneStep; class=class="str">"cmt">//progression difference class="type">int an = class="num">0; class=class="str">"cmt">//n th member of progression, class="type">int Ssum = class="num">0; ArrayResize(cCnt, class="num">1); for (class="type">int n = class="num">1;; n++) { an = a1 + (n - class="num">1) * d; Ssum = n * (a1 + an) / class="num">2; if (Ssum == Sn) { ArrayResize(cCnt, n); cCnt [n - class="num">1] = an; break; } else { if (Ssum < Sn) { ArrayResize(cCnt, n); cCnt [n - class="num">1] = an; } else { if (n == class="num">1) { ArrayResize(cCnt, n); cCnt [n - class="num">1] = Sn; break; } else { n--; an = a1 + (n - class="num">1) * d; class="type">int diff = Sn - ((n) * (a1 + an) / class="num">2); class="type">int index = ArraySize(cCnt) - class="num">1; class="kw">while (true) { if (index < class="num">0) index = ArraySize(cCnt) - class="num">1;
变异步长随迭代衰减的实现细节
这段逻辑来自一个基于种群的优化类,核心在 Moving() 里对父代个体做扰动生成子代。关键不是随机乱跳,而是用剩余迭代比例 k 来收缩步长:k = (epochs - epochsCNT) / epochs,越接近训练尾段,扰动幅度越小,倾向让参数在局部收敛。 具体对每个坐标 c,先按 rnd 正负决定向区间上界或下界取距离 dist,再乘 rnd * k * mutation。mutation 是外部传入的变异强度,若你在 MT5 里跑同类 EA,把 mutation 从 0.1 调到 0.3 可能让早期探索更激进,但尾段仍受 k 压制。 注意 SeInDiSp() 每次都把新坐标吸附回离散网格(rangeMin/Max/Step 定义),这意味着连续空间被量化。若 rangeStep 设得过大,k 的衰减可能看不出平滑效果,参数会呈现台阶式跳动。外汇与贵金属市场高风险,此类优化仅用于历史拟合验证,实盘概率优势不保证。 初始化段也值得看:ArrayReverse(cCnt) 把父代计数倒序,使优先生出的父代排在后面参与分配;ind/pVal/pTemp 等数组按 popSize + parentsNumb 重设尺寸,说明子代池是父代与原始种群的叠加。开 MT5 把这段贴进同类结构,改 parentsNumb 观察内存重分配是否触发数组越界报错。
cCnt [index]++; index--; diff--; if (diff <= class="num">0) break; } break; } } } parentsNumb = ArraySize(cCnt); ArrayReverse(cCnt, class="num">0, WHOLE_ARRAY); ArrayResize(ind, popSize + parentsNumb); ArrayResize(val, popSize + parentsNumb); ArrayResize(pTemp, popSize + parentsNumb); ArrayResize(a, popSize); for (class="type">int i = class="num">0; i < popSize; i++) a [i].Init(coords); ArrayResize(parents, popSize + parentsNumb); for (class="type">int i = class="num">0; i < popSize + parentsNumb; i++) parents [i].Init(coords); ArrayResize(rangeMax, coords); ArrayResize(rangeMin, coords); ArrayResize(rangeStep, coords); ArrayResize(cB, coords); } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_Micro_AIS::Moving() { 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] = RNDfromCI(rangeMin [c], rangeMax [c]); a [i].c [c] = SeInDiSp(a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; class="kw">return; } class="type">int indx = class="num">0; class="type">class="kw">double min = DBL_MAX; class="type">class="kw">double max = -DBL_MAX; class="type">class="kw">double dist = class="num">0.0; class="type">int cnt = class="num">0; class="type">class="kw">double rnd = class="num">0.0; for (class="type">int i = class="num">0; i < parentsNumb; i++) { for (class="type">int cl = class="num">0; cl < cCnt [i]; cl++) { for (class="type">int c = class="num">0; c < coords; c++) { a [indx].c [c] = parents [i].c [c]; class="type">class="kw">double k = ((class="type">class="kw">double)epochs - (class="type">class="kw">double)epochsCNT) / (class="type">class="kw">double)epochs; rnd = RNDfromCI(-class="num">1.0, class="num">1.0); if (rnd > class="num">0.0) dist = (rangeMax [c] - a [indx].c [c]); else dist = (a [indx].c [c] - rangeMin [c]); a [indx].c [c] = a [indx].c [c] + dist * rnd * k * mutation; a [indx].c [c] = SeInDiSp(a [indx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } indx++; } } }
◍ 精英保留与种群更替的实现细节
在 AO 微结构智能系统的迭代函数里,Revision 负责把当前代里最差的个体挑出来,并用历史最优解覆盖它。代码先扫一遍 popSize 规模的种群,只要某个个体的适应度 a[i].f 超过记录值 fB,就记下下标 indx,随后用 ArrayCopy 把该个体的参数数组 c 整体搬进 cB,保证跨代不丢失最优解。 后半段把当前代 a 整体平移塞进 parents 缓冲区的 parentsNumb 偏移之后,再对总长度 parentsNumb+popSize 做 Sorting。这意味着每一代结束后,前 parentsNumb 个老父代和整批新子代混合并重排,epochsCNT 自增以追踪进化轮次。 在 MT5 里把这段直接挂到自己的 EA 遗传模块中,把 popSize 设成 50、parentsNumb 设成 10 跑 EURUSD 的 M15 回测,能直观看到 fB 通常在第 12~18 个 epoch 后收敛。外汇与贵金属波动剧烈,这类自适应参数寻优仅降低过拟合概率,实盘仍属高风险。
class="type">void C_AO_Micro_AIS::Revision() { class="type">int indx = -class="num">1; for (class="type">int i = class="num">0; i < popSize; i++) { if (a [i].f > fB) indx = i; } if (indx != -class="num">1) { fB = a [indx].f; ArrayCopy(cB, a [indx].c, class="num">0, class="num">0, WHOLE_ARRAY); } for (class="type">int i = parentsNumb; i < parentsNumb + popSize; i++) { parents [i] = a [i - parentsNumb]; } Sorting(parents, parentsNumb + popSize); epochsCNT++; }
「Micro-AIS 跑分与收敛曲线异象」
在 MT5 策略测试台对 Micro-AIS 做 10000 次函数求值,参数串 C_AO_Micro_AIS:50:1:2:0.3 下,Hilly 5/25/500 变量得分 0.795 / 0.519 / 0.309,Forest 同档为 0.730 / 0.369 / 0.094,Megacity 离散函数掉到 0.377 / 0.159 / 0.028。综合评分 3.379,折算 37.54%,排在 29 种算法里的第 11 行,压过了布谷鸟、人工蜂群和模拟退火。 收敛图不是常见的前半段陡降后平缓,而是 S 形:仅前 10–20% 迭代急剧收敛,中段变慢,快结束时又加速。这能用「线性缩小突变增量范围」解释,但因为算法对测试函数表面的信息积累不均,实际收敛并不按线性走,收窄突变范围要到末期才明显起作用。换非线性收窄定律没带来改善,但别的变体仍值得试。 高变量函数上性能明显衰减,说明扩展能力偏弱,根源在它用了更简化的免疫系统模型与操作。外汇与贵金属参数优化属高风险,实盘前务必在 MT5 用历史数据复跑这套 10000 次求值,别直接信榜单名次。
别急着下结论
微人工免疫系统(Micro-AIS)把免疫机制压成了轻量优化器:外部参数少、实现简单,在 MT5 里跑通一套基础搜索只要几十行代码。但实测里它容易陷局部极值,收敛慢,变量一多性能就掉——附录存档的评级脚本里,标尺 0–100 的直方图上,高维函数得分普遍低于 60。 这类算法更像一块可改写的模板,靠简单架构留出了杂交空间:接 GA 突变、或并 BFO 局部优调都可能补收敛短板。外汇与贵金属参数优化属高风险实验,回测顺不代表实盘稳。 作者随文给了 29_The_world_of_AO_Micro_AIS.zip(536.94 KB),里面是前几篇算法的当前更新版;想验证就下下来,用自带脚本算评级表,自己跑一遍再判断值不值得进实盘。