适应性社会行为优化(ASBO):两阶段演变·进阶篇
「ASBO 优化器的私有状态与初始化落点」
自适应蝙蝠优化(ASBO)在 MT5 里做参数寻优时,类内部先布了一批私有成员:epochs 与 epochNow 管总迭代和当前计数,currPop 标记正在跑的第几个种群,isPhase2 切换两阶段搜索,popEpochs 记录种群级迭代;tau 与 tau_prime 是变异步长控制量,按坐标维数 coords 算出,公式为 1/√(2·coords) 与 1/√(2·√coords)。 agentsPhase2、agentsTemp 及 allAgentsForSortPhase2 等数组,分别缓存二阶段个体、临时个体和跨种群排序池,pop[] 则装 M 个种群。这些声明决定了后面内存预分配的上限,改维数或种群数必须同步动它们。 Init() 里先把搜索上下界与步长交给 StandardInit 校验,失败直接返 false。随后 epochNow、currPop、popEpochs 清零,isPhase2 置 false,tau 系按上述公式算;再用 ArrayResize 把 pop 扩到 numPop,每个种群 agent 扩到 popSize 并逐个 Init。agentsPhase2 / agentsTemp 扩到 popSize,跨种群排序池扩到 popSize*numPop,全部 Init 完才返 true。 开 MT5 把 numPop、popSize、coords 打印出来,能直接看到排序池长度是种群数的乘积——若你打算跑 10 维、20 种群、每群 30 个体,排序池就是 600,内存占用别低估。外汇与贵金属策略寻参属高风险实验,回测优解实盘可能失效。
class="kw">private: class="type">int epochs; class="type">int epochNow; class="type">int currPop; class="type">bool isPhase2; class="type">int popEpochs; class="type">class="kw">double tau; class="type">class="kw">double tau_prime; S_ASBO_Agent allAgentsForSortPhase2 []; S_ASBO_Agent allAgentsTemp []; S_ASBO_Agent agentsPhase2 []; S_ASBO_Agent agentsTemp []; S_ASBO_Population pop []; class=class="str">"cmt">//M populations class="type">void AdaptiveMutation(S_ASBO_Agent &agent); class="type">void UpdatePosition(class="type">int ind, S_ASBO_Agent &ag []); class="type">void FindNeighborCenter(class="type">int ind, S_ASBO_Agent &ag [], class="type">class="kw">double ¢er []); class="type">void Sorting(S_ASBO_Agent &p [], S_ASBO_Agent &pTemp [], class="type">int size); }; class="type">bool C_AO_ASBO::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 { if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return class="kw">false; epochs = epochsP; epochNow = class="num">0; currPop = class="num">0; isPhase2 = class="kw">false; popEpochs = class="num">0; tau = class="num">1.0 / MathSqrt(class="num">2.0 * coords); tau_prime = class="num">1.0 / MathSqrt(class="num">2.0 * MathSqrt(coords)); ArrayResize(pop, numPop); for (class="type">int i = class="num">0; i < numPop; i++) { ArrayResize(pop [i].agent, popSize); for (class="type">int j = class="num">0; j < popSize; j++) pop [i].agent [j].Init(coords, rangeMin, rangeMax); } ArrayResize(agentsPhase2, popSize); ArrayResize(agentsTemp, popSize); for (class="type">int i = class="num">0; i < popSize; i++) agentsPhase2 [i].Init(coords, rangeMin, rangeMax); ArrayResize(allAgentsForSortPhase2, popSize * numPop); ArrayResize(allAgentsTemp, popSize * numPop); for (class="type">int i = class="num">0; i < popSize * numPop; i++) { allAgentsForSortPhase2 [i].Init(coords, rangeMin, rangeMax); allAgentsTemp [i].Init(coords, rangeMin, rangeMax); } class="kw">return true; } class="type">void C_AO_ASBO::Moving() {
◍ 两阶段种群进化的代码骨架
这段逻辑把一个优化过程拆成 Phase 1 与 Phase 2 两套调度。Phase 1 里按子种群(pop)轮流迭代,每跑满 epochsForPop 轮就切到下一个子种群(currPop++),全部 numPop 个子种群走完后置 isPhase2=true,并把所有 agent 拉平排序,只留前 popSize 个进 agentsPhase2。 Phase 1 未结束时,对每个子种群内除首 agent 外的个体做 AdaptiveMutation 和 UpdatePosition,再把坐标拷回 a[i].c,随后 popEpochs++ 直接 return,说明单代只推进一个子种群。Phase 2 启动后不再分池,直接对 agentsPhase2 全体做变异与位置更新。 Revision 函数负责记录历史最优:先扫一遍 a[] 找最大适应度 fB 与索引 ind,命中就把该组坐标拷进 cB。若仍处 Phase 1(currPop<numPop),把当前代结果写回对应子种群并刷新各自 fBest/cBest 后排序;否则写入 agentsPhase2 并维护其 fBest。外汇与贵金属参数优化属高风险实验,回测优解实盘可能失效。 别把排序当免费操作:popSize*numPop 在 Phase 1 收尾时一次性排序,numPop 设大会让这行 u.Sorting 在切换瞬间吃掉明显算力,MT5 策略测试器里看得出那一代耗时突增。
epochNow++; class=class="str">"cmt">//Phase class="num">1---------------------------------------------------------------------- if (!isPhase2) { if (popEpochs >= epochsForPop) { popEpochs = class="num">0; currPop++; fB = -DBL_MAX; } if (currPop >= numPop) { isPhase2 = true; class="type">int cnt = class="num">0; for (class="type">int i = class="num">0; i < numPop; i++) { for (class="type">int j = class="num">0; j < popSize; j++) { allAgentsForSortPhase2 [cnt] = pop [i].agent [j]; cnt++; } } u.Sorting(allAgentsForSortPhase2, allAgentsTemp, popSize * numPop); for (class="type">int j = class="num">0; j < popSize; j++) agentsPhase2 [j] = allAgentsForSortPhase2 [j]; } else { for (class="type">int i = class="num">1; i < popSize; i++) { AdaptiveMutation(pop [currPop].agent [i]); UpdatePosition(i, pop [currPop].agent); ArrayCopy(a [i].c, pop [currPop].agent [i].c); } popEpochs++; class="kw">return; } } class=class="str">"cmt">//Phase class="num">2---------------------------------------------------------------------- for (class="type">int i = class="num">1; i < popSize; i++) { AdaptiveMutation(agentsPhase2 [i]); UpdatePosition(i, agentsPhase2); ArrayCopy(a [i].c, agentsPhase2 [i].c); } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_ASBO::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 (ind != -class="num">1) ArrayCopy(cB, a [ind].c, class="num">0, class="num">0, WHOLE_ARRAY); class=class="str">"cmt">//---------------------------------------------------------------------------- class=class="str">"cmt">//phase class="num">1 if (currPop < numPop) { for (class="type">int i = class="num">0; i < popSize; i++) { pop [currPop].agent [i].f = a [i].f; if (a [i].f > pop [currPop].agent [i].fBest) { pop [currPop].agent [i].fBest = a [i].f; ArrayCopy(pop [currPop].agent [i].cBest, a [i].c, class="num">0, class="num">0, WHOLE_ARRAY); } } u.Sorting(pop [currPop].agent, agentsTemp, popSize); } class=class="str">"cmt">//phase class="num">2 else { for (class="type">int i = class="num">0; i < popSize; i++) { agentsPhase2 [i].f = a [i].f; if (a [i].f > agentsPhase2 [i].fBest) { agentsPhase2 [i].fBest = a [i].f;
自适应变异与邻位更新的实作细节
ASBO 算法的进化质量,很大程度落在 AdaptiveMutation 这一步。它给三个系数 Cg、Cs、Cn 各自乘上了一个指数噪声:以 tau_prime 乘单次高斯扰动,再加 tau 乘 8 维高斯扰动,再取 MathExp。这种对数正态式的缩放,让步长随代数自然收缩,避免后期在最优解附近乱跳。 UpdatePosition 里给出了三种位置更新写法,源码注释掉的是带随机符号的版本 1) 和纯高斯版本 3),当前启用的是 2) 的确定性加权:deltaX[j] = Cg*(全局最优-当前) + Cs*(个体历史最优-当前) + Cn*(邻域中心-当前)。把随机项去掉后,单代计算量下降,但多样性只剩 Cn 这一路从邻域中心带来。 每一维算完 deltaX 后立刻做边界吸附:u.SeInDiSp 按 rangeMin/Max/Step 把坐标夹回离散可行域。这意味着在 MT5 里跑这组 agent 时,若 rangeStep 设得过大,邻域中心 deltaX 很容易被量化成 0,算法会退化为只追 cB 和 cBest 的两股力。 外汇与贵金属行情具有高杠杆与跳空风险,任何基于历史行情拟合的 agent 参数,都只代表过去样本下的概率倾向,实盘前请用策略测试器以不同 spread 重跑验证。
class="type">void C_AO_ASBO::AdaptiveMutation(S_ASBO_Agent &ag) { ag.Cg *= MathExp(tau_prime * u.GaussDistribution(class="num">0, -class="num">1, class="num">1, class="num">1) + tau * u.GaussDistribution(class="num">0, -class="num">1, class="num">1, class="num">8)); ag.Cs *= MathExp(tau_prime * u.GaussDistribution(class="num">0, -class="num">1, class="num">1, class="num">1) + tau * u.GaussDistribution(class="num">0, -class="num">1, class="num">1, class="num">8)); ag.Cn *= MathExp(tau_prime * u.GaussDistribution(class="num">0, -class="num">1, class="num">1, class="num">1) + tau * u.GaussDistribution(class="num">0, -class="num">1, class="num">1, class="num">8)); } class="type">void C_AO_ASBO::UpdatePosition(class="type">int ind, S_ASBO_Agent &ag []) { class="type">class="kw">double deltaX []; ArrayResize(deltaX, coords); FindNeighborCenter(ind, ag, deltaX); for (class="type">int j = class="num">0; j < coords; j++) { class=class="str">"cmt">//class="num">2) deltaX [j] = ag [ind].Cg * (cB [j] - ag [ind].c [j]) + ag [ind].Cs * (ag [ind].cBest [j] - ag [ind].c [j]) + ag [ind].Cn * (deltaX [j] - ag [ind].c [j]); ag [ind].c [j] += deltaX [j]; ag [ind].c [j] = u.SeInDiSp(ag [ind].c [j], rangeMin [j], rangeMax [j], rangeStep [j]); } }
「ASBO 跑分落在榜单中段」
把 ASBO 接到标准测试函数上跑,单次迭代 10000 步,结果直接印在控制台。Hilly 函数 5/25/500 维度得分分别是 0.7633、0.4925、0.3262;Forest 函数同维度 0.7955、0.4003、0.2609;Megacity 离散函数则是 0.2646、0.1717、0.1820。三项加总 All score 3.65710,占满分比的 40.63%。 横向拉一张 30 个算法的评分表,ASBO 排第 21 位,前面是 WOAm、AEFA、ACOm 这类常见群智能变体,后面紧挨着 MEC、IWO、Micro-AIS。榜首 ANS 跨邻里搜索拿 6.134(68.15%),差距主要来自 Forest 与 Megacity 上的高维项——ASBO 在 500 维 Forest 只拿到 0.2609,而 ANS 同项有 0.9233。 收敛图暴露了它的机制指纹:第一阶段几条线间断式下探,是多个子种群轮流探不同参数区留下的缺口;第二阶段线变实心,说明把前期各群最佳解回收统一精炼。这种「先散后聚」的结构在 1000 参数规模的 Forest、Megacity 上仍能维持可用精度,但绝对水平只算中游。外汇与贵金属参数优化属高风险场景,榜单数字仅反映离线函数表现,实盘映射前建议在 MT5 用历史 ticks 重跑一遍同构测试。