English Русский Español Português
preview
极值优化(EO)

极值优化(EO)

MetaTrader 5交易 |
29 0
Andrey Dik
Andrey Dik

内容

  1. 引言
  2. 算法实现
  3. 测试结果


引言

现实中的诸多问题(尤其是交易优化问题),其目标函数多为复杂离散空间形态,存在多重局部极值、不连续区间与不可导区域,因此传统基于梯度的优化方法并不适用。为解决此类问题,学界发展出了多种元启发式算法。各类算法在搜索空间的全局探索与局部利用的平衡上各有优劣。

极值优化(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模型的演化遵循间断平衡理论:系统长期保持稳态,偶尔被突发性剧变打破。极值优化算法复用了这一核心原理:仅对系统内劣质组件进行迭代更新。一个组件的改进可能显著提升整体解,但也可能使其他组件变成“较差”组件。

极值优化算法将原本应用于生态系统模型的自组织临界性原理,迁移至优化求解领域。其核心创新点在于:利用复杂系统与生俱来的自组织特性,自动平衡搜索空间的全局探索与局部利用。我们将通过实际工程问题验证该算法的有效性,算法伪代码流程如下。

初始化:

  1. 基于种群规模(popSize)生成智能体种群  
  2. 初始化参数
  3. 通过创建数组对智能体和组件排序

首次运行(初始化种群):

  1. 对于种群中的每个智能体
    • 遍历智能体的各个组件(坐标):
      • 在合法取值范围内随机赋值
      • 按需执行数据离散化处理

基本优化循环:

对种群内每个智能体执行以下操作:

  1. 智能体排序
    • 生成包含所有智能体及其适应度的列表
    • 按适应度排序(对于最大化的劣解优先排序)
  2. 选取目标智能体
    • 基于幂律分布P(n) ∝ n^(-τ)
    • 依据该分布随机选取排序位次
    • 将该位次对应的智能体确定为待优化目标智能体
  3. 目标智能体组件排序
    • 逐一计算目标智能体所有组件的适应度:
      • 适应度计算公式:适应度 = 1 - (相对最优值的归一化偏差)
    • 按组件适应度升序排序(劣质组件优先)
  4. 组件筛选与变异
    • 通过幂律分布随机选取待优化组件的排序位次
    • 为选中组件重新随机赋予合法数值
    • 执行边界校验,并按需离散化

结果更新:

  1. 依据适应度对全体种群排序(最优解置顶)
  2. 如果迭代得到更优解,则更新全局最优解

让我们通过代码实现该算法。我们将自定义极值优化算法类,基于优化最差单元、迭代寻优的核心逻辑实现EO算法。该类继承自算法基类C_AO,并拓展了专属的算法参数与核心方法。构造函数与析构函数:分别用于初始化算法参数和释放程序资源。主要参数:

  • popSize —— 种群规模,即种群内的智能体总数量;
  • tau —— 幂律分布指数,用于控制单元筛选概率;
  • greedyStart —— 贪心初始化智能体的占比;
  • eliteUpdate —— 单次迭代中参与更新的智能体占比。
参数数组params用于存储算法配置参数,同步原始参数数值,适配界面交互与外部程序调控需求。方法:
  • SetParams —— 从参数数组中提取当前参数值并使其生效;
  • Init —— 结合参数取值范围与迭代总轮次,完成种群初始化;
  • Moving  —— 执行单步算法运算,更新种群整体状态;
  • Revision —— 更新评估结果,或为下一阶段运算做前置准备。 
内部结构体:
  • RankedComponent —— 用于存储智能体各组件按质量(适应度)排序结果的结构体;
  • RankedAgent —— 用于存储智能体按整体适应度排序结果的结构体。
内部变量与参数:
  • compRanksagentRanks —— 存储排序后组件和智能体数据的结构体数组;
  • taugreedyStarteliteUpdate —— 算法运行行为的核心控制参数。
内部辅助方法:
  • 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. 一般情况(τ ≠ 1.0):

      • 计算归一化系数"norm",保证概率尺度正确;
      • 通过u.RNDprobab()生成区间[0, 1)内的随机数r;
      • 利用逆变换公式,根据"r"、"norm"和"τ"计算排名"rank";
      • 将排名限制在[0, maxRank - 1]范围内,防止数组越界。
    2. 特殊情况(τ = 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%)

    可视化结果显示,低维函数对应的测试结果离散度较大(绿色线条)。

    Hilly

    EOm在Hilly测试函数上

    Forest

    EOm在Forest测试函数上

    Megacity

    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)
    1ANS跨邻域搜索0.949480.847760.438572.235811.000000.923340.399882.323230.709230.634770.230911.574916.13468.15
    2CLA密码锁算法(joo)0.953450.871070.375902.200420.989420.917090.316422.222940.796920.693850.193031.683806.10767.86
    3AMOm动物迁徙优化M0.903580.843170.462842.209590.990010.924360.465982.380340.567690.591320.237731.396755.98766.52
    4(P+O)ES(P+O) 进化策略0.922560.881010.400212.203790.977500.874900.319452.171850.673850.629850.186341.490035.86665.17
    5CTA彗星尾算法(joo)0.953460.863190.277702.094350.997940.857400.339492.194840.887690.564310.105121.557125.84664.96
    6TETA时间演化旅行算法(joo)0.913620.823490.319902.057010.970960.895320.293242.159520.734620.685690.160211.580525.79764.41
    7SDSm随机扩散搜索 M0.930660.854450.394762.179880.999830.892440.196192.088460.723330.611000.106701.441035.70963.44
    8BOAm台球优化算法M0.957570.825990.252352.035901.000000.900360.305022.205380.735380.525230.095631.356255.59862.19
    9AAm射箭算法M0.917440.708760.421602.047800.925270.758020.353282.036570.673850.552000.237381.463235.54861.64
    10ESG社会群体的进化(joo)0.999060.796540.350562.146161.000000.828630.131021.959650.823330.553000.047251.423585.52961.44
    11SIA模拟各向同性退火(joo)0.957840.842640.414652.215130.982390.795860.205071.983320.686670.493000.090531.270205.46960.76
    12EOm极值优化M0.761660.772420.317471.851550.999990.767510.235272.002770.747690.539690.142491.429875.28458.71
    13BBO生物地理学优化算法0.949120.694560.350311.993990.938200.673650.256821.868670.746150.482770.173691.402615.26558.50
    14ACS人工协同搜索0.755470.747440.304071.806981.000000.888610.224132.112740.690770.481850.133221.305835.22658.06
    15DA辩证算法0.861830.700330.337241.899400.981630.727720.287181.996530.703080.452920.163671.319675.21657.95
    16BHAm黑洞算法M0.752360.766750.345831.864930.935930.801520.271772.009230.650770.516460.154721.321955.19657.73
    17ASO无序社会优化0.848720.746460.314651.909830.961480.791500.238031.991010.570770.540620.166141.277525.17857.54
    18RFO皇家同花顺优化(joo)0.833610.737420.346291.917330.894240.738240.240981.873460.631540.502920.164211.298675.08956.55
    19AOSm原子轨道搜索M0.802320.704490.310211.817020.856600.694510.219961.771070.746150.528620.143581.418355.00655.63
    20TSEA龟壳演化算法(joo)0.967980.644800.296721.909490.994490.619810.227081.841390.690770.426460.135981.253225.00455.60
    21BSA回溯搜索算法0.973090.545340.290981.809410.999990.585430.217471.802890.847690.369530.129781.347004.95955.10
    22DE差分进化0.950440.616740.303081.870260.953170.788960.166521.908650.786670.360330.029531.176534.95555.06
    23SRA成功餐饮经营者算法 (joo)0.968830.634550.292171.895550.946370.555060.191241.692670.749230.440310.125261.314804.90354.48
    24CRO化学反应优化0.946290.661120.298531.905930.879060.584220.211461.674730.758460.426460.126861.311784.89254.36
    25BIO血液遗传优化算法(joo)0.815680.653360.308771.777810.899370.653190.217601.770160.678460.476310.139021.293784.84253.80
    26BSA鸟群算法0.893060.649000.262501.804550.924200.711210.249391.884790.693850.326150.100121.120124.80953.44
    27DEA差分进化算法0.759950.675720.341711.777380.895820.642230.239411.777460.615380.440310.151151.206844.76252.91
    28HS和声搜索0.865090.687820.325271.878180.999990.680020.095901.775920.620000.422670.054581.097254.75152.79
    29SSG树苗播种和生长0.778390.649250.395431.823080.859730.624670.174291.658690.646670.441330.105981.193984.67651.95
    30BCOm细菌趋化性优化算法M0.759530.622680.314831.697040.893780.613390.225421.732590.653850.420920.144351.219124.64951.65
    31ABO非洲水牛优化0.833370.622470.299641.755480.921700.586180.197231.705110.610000.431540.132251.173784.63451.49
    32(PO)ES(PO) 进化策略0.790250.626470.429351.846060.876160.609430.195911.681510.590000.379330.113221.082554.61051.22
    33FBA基于分形的算法0.790000.651340.289651.730990.871580.568230.188771.628580.610770.460620.123981.195374.55550.61
    34TSm禁忌搜索M0.877950.614310.291041.783300.928850.518440.190541.637830.610770.382150.121571.114494.53650.40
    35BSO头脑风暴优化0.937360.576160.296881.810410.931310.558660.235371.725340.552310.290770.119140.962224.49849.98
    36WOAm鲸鱼优化算法M0.845210.562980.262631.670810.931000.522780.163651.617430.663080.411380.113571.188034.47649.74
    37AEFA人工电场算法0.877000.617530.252351.746880.927290.726980.180641.834900.666150.116310.095080.877544.45949.55
    38AEO基于人工生态系统的优化算法0.913800.467130.264701.645630.902230.437050.214001.553270.661540.308000.285631.255174.45449.49
    39CAm骆驼算法M0.786840.560420.351331.698590.827720.560410.243361.631490.648460.330920.134181.113564.44449.37
    40ACOm蚁群优化M0.881900.661270.303771.846930.858730.586800.150511.596040.596670.373330.024720.994724.43849.31
    41CMAES协方差矩阵自适应演化策略0.762580.720890.000001.483470.820560.796160.000001.616720.758460.490770.000001.249234.34948.33
    42BFO-GA细菌觅食优化 - ga0.891500.551110.315291.757900.969820.396120.063051.428990.726670.275000.035251.036924.22446.93
    43SOA简单优化算法0.915200.469760.270891.655850.896750.374010.169841.440600.695380.280310.108521.084224.18146.45
    44ABHA人工蜂巢算法0.841310.542270.263041.646630.878580.477790.171811.528180.509230.338770.103970.951974.12745.85
    45ACMO大气云模型优化0.903210.485460.304031.692700.802680.378570.191781.373030.623080.244000.107950.975034.04144.90
    RW随机游走0.487540.321590.257811.066940.375540.219440.158770.753750.279690.149170.098470.527342.34826.09


    总结

    本次提出的轻量化改进极值优化算法(EOm),为元启发式优化领域提供了一个具有参考价值的研究案例:适度脱离原有理论框架,反而能显著提升算法的实际性能。该算法在45种群智能优化算法中位列第12,本质上属于混合改进算法,仅保留了原始EO的幂律分布核心思想。 

    最差智能体“复活”机制能够有效防止算法早熟收敛,维持种群多样性,并为全局搜索提供更多可能性。简化计算结构:舍弃逐个组件适应度评估,降低了计算复杂度,并直接对完整解进行迭代更新,提升收敛速度。

    本次改进型EO算法的优异表现,印证了元启发式算法设计的一条重要准则:算法性能的优劣,并不取决于其对原始理论的忠实程度,而是取决于其对搜索空间探索与开发能力的平衡。该实现方案虽然突破了传统极值优化算法的经典原理,却具备收敛速度快、求解效果具有竞争力、实现与配置简便等优势。

    因此,该算法可作为种群优化算法体系中实用的工具,尤其适用于需要兼顾求解质量与计算成本的优化场景。

    tab

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

    tab

    图例2. 算法测试结果的直方图(评分范围为0到100,越高越好,其中100为理论上的最高可能得分,压缩包中附有用于计算排名表的脚本)

    EOm算法的优缺点:

    优点:

    1. 实现简单
    2. 快速而高效
    3. 在离散问题上表现良好

    缺点:

    1. 在低维函数上的结果离散度较大
    2. 在“平滑型”低维问题上表现为平均水平

    文章附有一个包含当前版本算法代码的归档文件。本文作者对标准算法描述的绝对准确性不承担责任。为提升搜索能力,已经对其中的许多算法进行了修改。文章中表述的结论和论断都是基于实验的结果。


    文中所用的程序

    #名称类型描述
    1#C_AO.mqh

    种群优化算法的基类
    2#C_AO_enum.mqh

    种群优化算法的枚举说明
    3TestFunctions.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

    附加的文件 |
    EOm.zip (247.28 KB)
    您应该了解的MQL5向导技巧(第 75 部分):动量震荡指标与包络线的应用 您应该了解的MQL5向导技巧(第 75 部分):动量震荡指标与包络线的应用
    比尔・威廉姆斯的动量震荡指标(AO)与包络线通道是一对可以在 MQL5 智能交易系统(EA)中互补使用的指标组合。我们利用动量震荡指标识别趋势的能力,同时引入包络线通道来定义支撑位和阻力位。在探索这组指标组合的过程中,我们使用 MQL5 向导来构建并测试二者可能具备的交易潜力。
    在MQL5中自定义交易品种:创建三维K线自定义品种 在MQL5中自定义交易品种:创建三维K线自定义品种
    本文详细讲解创新指标文件3DBarCustomSymbol.mq5的完整制作流程,该指标可在MetaTrader 5中生成自定义品种,将价格、时间、成交量与波动率整合为一体化三维可视化图形。我们会讲解其数学理论基础、系统架构、实操实现步骤,以及在交易策略中的实际应用方式。
    新手在交易中的10个基本错误 新手在交易中的10个基本错误
    新手在交易中会犯的10个基本错误: 在市场刚开始时交易, 获利时不适当地仓促, 在损失的时候追加投资, 从最好的仓位开始平仓, 翻本心理, 最优越的仓位, 用永远买进的规则进行交易, 在第一天就平掉获利的仓位,当发出建一个相反的仓位警示时平仓, 犹豫。
    市场模拟(第 23 部分):SQL 入门(六) 市场模拟(第 23 部分):SQL 入门(六)
    在本文中,我们将探讨如何可视化一个数据库,并借此了解其结构。这是通过分析数据库的内部结构来实现的。虽然乍一看这似乎没有必要,但如果我们真的想成为数据库管理员,那么这样做是完全合理的。毕竟,有些人以维护和设计数据库为生。