极值优化(EO)
内容
引言
现实中的诸多问题(尤其是交易优化问题),其目标函数多为复杂离散空间形态,存在多重局部极值、不连续区间与不可导区域,因此传统基于梯度的优化方法并不适用。为解决此类问题,学界发展出了多种元启发式算法。各类算法在搜索空间的全局探索与局部利用的平衡上各有优劣。
极值优化(EO)是一种源自Bak‑Sneppen模型的元启发式优化算法。该算法由Stefan Boettcher和Allon Percus于1999年提出,其灵感来源于自组织临界性理论:复杂系统会自发演化至临界状态,并不断发生不同规模的雪崩式波动。针对连续优化场景,可以进一步开发基于种群的改进版EO算法,通过迭代式种群完成更新。
算法实现
Bak-Sneppen模型最初用于描述生态系统的演化过程,并揭示自组织临界性现象。这是一个简单的协同演化模型,用以说明局部相互作用如何引发全局性的临界事件。
Bak-Sneppen模型原理
物种生态系统:
- 共有N个物种排列成链状(或分布在网格上);
- 每个物种i对应一个适应度值fi ∈ [0,1];
- 适应度代表物种对环境的适应能力。
演化动力学
在每一步迭代中: 1. 找到适应度最小的物种:fmin = min{fi} 2. 将该物种及其相邻物种的适应度替换为新的随机值 3. 重复上述过程自组织临界性
- 系统会自主演化至临界状态;
- 形成临界阈值fc ≈ 0.67;
- 适应度低于该阈值的物种会以雪崩式突变的形式被淘汰;
- 雪崩规模服从幂律分布。
系统无需外部干预,会长期处于停滞平稳阶段,随后突发不同规模的雪崩式剧烈波动,以此完成自组织演化。雪崩规模遵循幂律变化规律:P(s) ~ s^(-τ)。在无固定特征尺度的系统中,物种的存续周期同样服从幂律分布。单一物种的状态变化会影响其相邻物种,局部交互作用最终催生整体系统的全局演化特征。
Boettcher与Percus基于Bak-Sneppen模型的核心原理,研发出了极值优化算法。该算法的核心逻辑并非优化最优个体,而是改进最差个体。这种聚焦最差单元的优化思路看似反直觉,却能有效摆脱局部最优解的桎梏。Bak-Sneppen模型的演化遵循间断平衡理论:系统长期保持稳态,偶尔被突发性剧变打破。极值优化算法复用了这一核心原理:仅对系统内劣质组件进行迭代更新。一个组件的改进可能显著提升整体解,但也可能使其他组件变成“较差”组件。
极值优化算法将原本应用于生态系统模型的自组织临界性原理,迁移至优化求解领域。其核心创新点在于:利用复杂系统与生俱来的自组织特性,自动平衡搜索空间的全局探索与局部利用。我们将通过实际工程问题验证该算法的有效性,算法伪代码流程如下。
初始化:
- 基于种群规模(popSize)生成智能体种群
- 初始化参数
- 通过创建数组对智能体和组件排序
首次运行(初始化种群):
- 对于种群中的每个智能体:
- 遍历智能体的各个组件(坐标):
- 在合法取值范围内随机赋值
- 按需执行数据离散化处理
- 遍历智能体的各个组件(坐标):
基本优化循环:
对种群内每个智能体执行以下操作:
- 智能体排序:
- 生成包含所有智能体及其适应度的列表
- 按适应度排序(对于最大化的劣解优先排序)
- 选取目标智能体:
- 基于幂律分布P(n) ∝ n^(-τ)
- 依据该分布随机选取排序位次
- 将该位次对应的智能体确定为待优化目标智能体
- 目标智能体组件排序:
- 逐一计算目标智能体所有组件的适应度:
- 适应度计算公式:适应度 = 1 - (相对最优值的归一化偏差)
- 按组件适应度升序排序(劣质组件优先)
- 逐一计算目标智能体所有组件的适应度:
- 组件筛选与变异:
- 通过幂律分布随机选取待优化组件的排序位次
- 为选中组件重新随机赋予合法数值
- 执行边界校验,并按需离散化
结果更新:
- 依据适应度对全体种群排序(最优解置顶)
- 如果迭代得到更优解,则更新全局最优解
让我们通过代码实现该算法。我们将自定义极值优化算法类,基于优化最差单元、迭代寻优的核心逻辑实现EO算法。该类继承自算法基类C_AO,并拓展了专属的算法参数与核心方法。构造函数与析构函数:分别用于初始化算法参数和释放程序资源。主要参数:
- popSize —— 种群规模,即种群内的智能体总数量;
- tau —— 幂律分布指数,用于控制单元筛选概率;
- greedyStart —— 贪心初始化智能体的占比;
- eliteUpdate —— 单次迭代中参与更新的智能体占比。
- SetParams —— 从参数数组中提取当前参数值并使其生效;
- Init —— 结合参数取值范围与迭代总轮次,完成种群初始化;
- Moving —— 执行单步算法运算,更新种群整体状态;
- Revision —— 更新评估结果,或为下一阶段运算做前置准备。
- RankedComponent —— 用于存储智能体各组件按质量(适应度)排序结果的结构体;
- RankedAgent —— 用于存储智能体按整体适应度排序结果的结构体。
- compRanks与agentRanks —— 存储排序后组件和智能体数据的结构体数组;
- tau、greedyStart、eliteUpdate —— 算法运行行为的核心控制参数。
- ApplyExtremalOptimization —— 基于幂律分布、按元素排名筛选并更新取值的核心极值优化机制;
- CalculateComponentFitness —— 计算智能体单个组件的适应度;
- SelectRankByPowerLaw —— 基于幂律分布选取组件或智能体的排名,实现对最差单元的定向优化。
该类专为极值优化算法设计,尤其适用于通过逐步替换种群个体中适应度最低的组件,来搜索高质量解的优化场景。
//———————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_EO::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ ArrayResize (compRanks, coords); ArrayResize (agentRanks, popSize); return true; } //————————————————————————————————————————————————————————————————————
EO类的初始化方法负责为后续算法运行准备初始条件。该方法接收各变量的取值范围(最小值、最大值)、变量更新步长,以及可选的迭代次数(轮次)作为参数。
方法首先调用标准初始化流程,完成基础参数配置与通用数据结构的准备。如果该流程执行失败,则初始化直接终止。
如果初始化成功,方法将重新分配用于存储排序后组件与排序后智能体的数组容量。组件数组的大小根据问题的变量个数或坐标维度设定,智能体数组的大小则根据指定的种群规模设定。上述步骤完成后,方法返回初始化成功的结果。
//———————————————————————————————————————————————————————————————————— //--- Main loop of the algorithm void C_AO_EO::Moving () { // Initial population setup if (!revision) { int greedyCount = (int)(popSize * greedyStart); for (int i = 0; i < popSize; i++) { // Random initialization for the rest for (int c = 0; c < coords; c++) { a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; return; } // Apply Extremal Optimization --------------------------------- ApplyExtremalOptimization (); } //————————————————————————————————————————————————————————————————————
ApplyExtremalOptimization方法实现了用于更新智能体种群的EO算法主循环。其核心目的是依据EO算法原理,通过修改智能体的各组件,优化求解结果。算法每次迭代更新的个体数量,由eliteUpdate参数(更新的智能体比例)与种群规模共同计算得出。所有智能体按照适应度排序,算法采用由差到优的排序规则,以适配最大化寻优场景。再通过幂律概率分布筛选出需要更新的目标智能体。
对于选中的智能体,程序会对其所有组件进行排序。系统为每一个组件单独计算适应度,以评判该组件对智能体整体性能的贡献程度,并按照组件适应度由差到优完成排序。算法再次利用幂律概率分布,随机选取一个智能体组件进行更新,该机制能够优先选中表现最差的组件。
被选中的智能体组件将被替换为合法区间内的随机值,其上下限由rangeMin和rangeMax参数指定。更新后的数值会经过合法性校验,确保严格符合预设的取值边界与步长要求。该方法对所有待更新智能体循环执行上述流程,实现了极值优化算法的核心逻辑:筛选系统中性能最差的组件并随机重置,持续提升整体解的适应度与优化效果。
//———————————————————————————————————————————————————————————————————— //--- Apply Extremal Optimization void C_AO_EO::ApplyExtremalOptimization () { // Number of agents to update in this iteration int numUpdates = MathMax (1, (int)(popSize * eliteUpdate)); // Update selected agents based on EO principle //for (int update = 0; update < numUpdates; update++) for (int update = 0; update < popSize; update++) { // Step 1: Select an agent to modify // Use ranking by overall fitness int targetAgent; // Rank agents by fitness (from worst to best for maximization) for (int i = 0; i < popSize; i++) { agentRanks [i].idx = i; agentRanks [i].fitness = a [i].f; } // Sort (worst first to maximize) for (int i = 0; i < popSize - 1; i++) { for (int j = i + 1; j < popSize; j++) { if (agentRanks [i].fitness > agentRanks [j].fitness) { RankedAgent temp = agentRanks [i]; agentRanks [i] = agentRanks [j]; agentRanks [j] = temp; } } } // Select an agent according to the power-law distribution int rank = SelectRankByPowerLaw (popSize); targetAgent = agentRanks [rank].idx; // Step 2: Rank the components of the selected agent for (int c = 0; c < coords; c++) { compRanks [c].agentIdx = targetAgent; compRanks [c].componentIdx = c; compRanks [c].fitness = CalculateComponentFitness (targetAgent, c); } // Sort components (worst first) for (int i = 0; i < coords - 1; i++) { for (int j = i + 1; j < coords; j++) { if (compRanks [i].fitness > compRanks [j].fitness) { RankedComponent temp = compRanks [i]; compRanks [i] = compRanks [j]; compRanks [j] = temp; } } } // Step 3: Select a component to change according to P(n) ∝ n^(-τ) int compRank = SelectRankByPowerLaw (coords); int compIdx = compRanks [compRank].componentIdx; // Step 4: Replace the selected component with a new random value // This is the key principle of EO - unconditional replacement with random a [targetAgent].c [compIdx] = u.RNDfromCI (rangeMin [compIdx], rangeMax [compIdx]); // Check boundaries a [targetAgent].c [compIdx] = u.SeInDiSp (a [targetAgent].c [compIdx], rangeMin [compIdx], rangeMax [compIdx], rangeStep [compIdx]); } } //————————————————————————————————————————————————————————————————————
CalculateComponentFitness方法用于计算智能体单个组件的适应度值。该函数在EO算法中起到关键作用,用于评估每个组件对整体解适应度的贡献程度。算法使用简单的指标,将组件“适应度”定义为其对整体适应度的相对贡献度。
首先,将“适应度”初始化为0。接下来,计算组件的取值区间长度(range)。如果区间长度大于0,则计算归一化偏差。偏差的计算方式为:当前组件值与基准值cB之差的绝对值,再除以组件的取值区间长度,完成归一化。适应度通过1.0减去该“偏差”得到。这使得“适应度”与偏差成反比:组件值与目标值的偏差越小,其“适应度”越高。
因此,该函数返回指定组件的适应度得分,用于反映当前组件值与目标值的匹配程度,为后续组件排序提供依据。
//———————————————————————————————————————————————————————————————————— //--- Calculate the fitness component double C_AO_EO::CalculateComponentFitness (int agentIdx, int componentIdx) { // For the general optimization problem we use a simple metric // λi = relative contribution of the component to the fitness double fitness = 0.0; double range = rangeMax [componentIdx] - rangeMin [componentIdx]; if (range > 0) { // Normalized deviation double deviation = MathAbs (a [agentIdx].c [componentIdx] - cB [componentIdx]) / range; fitness =1.0 - deviation;// Invert so that bigger = better } return fitness; } //————————————————————————————————————————————————————————————————————
SelectRankByPowerLaw方法实现了基于幂律概率分布从列表中选取元素位次(排名)的功能。该方法是EO算法的核心,通过该方法完成智能体与待修改组件的筛选。方法接收参数maxRank,表示元素的最大排名。
该方法基于逆变换法生成服从幂律分布的随机值,分布公式为:P(n) ∝ n^(-τ),其中,n为元素排名; τ(tau)为控制幂律分布形态的参数。
根据参数"tau"的取值,分两种情况实现:
一般情况(τ ≠ 1.0):
- 计算归一化系数"norm",保证概率尺度正确;
- 通过u.RNDprobab()生成区间[0, 1)内的随机数r;
- 利用逆变换公式,根据"r"、"norm"和"τ"计算排名"rank";
- 将排名限制在[0, maxRank - 1]范围内,防止数组越界。
特殊情况(τ = 1.0):
- 为τ = 1.0场景单独计算归一化系数"norm";
- 使用专用公式计算排名"rank";
- 将排名限制在[0, maxRank - 1]范围内。
最终,该方法返回一个整数类型的排名"rank",表示元素在排序列表中被选中的位置。该选取过程是概率性的:依据EO算法原理,排名越低(表现越差)的元素被选中的概率越高。
//———————————————————————————————————————————————————————————————————— //--- Select a rank according to a power-law distribution int C_AO_EO::SelectRankByPowerLaw (int maxRank) { // P(n) ∝ n^(-τ), where n is a rank from 1 to maxRank // Use the inverse transformation method double r = u.RNDprobab (); if (tau != 1.0) { // General case: inverse transform for P(n) ∝ n^(-τ) double norm = (1.0 - MathPow (maxRank + 1.0, 1.0 - tau)) / (1.0 - tau); double x = r * norm; int rank = (int)MathPow ((1.0 - tau) * x + 1.0, 1.0 / (1.0 - tau)) - 1; if (rank >= maxRank) rank = maxRank - 1; if (rank < 0) rank = 0; return rank; } else { // Special case τ = 1: P(n) ∝ 1/n double norm = MathLog (maxRank + 1.0); int rank = (int)(MathExp (r * norm) - 1.0); if (rank >= maxRank) rank = maxRank - 1; if (rank < 0) rank = 0; return rank; } } //————————————————————————————————————————————————————————————————————
"Revision"方法用于更新EO算法运行过程中当前最优解的相关信息。其作用是确定当前最优解,并更新存储该最优解信息的全局变量。方法首先创建临时数组aT用于存放排序后的智能体。通过内置函数u.Sorting对智能体种群a进行排序。排序采用最大化目标规则,即适应度函数值f最高的最优智能体,会被排在数组a的最前端。该数组是核心数据结构,存储了所有智能体信息,包括其组件c的取值与适应度函数值f。
排序完成后,程序会判断最优智能体(即a[0])的适应度值f,是否大于当前全局最优值fB(保存迄今找到的最优解适应度)。如果当前智能体优于全局最优解,则将该智能体的组件值a[0].c复制到cB数组(存储最优解的组件),并将fB更新为该智能体的适应度值a[0].f。
因此,Revision函数会更新最优解信息,保存其组件与适应度值,并对种群进行排序,使最优解始终位于数组起始位置。
//———————————————————————————————————————————————————————————————————— //--- Update the best solutions void C_AO_EO::Revision () { // Sort the population for MAXIMIZATION static S_AO_Agent aT []; ArrayResize (aT, popSize); // Use the built-in sorting function u.Sorting (a, aT, popSize); // Update the global best solution if (a [0].f > fB) { ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY); fB = a [0].f; } } //————————————————————————————————————————————————————————————————————
测试结果
测试后,该算法效果并不理想,因此我决定重新审视所有原始方法,并推出一个旨在提升算法收敛速度的改进版本。EO|极值优化(Boettcher-Percus)|50.0|1.4|0.5|0.3|
=============================
5 Hilly's;函数运行次数:10000;结果:0.5146369337195529
25 Hilly's;函数运行次数:10000;结果:0.29089804555433085
500 Hilly's;函数运行次数:10000;结果:0.25192095557138877
=============================
5 Forest's;函数运行次数:10000;结果:0.367128650332966
25 Forest's;函数运行次数:10000;结果:0.19477408852361866
500 Forest's;函数运行次数:10000;结果:0.15367465708144543
=============================
5 Megacity's;函数运行次数:10000;结果:0.2584615384615384
25 Megacity's;函数运行次数:10000;结果:0.12707692307692314
500 Megacity's;函数运行次数:10000;结果:0.09413846153846227
=============================
总分:2.25271 (25.03%)
我对该优化方法的个人解读如下:我们实现一个继承自基础优化类的新类。
构造函数用于设置算法初始参数:种群规模、需要增强(优化)的最差解数量、变异概率,以及用于选择与变异操作的分布指数。所有参数均存储在结构体数组中,便于后续修改与自定义配置。
SetParams方法根据参数数组中的数值更新类的内部变量。类中声明了以下方法:算法初始化;执行迭代步进(更新状态);结果校验(重新评估解或为下一次迭代做准备)。类的私有部分存储用于跟踪当前迭代轮次与总迭代轮次的变量。同时定义一个方法,对种群中某个解的指定组件执行变异,这是本算法实现的核心部分。
//———————————————————————————————————————————————————————————————————— class C_AO_EOm : public C_AO { public: //---------------------------------------------------------- ~C_AO_EOm () { } C_AO_EOm () { ao_name = "EOm"; ao_desc = "Extremal Optimization M"; ao_link = "https://www.mql5.com/en/articles/18755"; popSize = 50; // Population size popRaising = 3; // Boost the worst agents mutationRate = 0.1; // Mutation probability powCh = 2.0; // Selection power-law exponent powMut = 8.0; // Mutation power-law exponent ArrayResize (params, 5); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "popRaising"; params [1].val = popRaising; params [2].name = "mutationRate"; params [2].val = mutationRate; params [3].name = "powCh"; params [3].val = powCh; params [4].name = "powMut"; params [4].val = powMut; } void SetParams () { popSize = (int)params [0].val; popRaising = (int)params [1].val; mutationRate = params [2].val; powCh = params [3].val; powMut = params [4].val; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0); void Moving (); void Revision (); //------------------------------------------------------------------ int popRaising; // Boost the worst agents double mutationRate; // Mutation probability double powCh; // Selection power-law exponent double powMut; // Mutation power-law exponent private: //--------------------------------------------------------- int currentEpoch; // current epoch int totalEpochs; // total number of epochs void MutateComponent (int agentIdx, int componentIdx); }; //————————————————————————————————————————————————————————————————————
通过Init方法完成算法的初始化工作。该函数的输入参数包括:优化参数的最小值数组、最大值数组、步长数组,以及算法运行的总迭代轮次(总循环数)。
方法内部会首先调用基类C_AO的通用初始化函数StandardInit,该函数执行全局初始化、内存分配,并配置优化参数的取值范围与迭代步长等基础配置。
如果StandardInit执行失败,Init函数将直接终止并返回false。如果StandardInit执行成功,该方法会初始化迭代计数变量:将当前迭代轮次(currentEpoch)置为 0,将总迭代轮次(totalEpochs)赋值为epochsP参数值。
最终,方法返回true,代表初始化完成。该方法通过配置初始参数与迭代计数器,完成算法运行前的全部准备工作。
//———————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_EOm::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ currentEpoch = 0; totalEpochs = epochsP; return true; } //————————————————————————————————————————————————————————————————————
Moving方法是极值优化算法的主迭代循环,负责在寻优过程中驱动解种群的迭代演化。迭代轮次自增: 更新当前迭代计数器currentEpoch。种群初始化(仅首轮迭代执行):如果校验标识revision为false(即算法首次迭代),则执行种群初始化。
对种群中的每个“智能体”(解)、以及解的每个“坐标”参数,系统会在该坐标预设区间(rangeMin, rangeMax)内生成随机值。同时调用SeInDiSp离散化函数,保证参数取值严格匹配预设的步长精度。初始化完成后,将"revision"设置为true,确保该初始化逻辑仅执行一次。该方法终止。
后续迭代轮次的极值优化:创建临时数组aT,以存储变异后的新解。遍历种群中的所有智能体,初始化临时结构体aT[i]。针对解的每一维坐标,生成随机数并进行powCh幂次运算,用于筛选“父代”。
对随机数做缩放处理,以此在ind种群中选出父代索引。判定变异类型:如果随机数小于变异概率mutationRate,则触发变异操作,依据幂律分布PowerDistribution对父代坐标值执行变异更新;如果不触发变异,则执行朝向最优解的“定向迭代”,并叠加随机噪声。新坐标值计算方式为父代坐标值、随机扰动值以及全局最优坐标cB [c] 与父代坐标值差值的总和。再通过SeInDiSp完成数值离散化。所有变异运算结束后,将临时数组aT中的全部新解批量拷贝至主种群数组a。
因此,该方法实现了算法的演化循环:每一轮迭代都会通过个体筛选、随机幂律变异和定向搜索机制更新解的状态,持续优化目标函数值(最小化或最大化),最终得到全局最优解。
//———————————————————————————————————————————————————————————————————— //--- Main loop of the algorithm void C_AO_EOm::Moving () { currentEpoch++; // Initial population setup if (!revision) { for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; return; } //Apply Extremal Optimization--------------------------------------- static S_AO_Agent aT []; ArrayResize (aT, popSize); for (int i = 0; i < popSize; i++) { aT [i].Init (coords); for (int c = 0; c < coords; c++) { double rnd = u.RNDprobab (); rnd = pow (rnd, powCh); int ind = (int)u.Scale (rnd, 0.0, 1.0, 0, popSize - 1); // Select the mutation type double mutType = u.RNDprobab (); if (mutType < mutationRate) { aT [i].c [c] = u.PowerDistribution (a [ind].c [c], rangeMin [c], rangeMax [c], powMut); } else { // Directed movement towards the better with noise aT [i].c [c] = a [ind].c [c] + u.RNDprobab () * (cB [c] - a [ind].c [c]); } // Check boundaries aT [i].c [c] = u.SeInDiSp (aT [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } for (int i = 0; i < popSize; i++) ArrayCopy (a [i].c, aT [i].c); } //————————————————————————————————————————————————————————————————————
该方法负责在算法运行过程中,实时更新最优解的相关信息。
种群排序:创建临时数组aT,用于拷贝存储种群数据。通过内置方法u.Sorting,依据目标函数值对主种群数组a进行排序。
更新全局最优解:校验种群首个元素(当前最优解)的目标函数值f是否优于当前全局最优值fB。 如果当前解更优,则将首个元素的坐标a[0].c赋值给全局最优坐标cB,同时将该更优解的目标函数值更新至fB。
更新最差解:将种群末尾元素的目标函数值,更新至全局最差值变量fW。
最差解优化提升:循环执行popRaising次最差解优化操作,对种群中的最差个体进行改良:选取排序后种群数组末尾的popRaising个最差智能体,将其目标函数值f重新随机赋值,取值区间介于当前全局最差值fW与全局最优值fB之间。该操作旨在增加种群多样性,同时推动劣质解向优质解区间靠拢。
种群二次重排:使用u.Sorting方法对种群数组a重新排序。在个体目标值f更新后,需要重新排序恢复种群的有序结构。
//———————————————————————————————————————————————————————————————————— //--- Update the best solutions void C_AO_EOm::Revision () { // Sort the population -------------------------------------------- static S_AO_Agent aT []; ArrayResize (aT, popSize); u.Sorting (a, aT, popSize); // Update the global best solution if (a [0].f > fB) { ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY); fB = a [0].f; } fW = a [popSize - 1].f; //------------------------------------------------------------------ for (int i = 0; i < popRaising; i++) { a [popSize - 1 - i].f = u.RNDfromCI (fW, fB); } u.Sorting (a, aT, popSize); } //————————————————————————————————————————————————————————————————————
现在,该算法(在一定程度上继承并改进了EO的理念),采用幂律分布来选择候选供体( donor),不再局限于只挑选表现较差的个体。
a [popSize - 1 - i].f = u.RNDfromCI (fW, fB);我新增了一项标准EO中没有的机制:算法会对表现最差的智能体进行“复活”操作,为其随机分配一个介于种群当前最差适应度fW与最优适应度fB之间的适应度值。基于幂律分布的变异是“幂律分布变异”思路的具体实现。向优定向移动(当mutType >= mutationRate时触发)作为附加机制,旨在加快算法的收敛速度。
现在来看一下改进版算法的运行结果。我们可以将以下结果纳入评测表中。
EO|极值优化改进版|50.0|3.0|0.1|2.0|8.0|
=============================
5 Hilly's;函数运行次数:10000;结果:0.7616651321732368
25 Hilly's;函数运行次数:10000;结果:0.7724295992586084
500 Hilly's;函数运行次数:10000;结果:0.3174703398632668
=============================
5 Forest's;函数运行次数:10000;结果:0.9999936711143977
25 Forest's;函数运行次数:10000;结果:0.7675163269928252
500 Forest's;函数运行次数:10000;结果:0.23527203643380376
=============================
5 Megacity's;函数运行次数:10000;结果:0.7476923076923077
25 Megacity's;函数运行次数:10000;结果:0.5396923076923076
500 Megacity's;函数运行次数:10000;结果:0.14249230769230903
=============================
总分:5.28422 (58.71%)
可视化结果显示,低维函数对应的测试结果离散度较大(绿色线条)。

