珊瑚礁优化算法(CRO)·进阶篇
(2/3)·标准CRO只靠自然竞争太慢?引入逆幂律分布后邻域搜索效率明显不同
珊瑚礁优化器的初始化与布点逻辑
C_AO_CRO 类的 Init 先把搜索区间、步长和父类的 StandardInit 接好,随后算出礁体总规模 totalReefSize = reefRows * reefCols。初始珊瑚数由 rho0 * totalReefSize 四舍五入得到,若超过 popSize 则截断为 popSize,这一步直接决定种群起跑密度。 InitReef 负责在礁体内随机撒点:用 do-while 找未被占用的位置,pos 越界会被夹回 [0, totalReefSize-1]。每个新珊瑚的坐标由 u.RNDfromCI 在 [rangeMin, rangeMax] 内抽取,再经 u.SeInDiSp 对齐到离散步长——这意味着你调 rangeStep 会切实改变参数组合的解空间粒度。 Moving 里有个 revision 开关:当 revision 为 false 时,函数会对 occupied 为 true 的所有礁位做首次适应度评估。外汇与贵金属市场高波动,这类基于种群的优化仅用于历史参数搜索,实盘表现可能存在概率性偏差,开 MT5 把 rho0 从 0.3 改到 0.6 能直观看到初始珊瑚数变化。
class="type">bool C_AO_CRO::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">// search step class="kw">const class="type">int epochsP = class="num">0) class=class="str">"cmt">// number of epochs { class=class="str">"cmt">// Standard initialization of the parent class if (!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) class="kw">return class="kw">false; class=class="str">"cmt">//---------------------------------------------------------------------------- class=class="str">"cmt">// Calculate the reef total size totalReefSize = reefRows * reefCols; class=class="str">"cmt">// The number of starting corals should not exceed popSize class="type">int initialPopSize = (class="type">int)MathRound(rho0 * totalReefSize); if (initialPopSize > popSize) initialPopSize = popSize; class=class="str">"cmt">// Initialize the occupancy array and indices ArrayResize(occupied, totalReefSize); ArrayResize(reefIndices, totalReefSize); class=class="str">"cmt">// Fill the arrays with initial values for (class="type">int i = class="num">0; i < totalReefSize; i++) { occupied [i] = class="kw">false; reefIndices [i] = -class="num">1; } class=class="str">"cmt">// Reef initialization InitReef(); class="kw">return true; } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_CRO::InitReef() { class=class="str">"cmt">// Number of starting corals in the reef(based on rho0) class="type">int initialCorals = (class="type">int)MathRound(rho0 * totalReefSize); class=class="str">"cmt">// The number of starting corals should not exceed the population size if (initialCorals > popSize) initialCorals = popSize; class=class="str">"cmt">// Initialize initialCorals random positions in the reef for (class="type">int i = class="num">0; i < initialCorals; i++) { class="type">int pos; class=class="str">"cmt">// Look for a free position do { pos = (class="type">int)MathFloor(u.RNDfromCI(class="num">0, totalReefSize)); class=class="str">"cmt">// Protection against exceeding the array size if (pos < class="num">0) pos = class="num">0; if (pos >= totalReefSize) pos = totalReefSize - class="num">1; } class="kw">while (occupied [pos]); class=class="str">"cmt">// Create a new coral at the found position occupied [pos] = true; reefIndices [pos] = i; class=class="str">"cmt">// Generate random coordinates for a new coral for (class="type">int c = class="num">0; c < coords; c++) { class="type">class="kw">double coordinate = u.RNDfromCI(rangeMin [c], rangeMax [c]); a [i].c [c] = u.SeInDiSp(coordinate, rangeMin [c], rangeMax [c], rangeStep [c]); } } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class="type">void C_AO_CRO::Moving() { if (!revision) { class=class="str">"cmt">// Initial assessment of all corals in the reef for (class="type">int i = class="num">0; i < totalReefSize; i++) { if (occupied [i])
◍ 珊瑚礁算法的幼虫定居与修订循环
这段 CRO(珊瑚礁优化)实现里,Revision() 是每一代的核心驱动器:先扫一遍 totalReefSize 个礁位,把适应度 a[reefIndices[i]].f 优于当前全局最优 fB 的个体写进 cB,再走广播产卵、育雏、定居、无性繁殖、捕食五阶段。注意 larvae 数组按 totalReefSize * 2 预留空间,实际数量由 larvaCount 控制,避免每代反复分配内存。 LarvaSettling() 给单个幼虫最多 attemptsNum 次随机落位机会。每次用 MathFloor(u.RNDfromCI(0, totalReefSize)) 抽礁位,越界就跳过;若 occupied[pos] 为空,再在 popSize 范围内找一个未被 reefIndices 引用的空闲索引 newIndex,成功才落定。这种双重循环查重(j 扫礁位、i 扫种群)在礁规模大时偏慢,MT5 里跑 EURUSD 十五分钟数据、totalReefSize=200 时单代定居耗时可观,倾向用哈希或标记数组替换。 适应度计算被刻意移出类内,留到外部 FuncTests 统一评测,所以类里只管拓扑演进不管目标函数。外汇与贵金属参数优化属高风险实验,回测优解实盘可能漂移,验证前先在小样本周期跑通再放大。
{
class="type">int idx = reefIndices [i];
if (idx >= class="num">0 && idx < popSize)
{
class=class="str">"cmt">// Calculating fitness does not require class="kw">using GetFitness()
class=class="str">"cmt">// since it will be evaluated in external code(in FuncTests)
}
}
}
revision = true;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_CRO::Revision()
{
class=class="str">"cmt">// Update the global best solution
for (class="type">int i = class="num">0; i < totalReefSize; i++)
{
if (occupied [i] && a [reefIndices [i]].f > fB)
{
fB = a [reefIndices [i]].f;
ArrayCopy(cB, a [reefIndices [i]].c, class="num">0, class="num">0, WHOLE_ARRAY);
}
}
class=class="str">"cmt">// Form an array to store larvae
S_AO_Agent larvae [];
ArrayResize(larvae, totalReefSize * class="num">2); class=class="str">"cmt">// Allocate with reserve
class="type">int larvaCount = class="num">0;
class=class="str">"cmt">// Stage class="num">1: Broadcast Spawning
BroadcastSpawning(larvae, larvaCount);
class=class="str">"cmt">// Stage class="num">2: Brooding
Brooding(larvae, larvaCount);
class=class="str">"cmt">// Calculate the fitness function for each larva
class=class="str">"cmt">// (will be executed in external code in FuncTests)
class=class="str">"cmt">// Stage class="num">3: Larval settlement
for (class="type">int i = class="num">0; i < larvaCount; i++)
{
LarvaSettling(larvae [i]);
}
class=class="str">"cmt">// Stage class="num">4: Asexual reproduction
AsexualReproduction();
class=class="str">"cmt">// Stage class="num">5: Depredation
Depredation();
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">int C_AO_CRO::LarvaSettling(S_AO_Agent &larva)
{
class=class="str">"cmt">// Try to settle the larva attemptsNum times
for (class="type">int attempt = class="num">0; attempt < attemptsNum; attempt++)
{
class=class="str">"cmt">// Select a random position in the reef
class="type">int pos = (class="type">int)MathFloor(u.RNDfromCI(class="num">0, totalReefSize));
class=class="str">"cmt">// Check that the position is within the array
if (pos < class="num">0 || pos >= totalReefSize) class="kw">continue;
class=class="str">"cmt">// If the position is free, populate the larva
if (!occupied [pos])
{
class=class="str">"cmt">// Search for a free index in the agents array
class="type">int newIndex = -class="num">1;
for (class="type">int i = class="num">0; i < popSize; i++)
{
class="type">bool used = class="kw">false;
for (class="type">int j = class="num">0; j < totalReefSize; j++)
{
if (reefIndices [j] == i)
{
used = true;
class="kw">break;
}
}
if (!used)
{
newIndex = i;
class="kw">break;
}
}
if (newIndex != -class="num">1)「珊瑚虫占据与广播产卵的代码落点」
珊瑚礁优化(CRO)里,幼虫尝试定居的逻辑分两条路:位置空着就直接拷贝解并标记占用;已被占但幼虫适应度更高,则替换掉原珊瑚。两段都靠 ArrayCopy 搬移候选解向量,返回位置索引,失败统一回 -1。 广播产卵阶段先扫一遍 totalReefSize 收集 occupied 为 true 的索引,occupiedCount 为 0 就直接退出。Fb 参数决定参与广播的珊瑚比例:broadcastCount = MathRound(Fb * occupiedCount),下限钳到 1、上限不超 occupiedCount,避免空转或越界。 索引洗牌用 u.RNDfromCI(0, occupiedCount) 取随机位做交换,但每次都补了 j 与 i 的边界判断,这是原实现修掉数组越界隐患的地方。之后以步长 2 抽取相邻两只珊瑚当父母,idx1/idx2 再经 popSize 双重校验才进交叉,少一步就可能写烂种群数组。 在 MT5 里把 Fb 从默认 0.2 调到 0.8,同品种 EURUSD 回测中广播幼虫数会明显变多,种群多样性倾向上升,但单代计算耗时也可能翻倍——外汇与贵金属属高风险品种,参数改动须先在策略测试器验证过拟合。
{
class=class="str">"cmt">// Copy the larva&class="macro">#x27;s solution
ArrayCopy(a [newIndex].c, larva.c, class="num">0, class="num">0, WHOLE_ARRAY);
a [newIndex].f = larva.f;
class=class="str">"cmt">// Update information about the reef
occupied [pos] = true;
reefIndices [pos] = newIndex;
class="kw">return pos;
}
}
class=class="str">"cmt">// If the position is occupied, check if the larva is better than the current coral
else
if (occupied [pos] && reefIndices [pos] >= class="num">0 && reefIndices [pos] < popSize && larva.f > a [reefIndices [pos]].f)
{
class=class="str">"cmt">// The larva displaces the existing coral
ArrayCopy(a [reefIndices [pos]].c, larva.c, class="num">0, class="num">0, WHOLE_ARRAY);
a [reefIndices [pos]].f = larva.f;
class="kw">return pos;
}
}
class=class="str">"cmt">// If the larva failed to settle, class="kw">return -class="num">1
class="kw">return -class="num">1;
}
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
class="type">void C_AO_CRO::BroadcastSpawning(S_AO_Agent &larvae [], class="type">int &larvaCount)
{
class=class="str">"cmt">// Find all occupied positions
class="type">int occupiedIndices [];
class="type">int occupiedCount = class="num">0;
for (class="type">int i = class="num">0; i < totalReefSize; i++)
{
if (occupied [i])
{
ArrayResize(occupiedIndices, occupiedCount + class="num">1);
occupiedIndices [occupiedCount] = i;
occupiedCount++;
}
}
class=class="str">"cmt">// Check if there are no occupied positions
if (occupiedCount == class="num">0) class="kw">return;
class=class="str">"cmt">// Select the Fb share for broadcast spawning
class="type">int broadcastCount = (class="type">int)MathRound(Fb * occupiedCount);
if (broadcastCount <= class="num">0) broadcastCount = class="num">1; class=class="str">"cmt">// At least one coral
if (broadcastCount > occupiedCount) broadcastCount = occupiedCount;
class=class="str">"cmt">// Shuffle the indices
for (class="type">int i = class="num">0; i < occupiedCount; i++)
{
class=class="str">"cmt">// Register the array out-of-bounds problem
class="type">int j = (class="type">int)MathFloor(u.RNDfromCI(class="num">0, occupiedCount));
class=class="str">"cmt">// Ensure that j is within the array bounds
if (j >= class="num">0 && j < occupiedCount && i < occupiedCount)
{
class="type">int temp = occupiedIndices [i];
occupiedIndices [i] = occupiedIndices [j];
occupiedIndices [j] = temp;
}
}
class=class="str">"cmt">// Form pairs and create offspring
for (class="type">int i = class="num">0; i < broadcastCount - class="num">1; i += class="num">2)
{
if (i + class="num">1 < broadcastCount) class=class="str">"cmt">// Make sure there is a second parent
{
class="type">int idx1 = reefIndices [occupiedIndices [i]];
class="type">int idx2 = reefIndices [occupiedIndices [i + class="num">1]];
if (idx1 >= class="num">0 && idx1 < popSize && idx2 >= class="num">0 && idx2 < popSize)
{
class=class="str">"cmt">// Initialize the larva珊瑚礁算法的交叉与孵育实现
人工珊瑚礁优化(CRO)里,子代生成靠两条路径:双亲交叉与 occupied 位点的自繁殖。下面这段交叉逻辑取两个父代坐标均值,再叠加 ±0.1 倍参数区间的随机扰动,最后用 SeInDiSp 夹回离散合法网格。
larvae [larvaCount].Init(coords); class=class="str">"cmt">// Create a new larva as a result of crossover for (class="type">int c = class="num">0; c < coords; c++) { class=class="str">"cmt">// Simple crossover method: average of parents&class="macro">#x27; coordinates with a small mutation class="type">class="kw">double value = (a [idx1].c [c] + a [idx2].c [c]) / class="num">2.0 + u.RNDfromCI(-class="num">0.1, class="num">0.1) * (rangeMax [c] - rangeMin [c]); larvae [larvaCount].c [c] = u.SeInDiSp(value, rangeMin [c], rangeMax [c], rangeStep [c]); } class=class="str">"cmt">// Increase the larvae counter larvaCount++;
class="type">void C_AO_CRO::Brooding(S_AO_Agent &larvae [], class="type">int &larvaCount) { class="type">int occupiedIndices []; class="type">int occupiedCount = class="num">0; for (class="type">int i = class="num">0; i < totalReefSize; i++) { if (occupied [i]) { ArrayResize(occupiedIndices, occupiedCount + class="num">1); occupiedIndices [occupiedCount] = i; occupiedCount++; } } if (occupiedCount == class="num">0) class="kw">return; class="type">int broodingCount = (class="type">int)MathRound((class="num">1.0 - Fb) * occupiedCount); if (broodingCount <= class="num">0) broodingCount = class="num">1; if (broodingCount > occupiedCount) broodingCount = occupiedCount; for (class="type">int i = class="num">0; i < occupiedCount; i++) { class="type">int j = (class="type">int)MathFloor(u.RNDfromCI(class="num">0, occupiedCount)); if (j >= class="num">0 && j < occupiedCount && i < occupiedCount) { class="type">int temp = occupiedIndices [i]; occupiedIndices [i] = occupiedIndices [j]; occupiedIndices [j] = temp; } } for (class="type">int i = class="num">0; i < broodingCount; i++) { if (i < occupiedCount) { class="type">int idx = reefIndices [occupiedIndices [i]]; if (idx >= class="num">0 && idx < popSize) { larvae [larvaCount].Init(coords); for (class="type">int c = class="num">0; c < coords; c++) { class="type">class="kw">double value = a [idx].c [c] + u.RNDfromCI(-class="num">0.2, class="num">0.2) * (rangeMax [c] - rangeMin [c]);
larvae [larvaCount].Init(coords); class=class="str">"cmt">// Create a new larva as a result of crossover for (class="type">int c = class="num">0; c < coords; c++) { class=class="str">"cmt">// Simple crossover method: average of parents&class="macro">#x27; coordinates with a small mutation class="type">class="kw">double value = (a [idx1].c [c] + a [idx2].c [c]) / class="num">2.0 + u.RNDfromCI(-class="num">0.1, class="num">0.1) * (rangeMax [c] - rangeMin [c]); larvae [larvaCount].c [c] = u.SeInDiSp(value, rangeMin [c], rangeMax [c], rangeStep [c]); } class=class="str">"cmt">// Increase the larvae counter larvaCount++; class="type">void C_AO_CRO::Brooding(S_AO_Agent &larvae [], class="type">int &larvaCount) { class="type">int occupiedIndices []; class="type">int occupiedCount = class="num">0; for (class="type">int i = class="num">0; i < totalReefSize; i++) { if (occupied [i]) { ArrayResize(occupiedIndices, occupiedCount + class="num">1); occupiedIndices [occupiedCount] = i; occupiedCount++; } } if (occupiedCount == class="num">0) class="kw">return; class="type">int broodingCount = (class="type">int)MathRound((class="num">1.0 - Fb) * occupiedCount); if (broodingCount <= class="num">0) broodingCount = class="num">1; if (broodingCount > occupiedCount) broodingCount = occupiedCount; for (class="type">int i = class="num">0; i < occupiedCount; i++) { class="type">int j = (class="type">int)MathFloor(u.RNDfromCI(class="num">0, occupiedCount)); if (j >= class="num">0 && j < occupiedCount && i < occupiedCount) { class="type">int temp = occupiedIndices [i]; occupiedIndices [i] = occupiedIndices [j]; occupiedIndices [j] = temp; } } for (class="type">int i = class="num">0; i < broodingCount; i++) { if (i < occupiedCount) { class="type">int idx = reefIndices [occupiedIndices [i]]; if (idx >= class="num">0 && idx < popSize) { larvae [larvaCount].Init(coords); for (class="type">int c = class="num">0; c < coords; c++) { class="type">class="kw">double value = a [idx].c [c] + u.RNDfromCI(-class="num">0.2, class="num">0.2) * (rangeMax [c] - rangeMin [c]);
◍ 无性繁殖与捕食的礁体更新逻辑
珊瑚礁优化(CRO)在 MT5 里落地时,无性繁殖阶段先扫一遍 totalReefSize 个网格位置,把 occupied[i] 为真的下标收进 occupiedIndices 数组;若 occupiedCount 为 0 直接 return,避免空礁做无用计算。 接着按适应度排序,取 budCount = MathRound(Fa * occupiedCount) 个最优珊瑚克隆,Fa 为繁殖比例参数,budCount 下限锁死为 1、上限不超 occupiedCount,保证至少出一株克隆且不会越界。 克隆体用 ArrayCopy 原样拷贝父代坐标参数 c 与适应度 f,再丢进 LarvaSettling 尝试占坑,这一步决定了优解在种群里的扩散速度,外汇与贵金属参数寻优时可能明显影响收敛节奏,但杠杆品类高风险,回测不过别硬上。 捕食函数 Depredation 以 Pd 概率触发,同样先捞 occupiedIndices,排序后把数组前后对调,让适应度最差的先面对淘汰——和前面繁殖形成「留优去劣」的闭环,开 MT5 把 Fa、Pd 调到 0.1~0.3 区间跑一遍就能看出礁体置换频率的差异。
class="type">void C_AO_CRO::AsexualReproduction() { class="type">int occupiedIndices []; class="type">int occupiedCount = class="num">0; for (class="type">int i = class="num">0; i < totalReefSize; i++) { if (occupied [i]) { ArrayResize(occupiedIndices, occupiedCount + class="num">1); occupiedIndices [occupiedCount] = i; occupiedCount++; } } if (occupiedCount == class="num">0) class="kw">return; SortAgentsByFitness(occupiedIndices, occupiedCount); class="type">int budCount = (class="type">int)MathRound(Fa * occupiedCount); if (budCount <= class="num">0) budCount = class="num">1; if (budCount > occupiedCount) budCount = occupiedCount; for (class="type">int i = class="num">0; i < budCount; i++) { if (i < occupiedCount) { class="type">int idx = reefIndices [occupiedIndices [i]]; if (idx >= class="num">0 && idx < popSize) { S_AO_Agent clone; clone.Init(coords); ArrayCopy(clone.c, a [idx].c, class="num">0, class="num">0, WHOLE_ARRAY); clone.f = a [idx].f; LarvaSettling(clone); } } } } oid C_AO_CRO::Depredation() { if (u.RNDfromCI(class="num">0, class="num">1) < Pd) { class="type">int occupiedIndices[]; class="type">int occupiedCount = class="num">0; for (class="type">int i = class="num">0; i < totalReefSize; i++) { if (occupied[i]) { ArrayResize(occupiedIndices, occupiedCount + class="num">1); occupiedIndices[occupiedCount] = i; occupiedCount++; } } if (occupiedCount == class="num">0) class="kw">return; SortAgentsByFitness(occupiedIndices, occupiedCount); for (class="type">int i = class="num">0; i < occupiedCount / class="num">2; i++)