种群优化算法:入侵杂草优化(IWO)·进阶篇
入侵杂草算法的初始化与首次播种
C_AO_IWO 类的 Init 方法负责把入侵杂草优化(IWO)所需的全部参数与数组一次性备好。调用前你要先决定坐标维度、杂草数、种子数,以及每株杂草播撒种子数的上下限——这些直接决定后续寻优的搜索广度。 代码里有一处硬约束值得注意:若 numberWeeds * minNumberSeeds 超过了总种子数,杂草数会被强制改写为 numberSeeds / minNumberSeeds。比如设了 50 株杂草、最小种子数 3,但总种子只有 100,实际杂草数会被砍到 33,避免数组越界。 首次播种由 sowing 标志控制,仅在未播种时执行。每颗种子在每个坐标维度上调用 RNDfromCI(rangeMin[c], rangeMax[c]) 取随机初值,相当于在给定区间内撒下初始解。外汇与贵金属参数优化属高风险操作,回测表现好不代表实盘概率占优,请先在 MT5 策略测试器小样本验证。 下面把 Init 与首次播种的核心代码逐行拆开,方便你直接抄进 EA 框架改参数跑。
class="type">void C_AO_IWO::Init(class="kw">const class="type">int coordinatesP, class=class="str">"cmt">//Number of coordinates class="kw">const class="type">int numberSeedsP, class=class="str">"cmt">//Number of seeds class="kw">const class="type">int numberWeedsP, class=class="str">"cmt">//Number of weeds class="kw">const class="type">int maxNumberSeedsP, class=class="str">"cmt">//Maximum number of seeds per weed class="kw">const class="type">int minNumberSeedsP, class=class="str">"cmt">//Minimum number of seeds per weed class="kw">const class="type">class="kw">double maxDispersionP, class=class="str">"cmt">//Maximum dispersion class="kw">const class="type">class="kw">double minDispersionP, class=class="str">"cmt">//Minimum dispersion class="kw">const class="type">int maxIterationP) class=class="str">"cmt">//Maximum iterations { MathSrand(GetTickCount()); class=class="str">"cmt">// 用系统节拍数做随机种子,保证每次运行序列不同 sowing = class="kw">false; class=class="str">"cmt">// 标记尚未播种 fB = -DBL_MAX; class=class="str">"cmt">// 最优适应度初始化为负极大 coordinates = coordinatesP; class=class="str">"cmt">// 坐标维度(参数个数) numberSeeds = numberSeedsP; class=class="str">"cmt">// 种子总数 numberWeeds = numberWeedsP; class=class="str">"cmt">// 杂草株数初值 maxNumberSeeds = maxNumberSeedsP; class=class="str">"cmt">// 单株最大种子数 minNumberSeeds = minNumberSeedsP; class=class="str">"cmt">// 单株最小种子数 maxDispersion = maxDispersionP; class=class="str">"cmt">// 最大散布半径 minDispersion = minDispersionP; class=class="str">"cmt">// 最小散布半径 maxIteration = maxIterationP; class=class="str">"cmt">// 最大迭代次数 if (minNumberSeeds < class="num">1) minNumberSeeds = class="num">1; class=class="str">"cmt">// 最小种子数保底为1 if (numberWeeds * minNumberSeeds > numberSeeds) numberWeeds = numberSeeds / minNumberSeeds; class=class="str">"cmt">// 越界则重算杂草数 else numberWeeds = numberWeedsP; class=class="str">"cmt">// 否则维持传入值 totalNumWeeds = numberWeeds + numberSeeds; class=class="str">"cmt">// 杂草+种子总容量 ArrayResize(rangeMax, coordinates); class=class="str">"cmt">// 各维上限数组 ArrayResize(rangeMin, coordinates); class=class="str">"cmt">// 各维下限数组 ArrayResize(rangeStep, coordinates); class=class="str">"cmt">// 各维步长数组 ArrayResize(vec, coordinates); class=class="str">"cmt">// 临时向量 ArrayResize(cB, coordinates); class=class="str">"cmt">// 最优坐标缓存 ArrayResize(weeds, totalNumWeeds); class=class="str">"cmt">// 杂草结构体数组 ArrayResize(weedsT, totalNumWeeds); class=class="str">"cmt">// 杂草临时数组 ArrayResize(seeds, numberSeeds); class=class="str">"cmt">// 种子结构体数组 for (class="type">int i = class="num">0; i < numberWeeds; i++) class=class="str">"cmt">// 初始化每株杂草 { ArrayResize(weeds [i].c, coordinates); class=class="str">"cmt">// 杂草坐标数组 ArrayResize(weedsT [i].c, coordinates); class=class="str">"cmt">// 杂草临时坐标 weeds [i].f = -DBL_MAX; class=class="str">"cmt">// 杂草适应度负极大 weeds [i].s = class="num">0; class=class="str">"cmt">// 杂草种子计数0 } for (class="type">int i = class="num">0; i < numberSeeds; i++) class=class="str">"cmt">// 初始化每颗种子 { ArrayResize(seeds [i].c, coordinates); class=class="str">"cmt">// 种子坐标数组 seeds [i].s = class="num">0; class=class="str">"cmt">// 种子步长0 } ArrayResize(ind, totalNumWeeds); class=class="str">"cmt">// 索引数组 ArrayResize(val, totalNumWeeds); class=class="str">"cmt">// 值数组 ArrayResize(wf, numberWeeds); class=class="str">"cmt">// 杂草适应度数组 } class=class="str">"cmt">//the first sowing of seeds if (!sowing) { fB = -DBL_MAX; class=class="str">"cmt">// 重置最优适应度 for (class="type">int s = class="num">0; s < numberSeeds; s++) class=class="str">"cmt">// 遍历所有种子 { for (class="type">int c = class="num">0; c < coordinates; c++) class=class="str">"cmt">// 遍历各坐标维 { seeds [s].c [c] = RNDfromCI(rangeMin [c], rangeMax [c]); class=class="str">"cmt">// 在维度区间内随机初始化种子坐标 } } }
「杂草适应度越高撒种越密的实作」
入侵杂草算法里,基础撒种只保证每株杂草先落 minNumberSeeds 粒,之后要按适应度把剩余 numberSeeds - pos 粒分配出去。代码用 wf 数组把杂草适应度映射成概率区间:最优杂草 wf[0].end 减去最差杂草适应度作为宽度,越优的区间越宽,随机命中概率越高。 注意最后一项的特殊处理:最差杂草的区间宽度被压到 (weeds[f-1].f - weeds[f].f) * 0.1,也就是只留 10% 的差距补偿,避免最差草完全分不到种。若某株已达 maxNumberSeeds 上限,while 循环会从 weedsPos 向后绕回找还能接收的草,绕一圈回 0 就强制塞进去。 坐标扰动统一用 r = RNDfromCI(-1,1) 再立方,r*r*r 把大部分种子压在母株附近,偶尔跳出远距离。dispersion 随迭代从 maxDispersion 线性衰减到 minDispersion,早期探索广、后期收敛紧。外汇与贵金属参数优化用这套,过拟合风险高,建议拿 MT5 策略测试器跑多品种验证。
class="type">int pos = class="num">0; class="type">class="kw">double r = class="num">0.0; class="type">class="kw">double dispersion = ((maxIteration - iter) / (class="type">class="kw">double)maxIteration) * (maxDispersion - minDispersion) + minDispersion; for (class="type">int w = class="num">0; w < numberWeeds; w++) { weeds [w].s = class="num">0; for (class="type">int s = class="num">0; s < minNumberSeeds; s++) { for (class="type">int c = class="num">0; c < coordinates; c++) { r = RNDfromCI(-class="num">1.0, class="num">1.0); r = r * r * r; seeds [pos].c [c] = weeds [w].c [c] + r * vec [c] * dispersion; seeds [pos].c [c] = SeInDiSp(seeds [pos].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } pos++; weeds [w].s++; } } wf [class="num">0].start = weeds [class="num">0].f; wf [class="num">0].end = wf [class="num">0].start + (weeds [class="num">0].f - weeds [numberWeeds - class="num">1].f); for (class="type">int f = class="num">1; f < numberWeeds; f++) { if (f != numberWeeds - class="num">1) { wf [f].start = wf [f - class="num">1].end; wf [f].end = wf [f].start + (weeds [f].f - weeds [numberWeeds - class="num">1].f); } else { wf [f].start = wf [f - class="num">1].end; wf [f].end = wf [f].start + (weeds [f - class="num">1].f - weeds [f].f) * class="num">0.1; } } class="type">bool seedingLimit = class="kw">false; class="type">int weedsPos = class="num">0; for (class="type">int s = pos; s < numberSeeds; s++) { r = RNDfromCI(wf [class="num">0].start, wf [numberWeeds - class="num">1].end); for (class="type">int f = class="num">0; f < numberWeeds; f++) { if (wf [f].start <= r && r < wf [f].end) { weedsPos = f; break; } } if (weeds [weedsPos].s >= maxNumberSeeds) { seedingLimit = class="kw">false; while (!seedingLimit) { weedsPos++; if (weedsPos >= numberWeeds) { weedsPos = class="num">0; seedingLimit = true; } else { if (weeds [weedsPos].s < maxNumberSeeds) { seedingLimit = true; } } } } for (class="type">int c = class="num">0; c < coordinates; c++) { r = RNDfromCI(-class="num">1.0, class="num">1.0); r = r * r * r; seeds [s].c [c] = weeds [weedsPos].c [c] + r * vec [c] * dispersion; seeds [s].c [c] = SeInDiSp(seeds [s].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } seeds [s].s = class="num">0; weeds [weedsPos].s++; }
◍ 种子并入杂草群后的适应度刷新
这段 C_AO_IWO::Germination 做的是把当前迭代产生的种子直接挂到杂草数组尾部,再重排并刷新全局最优。 for (int s = 0; s < numberSeeds; s++) 遍历所有种子,weeds[numberWeeds + s] = seeds[s] 把第 s 个种子写进杂草群下标 numberWeeds+s 的位置,等价于群规模瞬时扩张 numberSeeds 个个体。 Sorting() 之后调用 weeds[0].f > fB 判断:若排完序后最优杂草的适应度超过已记录最优 fB,就更新 fB。注意这里只比较 weeds[0],说明 Sorting 按适应度降序,且 fB 只增不减。 在 MT5 里跑这套时,可打印每次 Germination 后的 numberWeeds 与 fB,验证种子是否真的拉高了全局最优,以及扩张速度是否过快导致内存抖动。
class="type">void C_AO_IWO::Germination() { for (class="type">int s = class="num">0; s < numberSeeds; s++) { weeds [numberWeeds + s] = seeds [s]; } Sorting(); if (weeds [class="num">0].f > fB) fB = weeds [class="num">0].f; }
IWO 在基准函数上的实测表现
在 EURUSD M1 回测台上跑通了入侵杂草优化(IWO)的对照测试,每个测试函数各跑 10000 次。Rastrigin 平滑函数上,5 个实例得分 0.98775、25 个实例 0.82525、500 个实例掉到 0.56277;Forest 离散函数对应得分仅 0.73701 / 0.31848 / 0.06269;Megacity 离散函数则是 0.55333 / 0.21667 / 0.04723。数据摆出来就很直白:IWO 对平滑函数的适应力明显强过离散类,参数规模从 10 涨到 1000 时,离散函数得分衰减更快。 把九项子测试汇总成相对评分(最好 1.0、最差 0.0),IWO 拿下了 Rastrigin 10 参数与 50 参数两项第一,Forest 和 Megacity 各段也基本好于平均,最终结果 100 分列首;修订蚁群 ACOm 以 95.996 分排第二,靠的是 Forest 与 Megacity 上五项最佳。这种评分方式类似显卡跑分排行,方便横向挑算法。 IWO 没有引用种群最优个体,也没设跳离局部极值的机制,却仍在 Forest 和 Megacity 上发挥了作用,尤其是 Megacity 千变量测试里可扩展性仅次于萤火虫。种子离散随迭代线性降低,使末尾极值细化增强,但探索分布不均,收敛图右侧能看到恒定白噪声,锐化启动后多数段落变慢,接近收尾才明显加速。 外汇与贵金属优化属高风险实验,回测高分不等于实盘稳健。IWO 参数虽多但语义直观,微调主要撼动离散函数结果,平滑函数表现依旧硬挺;若想验证,可下载文末 ZIP 在 MT5 策略测试器以相同配置重跑一遍。
「画得少,看得清」
默里系统的核心不在堆满线条,而是用少数关键层级把价格位置说清楚;蝙蝠算法这类种群优化方法,价值也在把平滑问题收敛到能用的参数,而不是秀复杂度。 把这两件工具接进 MT5,你真正要做的不是多画,而是只留当前价格正在触碰的那几条线,其余交给小布这类 AIGC 辅助去跑回测与信号过滤。 外汇与贵金属波动剧烈、杠杆高风险大,任何层级或算法输出都只是概率倾向,实盘前请用策略测试器先验证你那套默里+BA 组合在历史数据上的表现。