EOm在Hilly测试函数上

EOm在Forest测试函数上

EOm在Megacity测试函数上
测试结果显示,EOm算法在本次排名表中位列第12位。
| # | AO | 描述 | Hilly | Hilly 最终 | Forest | Forest 最终 | Megacity (离散) | Megacity 最终 | 最终 结果 | % of 最大 | ||||||
| 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | ||||||||
| 1 | ANS | 跨邻域搜索 | 0.94948 | 0.84776 | 0.43857 | 2.23581 | 1.00000 | 0.92334 | 0.39988 | 2.32323 | 0.70923 | 0.63477 | 0.23091 | 1.57491 | 6.134 | 68.15 |
| 2 | CLA | 密码锁算法(joo) | 0.95345 | 0.87107 | 0.37590 | 2.20042 | 0.98942 | 0.91709 | 0.31642 | 2.22294 | 0.79692 | 0.69385 | 0.19303 | 1.68380 | 6.107 | 67.86 |
| 3 | AMOm | 动物迁徙优化M | 0.90358 | 0.84317 | 0.46284 | 2.20959 | 0.99001 | 0.92436 | 0.46598 | 2.38034 | 0.56769 | 0.59132 | 0.23773 | 1.39675 | 5.987 | 66.52 |
| 4 | (P+O)ES | (P+O) 进化策略 | 0.92256 | 0.88101 | 0.40021 | 2.20379 | 0.97750 | 0.87490 | 0.31945 | 2.17185 | 0.67385 | 0.62985 | 0.18634 | 1.49003 | 5.866 | 65.17 |
| 5 | CTA | 彗星尾算法(joo) | 0.95346 | 0.86319 | 0.27770 | 2.09435 | 0.99794 | 0.85740 | 0.33949 | 2.19484 | 0.88769 | 0.56431 | 0.10512 | 1.55712 | 5.846 | 64.96 |
| 6 | TETA | 时间演化旅行算法(joo) | 0.91362 | 0.82349 | 0.31990 | 2.05701 | 0.97096 | 0.89532 | 0.29324 | 2.15952 | 0.73462 | 0.68569 | 0.16021 | 1.58052 | 5.797 | 64.41 |
| 7 | SDSm | 随机扩散搜索 M | 0.93066 | 0.85445 | 0.39476 | 2.17988 | 0.99983 | 0.89244 | 0.19619 | 2.08846 | 0.72333 | 0.61100 | 0.10670 | 1.44103 | 5.709 | 63.44 |
| 8 | BOAm | 台球优化算法M | 0.95757 | 0.82599 | 0.25235 | 2.03590 | 1.00000 | 0.90036 | 0.30502 | 2.20538 | 0.73538 | 0.52523 | 0.09563 | 1.35625 | 5.598 | 62.19 |
| 9 | AAm | 射箭算法M | 0.91744 | 0.70876 | 0.42160 | 2.04780 | 0.92527 | 0.75802 | 0.35328 | 2.03657 | 0.67385 | 0.55200 | 0.23738 | 1.46323 | 5.548 | 61.64 |
| 10 | ESG | 社会群体的进化(joo) | 0.99906 | 0.79654 | 0.35056 | 2.14616 | 1.00000 | 0.82863 | 0.13102 | 1.95965 | 0.82333 | 0.55300 | 0.04725 | 1.42358 | 5.529 | 61.44 |
| 11 | SIA | 模拟各向同性退火(joo) | 0.95784 | 0.84264 | 0.41465 | 2.21513 | 0.98239 | 0.79586 | 0.20507 | 1.98332 | 0.68667 | 0.49300 | 0.09053 | 1.27020 | 5.469 | 60.76 |
| 12 | EOm | 极值优化M | 0.76166 | 0.77242 | 0.31747 | 1.85155 | 0.99999 | 0.76751 | 0.23527 | 2.00277 | 0.74769 | 0.53969 | 0.14249 | 1.42987 | 5.284 | 58.71 |
| 13 | BBO | 生物地理学优化算法 | 0.94912 | 0.69456 | 0.35031 | 1.99399 | 0.93820 | 0.67365 | 0.25682 | 1.86867 | 0.74615 | 0.48277 | 0.17369 | 1.40261 | 5.265 | 58.50 |
| 14 | ACS | 人工协同搜索 | 0.75547 | 0.74744 | 0.30407 | 1.80698 | 1.00000 | 0.88861 | 0.22413 | 2.11274 | 0.69077 | 0.48185 | 0.13322 | 1.30583 | 5.226 | 58.06 |
| 15 | DA | 辩证算法 | 0.86183 | 0.70033 | 0.33724 | 1.89940 | 0.98163 | 0.72772 | 0.28718 | 1.99653 | 0.70308 | 0.45292 | 0.16367 | 1.31967 | 5.216 | 57.95 |
| 16 | BHAm | 黑洞算法M | 0.75236 | 0.76675 | 0.34583 | 1.86493 | 0.93593 | 0.80152 | 0.27177 | 2.00923 | 0.65077 | 0.51646 | 0.15472 | 1.32195 | 5.196 | 57.73 |
| 17 | ASO | 无序社会优化 | 0.84872 | 0.74646 | 0.31465 | 1.90983 | 0.96148 | 0.79150 | 0.23803 | 1.99101 | 0.57077 | 0.54062 | 0.16614 | 1.27752 | 5.178 | 57.54 |
| 18 | RFO | 皇家同花顺优化(joo) | 0.83361 | 0.73742 | 0.34629 | 1.91733 | 0.89424 | 0.73824 | 0.24098 | 1.87346 | 0.63154 | 0.50292 | 0.16421 | 1.29867 | 5.089 | 56.55 |
| 19 | AOSm | 原子轨道搜索M | 0.80232 | 0.70449 | 0.31021 | 1.81702 | 0.85660 | 0.69451 | 0.21996 | 1.77107 | 0.74615 | 0.52862 | 0.14358 | 1.41835 | 5.006 | 55.63 |
| 20 | TSEA | 龟壳演化算法(joo) | 0.96798 | 0.64480 | 0.29672 | 1.90949 | 0.99449 | 0.61981 | 0.22708 | 1.84139 | 0.69077 | 0.42646 | 0.13598 | 1.25322 | 5.004 | 55.60 |
| 21 | BSA | 回溯搜索算法 | 0.97309 | 0.54534 | 0.29098 | 1.80941 | 0.99999 | 0.58543 | 0.21747 | 1.80289 | 0.84769 | 0.36953 | 0.12978 | 1.34700 | 4.959 | 55.10 |
| 22 | DE | 差分进化 | 0.95044 | 0.61674 | 0.30308 | 1.87026 | 0.95317 | 0.78896 | 0.16652 | 1.90865 | 0.78667 | 0.36033 | 0.02953 | 1.17653 | 4.955 | 55.06 |
| 23 | SRA | 成功餐饮经营者算法 (joo) | 0.96883 | 0.63455 | 0.29217 | 1.89555 | 0.94637 | 0.55506 | 0.19124 | 1.69267 | 0.74923 | 0.44031 | 0.12526 | 1.31480 | 4.903 | 54.48 |
| 24 | CRO | 化学反应优化 | 0.94629 | 0.66112 | 0.29853 | 1.90593 | 0.87906 | 0.58422 | 0.21146 | 1.67473 | 0.75846 | 0.42646 | 0.12686 | 1.31178 | 4.892 | 54.36 |
| 25 | BIO | 血液遗传优化算法(joo) | 0.81568 | 0.65336 | 0.30877 | 1.77781 | 0.89937 | 0.65319 | 0.21760 | 1.77016 | 0.67846 | 0.47631 | 0.13902 | 1.29378 | 4.842 | 53.80 |
| 26 | BSA | 鸟群算法 | 0.89306 | 0.64900 | 0.26250 | 1.80455 | 0.92420 | 0.71121 | 0.24939 | 1.88479 | 0.69385 | 0.32615 | 0.10012 | 1.12012 | 4.809 | 53.44 |
| 27 | DEA | 差分进化算法 | 0.75995 | 0.67572 | 0.34171 | 1.77738 | 0.89582 | 0.64223 | 0.23941 | 1.77746 | 0.61538 | 0.44031 | 0.15115 | 1.20684 | 4.762 | 52.91 |
| 28 | HS | 和声搜索 | 0.86509 | 0.68782 | 0.32527 | 1.87818 | 0.99999 | 0.68002 | 0.09590 | 1.77592 | 0.62000 | 0.42267 | 0.05458 | 1.09725 | 4.751 | 52.79 |
| 29 | SSG | 树苗播种和生长 | 0.77839 | 0.64925 | 0.39543 | 1.82308 | 0.85973 | 0.62467 | 0.17429 | 1.65869 | 0.64667 | 0.44133 | 0.10598 | 1.19398 | 4.676 | 51.95 |
| 30 | BCOm | 细菌趋化性优化算法M | 0.75953 | 0.62268 | 0.31483 | 1.69704 | 0.89378 | 0.61339 | 0.22542 | 1.73259 | 0.65385 | 0.42092 | 0.14435 | 1.21912 | 4.649 | 51.65 |
| 31 | ABO | 非洲水牛优化 | 0.83337 | 0.62247 | 0.29964 | 1.75548 | 0.92170 | 0.58618 | 0.19723 | 1.70511 | 0.61000 | 0.43154 | 0.13225 | 1.17378 | 4.634 | 51.49 |
| 32 | (PO)ES | (PO) 进化策略 | 0.79025 | 0.62647 | 0.42935 | 1.84606 | 0.87616 | 0.60943 | 0.19591 | 1.68151 | 0.59000 | 0.37933 | 0.11322 | 1.08255 | 4.610 | 51.22 |
| 33 | FBA | 基于分形的算法 | 0.79000 | 0.65134 | 0.28965 | 1.73099 | 0.87158 | 0.56823 | 0.18877 | 1.62858 | 0.61077 | 0.46062 | 0.12398 | 1.19537 | 4.555 | 50.61 |
| 34 | TSm | 禁忌搜索M | 0.87795 | 0.61431 | 0.29104 | 1.78330 | 0.92885 | 0.51844 | 0.19054 | 1.63783 | 0.61077 | 0.38215 | 0.12157 | 1.11449 | 4.536 | 50.40 |
| 35 | BSO | 头脑风暴优化 | 0.93736 | 0.57616 | 0.29688 | 1.81041 | 0.93131 | 0.55866 | 0.23537 | 1.72534 | 0.55231 | 0.29077 | 0.11914 | 0.96222 | 4.498 | 49.98 |
| 36 | WOAm | 鲸鱼优化算法M | 0.84521 | 0.56298 | 0.26263 | 1.67081 | 0.93100 | 0.52278 | 0.16365 | 1.61743 | 0.66308 | 0.41138 | 0.11357 | 1.18803 | 4.476 | 49.74 |
| 37 | AEFA | 人工电场算法 | 0.87700 | 0.61753 | 0.25235 | 1.74688 | 0.92729 | 0.72698 | 0.18064 | 1.83490 | 0.66615 | 0.11631 | 0.09508 | 0.87754 | 4.459 | 49.55 |
| 38 | AEO | 基于人工生态系统的优化算法 | 0.91380 | 0.46713 | 0.26470 | 1.64563 | 0.90223 | 0.43705 | 0.21400 | 1.55327 | 0.66154 | 0.30800 | 0.28563 | 1.25517 | 4.454 | 49.49 |
| 39 | CAm | 骆驼算法M | 0.78684 | 0.56042 | 0.35133 | 1.69859 | 0.82772 | 0.56041 | 0.24336 | 1.63149 | 0.64846 | 0.33092 | 0.13418 | 1.11356 | 4.444 | 49.37 |
| 40 | ACOm | 蚁群优化M | 0.88190 | 0.66127 | 0.30377 | 1.84693 | 0.85873 | 0.58680 | 0.15051 | 1.59604 | 0.59667 | 0.37333 | 0.02472 | 0.99472 | 4.438 | 49.31 |
| 41 | CMAES | 协方差矩阵自适应演化策略 | 0.76258 | 0.72089 | 0.00000 | 1.48347 | 0.82056 | 0.79616 | 0.00000 | 1.61672 | 0.75846 | 0.49077 | 0.00000 | 1.24923 | 4.349 | 48.33 |
| 42 | BFO-GA | 细菌觅食优化 - ga | 0.89150 | 0.55111 | 0.31529 | 1.75790 | 0.96982 | 0.39612 | 0.06305 | 1.42899 | 0.72667 | 0.27500 | 0.03525 | 1.03692 | 4.224 | 46.93 |
| 43 | SOA | 简单优化算法 | 0.91520 | 0.46976 | 0.27089 | 1.65585 | 0.89675 | 0.37401 | 0.16984 | 1.44060 | 0.69538 | 0.28031 | 0.10852 | 1.08422 | 4.181 | 46.45 |
| 44 | ABHA | 人工蜂巢算法 | 0.84131 | 0.54227 | 0.26304 | 1.64663 | 0.87858 | 0.47779 | 0.17181 | 1.52818 | 0.50923 | 0.33877 | 0.10397 | 0.95197 | 4.127 | 45.85 |
| 45 | ACMO | 大气云模型优化 | 0.90321 | 0.48546 | 0.30403 | 1.69270 | 0.80268 | 0.37857 | 0.19178 | 1.37303 | 0.62308 | 0.24400 | 0.10795 | 0.97503 | 4.041 | 44.90 |
| RW | 随机游走 | 0.48754 | 0.32159 | 0.25781 | 1.06694 | 0.37554 | 0.21944 | 0.15877 | 0.75375 | 0.27969 | 0.14917 | 0.09847 | 0.52734 | 2.348 | 26.09 | |
总结
本次提出的轻量化改进极值优化算法(EOm),为元启发式优化领域提供了一个具有参考价值的研究案例:适度脱离原有理论框架,反而能显著提升算法的实际性能。该算法在45种群智能优化算法中位列第12,本质上属于混合改进算法,仅保留了原始EO的幂律分布核心思想。
最差智能体“复活”机制能够有效防止算法早熟收敛,维持种群多样性,并为全局搜索提供更多可能性。简化计算结构:舍弃逐个组件适应度评估,降低了计算复杂度,并直接对完整解进行迭代更新,提升收敛速度。
本次改进型EO算法的优异表现,印证了元启发式算法设计的一条重要准则:算法性能的优劣,并不取决于其对原始理论的忠实程度,而是取决于其对搜索空间探索与开发能力的平衡。该实现方案虽然突破了传统极值优化算法的经典原理,却具备收敛速度快、求解效果具有竞争力、实现与配置简便等优势。
因此,该算法可作为种群优化算法体系中实用的工具,尤其适用于需要兼顾求解质量与计算成本的优化场景。

图例1. 算法在相应测试中的颜色渐变表示

图例2. 算法测试结果的直方图(评分范围为0到100,越高越好,其中100为理论上的最高可能得分,压缩包中附有用于计算排名表的脚本)
EOm算法的优缺点:
优点:
- 实现简单
- 快速而高效
- 在离散问题上表现良好
缺点:
- 在低维函数上的结果离散度较大
- 在“平滑型”低维问题上表现为平均水平
文章附有一个包含当前版本算法代码的归档文件。本文作者对标准算法描述的绝对准确性不承担责任。为提升搜索能力,已经对其中的许多算法进行了修改。文章中表述的结论和论断都是基于实验的结果。
文中所用的程序
| # | 名称 | 类型 | 描述 |
|---|---|---|---|
| 1 | #C_AO.mqh | 库 | 种群优化算法的基类 |
| 2 | #C_AO_enum.mqh | 库 | 种群优化算法的枚举说明 |
| 3 | TestFunctions.mqh | 库 | 测试函数库 |
| 4 | TestStandFunctions.mqh | 库 | 测试台函数库 |
| 5 | Utilities.mqh | 库 | 辅助函数库 |
| 6 | CalculationTestResults.mqh | 库 | 用于计算比较表结果的脚本 |
| 7 | Testing AOs.mq5 | 脚本 | 面向所有种群优化算法的统一测试平台 |
| 8 | Simple use of population optimization algorithms.mq5 | 脚本 | 种群优化算法非可视化简易使用案例 |
| 9 | Test_AO_EOm.mq5 | 脚本 | EOm测试 |
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/18755
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
您应该了解的MQL5向导技巧(第 75 部分):动量震荡指标与包络线的应用
在MQL5中自定义交易品种:创建三维K线自定义品种
新手在交易中的10个基本错误
市场模拟(第 23 部分):SQL 入门(六)