English Русский Español Deutsch 日本語 Português
preview
中心引力优化(CFO)算法

中心引力优化(CFO)算法

MetaTrader 5交易 |
34 0
Andrey Dik
Andrey Dik


目录

  1. 概述
  2. 算法的实现
  3. 测试结果


概述

大自然经过数十亿年的进化,创造了许多高效的优化机制,激励研究人员创造新的算法。引力是这些令人叹为观止的自然现象之一 —— 它是支配宇宙天体运动的基本力。我们已经多次分析过类似的算法...

中心引力优化(CFO)算法是将重力运动学原理应用于数值优化领域的尝试。这种元启发式算法基于确定性运动定律,模拟了多维解空间中虚拟粒子的相互作用,其中每个粒子在重力模拟的影响下倾向于目标函数值更好的区域。

CFO 基于一个简单直观的比喻:想象一组分布在可能解决方案空间中的试验点(探测器)。每个探测器都有一个“质量”,该质量与其所代表的解决方案的质量成正比 —— 即目标函数的值。正如巨大的天体吸引质量较小的天体一样,具有最佳解决方案的探测器也会产生一个虚拟引力场,吸引其他探测器。

每个探测器的运动都遵循类似于牛顿定律的规律,加速度由其他探测器的总吸引力决定,位移则根据运动学方程发生。该算法的一个重要特点是其确定性 —— 与许多其他元启发式方法不同,CFO 在其核心运行循环中不使用随机变量。


算法的实现

CFO 算法的历史始于研究人员的思考:如果将物理定律应用于寻找最优解会怎样?想象一下,在一个广阔的丘陵地区,每个点的高度对应于解决方案的质量。山越高,解决方案越好。我们的目标是找到最高点,但问题是我们无法一次看到整个景观。相反,我们有一组研究人员(样本),能够在该区域内移动并报告其当前位置的海拔高度。

一开始,我们的探测器随机分布在整个区域。有些探测器最终流落到低地,另一些在山坡上,有些可能足够幸运,可以直接到达小山顶。每个探测器都有自己的“重量”,与它所在位置的高度成正比。点越高,探测器“越重”。现在最有趣的部分开始了 —— 我们的探测器并非随机游荡,而是遵循万有引力定律。想象一下,“较重的”探测器(找到最佳点的探测器)会吸引“较轻的”探测器(处于最差位置的探测器)。此外,这种吸引力是单向的:好的决策会吸引坏的决策,但反之则不然。

引力是根据类似于牛顿万有引力定律的规则计算的。这取决于探测器之间的“重量”差异(解决方案质量的差异)和它们之间的距离。具有高适应度函数值的探测器强烈吸引具有低值的附近探测器,但对远处样本的影响很小。在这些力的影响下,每个探测器都加速并开始移动。小的、“轻的”探测器像球从山坡上滚到山顶一样,迅速冲向“更重的”探测器。在算法的每一步中,探测器都会重新计算吸引力并调整其运动。如果探测器试图超出已确定的搜索区域边界,则会触发反射机制 —— 想象一下,区域边缘有一堵墙,探测器会从墙边反弹回允许的区域。

随着时间的推移,探测器开始聚集在地形的高地周围。它们大多集中在最有希望的区域周围,并且随着每一次迭代,它们都能更准确地确定峰值的位置。理想情况下,如果给算法足够的时间,所有探测结果都应该收敛到全局最大值附近 —— 即整个地形的最高点。

CFO 的特殊之处在于它本质上是一个确定性算法 —— 如果你用相同的初始探测器分布运行两次,它将给出相同的结果。这使其有别于许多其他依赖随机性的元启发式算法。 

CFO 算法

图1.中心引力优化算法的流程图 

图 1 显示了中心引力优化 (CFO) 算法在搜索空间中的运行原理。图中显示了目标函数景观,颜色从蓝色(低值)到黄色(高值)渐变。全局最大值(最高点)和局部最大值(最低峰值)。三个探测器(搜索代理)分别被指定为探测器 1、探测器 2 和探测器 3。吸引力(红色箭头)显示了较高点如何吸引探测器。 这是 CFO 的核心概念 —— 最好的决策会吸引最差的决策,但反之则不然。虚线表示样本达到最大值的轨迹。该过程的数学方程式包括:

引力计算:对于任意两个探测器 i 和 j,如果探测器 j 的函数值(质量)大于探测器 i 的函数值,则探测器 j 对探测器 i 施加的引力按以下公式计算:F = g × [(m_j - m_i)^α / d^β] × direction,其中:
  • g — 万有引力常数
  • m_j 和 m_i — j 和 i 探测器的函数值(质量)
  • α — 质量指数(通常为 2)
  • d — 探测器之间的距离
  • β — 距离指数(通常为 2)
  • direction 是从探测器 i 指向探测器 j 的单位向量。
加速度计算:探测器 i 的加速度计算为作用在其上的所有力的总和。
位置更新:每个探测器的位置根据以下公式更新:x_new = x_old + 0.5 × a,其中:
  • x_new — 新位置
  • x_old — 当前位置
  • a 加速度
边界处理:如果样本超出搜索范围,则会将其退回。

    该算法迭代地将这些计算应用于所有样本,逐渐将它们移向搜索空间中的最优值。随着时间的推移,样本倾向于聚集在全局和局部最大值附近,通常在全局最优值附近观察到最高浓度。

    CFO 的独特性在于其确定性和单向吸引机制,它将研究引向最佳解决方案,而不涉及其基本形式的随机性。CFO 算法的伪代码:

    1. 初始化
      • 定义搜索空间的边界。
      • 我们设定了以下参数:样本数量、引力常数、质量和距离的指数、重新定位因子。
      • 创建给定数量的样本,并将其随机放置在搜索空间中。
      • 对于每个样本,计算目标函数的初始值。
    2. 主循环(重复指定的世代数):
      • 对于每个探测器:
        • 将加速度矢量重置为零。
        • 考虑其他探测器的影响:
          • 如果另一个样本具有更好的目标函数值(更大的“质量”),就会产生吸引力。
          • 计算探测器之间的距离。
          • 吸引力与“质量”差的 α 次方成正比,与距离的 β 次方成反比。
          • 力的方向是从当前探测器指向“较重”的探测器。
          • 将所有具有最佳函数值的探测器的影响相加。
      • 计算完所有加速度后,更新探测器的位置:
        • 新位置 = 原位置 + 一半加速度。
        • 如果探测器超出搜索空间,则进行重新定位:
          • 当超出下边界时:我们将探测器反射到空间中,同时考虑重新定位因子。
          • 当超过上限时:类似地,但方向相反。
      • 重新计算所有探测器在新位置的目标函数值。
      • 更新已找到的最佳解决方案信息。
    3. 完成
      • 返回找到的最佳解决方案(目标函数值最大的探测器的坐标)。

    接下来我们来编写算法代码,定义 S_CFO_Agent 结构,它继承自 S_AO_Agent,这意味着 S_CFO_Agent 接收 S_AO_Agent 中定义的所有属性和方法。 

    结构字段:a[] 动态数组,用于存储加速度值。Init() 方法用于初始化结构体,根据传递的 coords 参数调整 c 数组的大小,并根据 coords 调整 a 加速度数组的大小。这样就可以根据坐标的数量动态设置数组大小,在使用加速度数组之前将其所有元素的值清除,并将 f 变量的值设置为 double 类型的最小可能值,以初始化适应度函数,确保计算出的任何其他值都大于当前值。

    //——————————————————————————————————————————————————————————————————————————————
    //--- CFO probe structure
    struct S_CFO_Agent : public S_AO_Agent
    {
        double a []; // acceleration vector
    
        void Init (int coords)
        {
          ArrayResize (c, coords);   // coordinates
          ArrayResize (a, coords);   // acceleration
          ArrayInitialize (a, 0.0);  // reset accelerations
          f = -DBL_MAX;              // fitness function value
        }
    };
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_CFO 类继承自 C_AO 类,并定义 CFO 算法。构造函数和析构函数。在这种情况下,析构函数不执行任何特定操作。C_AO_CFO() 是一个构造函数,用于初始化算法参数。它设置各种变量的值,例如:

    • popSize、g、alpha、beta、initialFrep、finalFrep、noiseFactor 是与算法及其优化函数相关的参数。
    • frep 当前重定位因子,由 initialFrep 初始化。
    • params 数组已初始化。它将包含算法参数。之后,它们的值会被复制到一个具有相应名称的数组中。

    类方法:

    • SetParams() 通过从参数数组中获取值来设置算法参数。它还将当前重新定位因子设置为 initialFrep。
    • Init() 负责使用最小和最大参数值初始化算法,以及更改这些参数值的步骤。epochsP 参数指定算法执行的世代数。
    • Moving() 负责在算法中移动探测器(代理)。
    • Revision() 可用于审核或更新代理的状态。

    类字段:

    • S_CFO_Agent probe[] 用于优化的探测器(代理)数组。
    • epochs、epochNow 私有字段,表示 世代总数和当前世代。

    其他私有方法:

    • InitialDistribution() — 初始化搜索空间中代理的分布。
    • UpdateRepFactor() 根据系统的当前状态更新重定位因子。
    • CalculateAccelerations() 根据代理的位置和交互计算代理的加速度。
    • UpdatePositions() 用于根据代理的加速度更新其位置。
    • CalculateDistanceSquared() 用于计算空间中两点之间的距离并评估代理之间交互的方法。

    //——————————————————————————————————————————————————————————————————————————————
    //--- Main class of the CFO algorithm
    class C_AO_CFO : public C_AO
    {
      public: //--------------------------------------------------------------------
      ~C_AO_CFO () { }
      C_AO_CFO ()
      {
        ao_name = "CFO";
        ao_desc = "Central Force Optimization";
        ao_link = "https://www.mql5.com/en/articles/17167";
    
        popSize     = 30;          // number of probes
        g           = 1.0;         // gravitational constant
        alpha       = 0.1;         // mass power
        beta        = 0.1;         // degree for distance
        initialFrep = 0.9;         // initial repositioning factor
        finalFrep   = 0.1;         // final repositioning factor
        noiseFactor = 1.0;         // random noise factor
    
        frep        = initialFrep; // current repositioning factor
    
        ArrayResize (params, 7);
        params [0].name = "popSize";     params [0].val = popSize;
        params [1].name = "g";           params [1].val = g;
        params [2].name = "alpha";       params [2].val = alpha;
        params [3].name = "beta";        params [3].val = beta;
        params [4].name = "initialFrep"; params [4].val = initialFrep;
        params [5].name = "finalFrep";   params [5].val = finalFrep;
        params [6].name = "noiseFactor"; params [6].val = noiseFactor;
      }
    
      void SetParams ()
      {
        popSize     = (int)MathMax (1, params [0].val);
        g           = params [1].val;
        alpha       = params [2].val;
        beta        = params [3].val;
        initialFrep = params [4].val;
        finalFrep   = params [5].val;
        noiseFactor = params [6].val;
    
        frep        = initialFrep;
      }
    
      bool Init (const double &rangeMinP  [],  // minimum values
                 const double &rangeMaxP  [],  // maximum values
                 const double &rangeStepP [],  // step change
                 const int     epochsP = 0);   // number of epochs
    
      void Moving   ();
      void Revision ();
    
      //----------------------------------------------------------------------------
      double g;              // gravitational constant 
      double alpha;          // power for mass
      double beta;           // degree for distance
      double initialFrep;    // initial repositioning factor
      double finalFrep;      // final repositioning factor
      double noiseFactor;    // random noise factor
    
      S_CFO_Agent probe [];  // array of probes
    
      private: //-------------------------------------------------------------------
      int    epochs;         // total number of epochs
      int    epochNow;       // current epoch
      double frep;           // repositioning factor
    
      void   InitialDistribution      ();
      void   UpdateRepFactor          ();
      void   CalculateAccelerations   ();
      void   UpdatePositions          ();
      double CalculateDistanceSquared (const double &x1 [], const double &x2 []);
    };
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_CFO 类的 Init() 方法负责初始化 CFO 算法,接受最小和最大参数值的数组、更改这些值的步长以及世代数(默认值为 0)。它调用 StandardInit 方法来检查传递的值范围的有效性。如果检查失败,该方法返回 “false”。它设置总轮数和当前世代数(0)。探测器(代理)数组的大小变为种群大小(popSize)。该方法通过调用 “probe” 数组中每个代理的 Init 方法并传递坐标来初始化每个代理。如果初始化成功,该方法返回 “true”。因此,Init 方法设置算法运行的初始参数和条件。

    //——————————————————————————————————————————————————————————————————————————————
    //--- Initialization
    bool C_AO_CFO::Init (const double &rangeMinP  [], // minimum values
                         const double &rangeMaxP  [], // maximum values
                         const double &rangeStepP [], // step change
                         const int     epochsP = 0)
    {
      if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
    
      //----------------------------------------------------------------------------
      epochs   = epochsP;
      epochNow = 0;
    
      // Initialization of probes
      ArrayResize (probe, popSize);
      for (int i = 0; i < popSize; i++) probe [i].Init (coords);
    
      return true;
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_CFO 类的 Moving() 方法负责 CFO 算法的主要步骤。该方法首先增加当前 epoch 计数器,指示算法的下一步。如果该方法是第一次被调用,且 “revision” 等于 “false”,则执行初始初始化,之后完成执行。这是设置初始值和状态所必需的。接下来,将适应度函数的值从代理数组复制到临时数组中,以保持它们与进一步计算的相关性。

    该方法更新了负责在搜索空间中重新定位代理的参数,这对算法的适应性很重要,然后该方法根据当前状态计算代理的加速度并更新其位置,从而确保代理在搜索空间内的移动。最后,该方法将新的代理位置与主数组同步,记录变化并确保数据一致性。Moving() 方法根据算法执行期间的适应度函数和当前位置,对代理的状态进行系统更新。

    //——————————————————————————————————————————————————————————————————————————————
    //--- The main step of the algorithm
    void C_AO_CFO::Moving ()
    {
      epochNow++;
    
      // Initial initialization
      if (!revision)
      {
        InitialDistribution ();
        revision = true;
        return;
      }
    
      //----------------------------------------------------------------------------
      // Copy the fitness function values from the base class
      for (int p = 0; p < popSize; p++)
      {
        probe [p].f = a [p].f;
      }
    
      // Update the repositioning parameter
      UpdateRepFactor ();
    
      // Main CFO loop
      CalculateAccelerations ();
      UpdatePositions ();
    
      // Synchronize positions with the base class
      for (int p = 0; p < popSize; p++)
      {
        ArrayCopy (a [p].c, probe [p].c);
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_CFO 类的 InitialDistribution 方法负责在搜索空间中初始分配探测器(代理)。该方法遍历 popSize 群体中的每个个体。对于每个代理,通过将数组 a 设置为 0 并将适应度函数 f 设置为最小值来初始化其值。对于每个代理坐标,在指定的边界(rangeMin 和 rangeMax)内执行值的随机分布。生成随机值后,使用 SeInDiSp 方法对其进行处理,将值调整到特定范围和 rangeStep 步长。最后,将代理坐标从临时数组 c 复制到主数组 a,从而固定每个代理的初始状态。

    //——————————————————————————————————————————————————————————————————————————————
    //--- Initial probe distribution
    void C_AO_CFO::InitialDistribution ()
    {
      for (int p = 0; p < popSize; p++)
      {
        ArrayInitialize (probe [p].a, 0.0);
        probe [p].f = -DBL_MAX;
    
        // Random distribution
        for (int c = 0; c < coords; c++)
        {
          probe [p].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
          probe [p].c [c] = u.SeInDiSp (probe [p].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
        }
    
        ArrayCopy (a [p].c, probe [p].c);
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_CFO 类的 UpdateRepFactor 方法负责在算法运行期间更新重定位因子(或抑制因子)。该方法首先进行检查,如果 epoch 数大于 0,则通过从 initialFrep 线性递减到 finalFrep 来计算“frep”重新定位因子的新值。这是根据当前 epochNow 在总 epoch 数内的值来完成的。如果迭代次数为 0,则 frep 保持等于 initialFrep。如果算法处于初始阶段,这确保了因子的稳定性。在该方法结束时,使用 MathMax 和 MathMin 函数将 frep 限制在 0 到 1 的范围内。这确保了重新定位因子不会超出既定的限制,这对算法的稳定性和正确操作非常重要。

    //——————————————————————————————————————————————————————————————————————————————
    //--- Update the repositioning factor
    void C_AO_CFO::UpdateRepFactor ()
    {
      // Linearly decrease frep from the initial to the final value
      if (epochs > 0) frep = initialFrep - (initialFrep - finalFrep) * epochNow / epochs;
      else frep = initialFrep;
    
      // Value constraint
      frep = MathMax (0.0, MathMin (1.0, frep));
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_CFO 类的 CalculateAccelerations 方法旨在根据所有其他代理的影响,计算总体中每个代理(或样本)的加速度。该方法的主要步骤和逻辑如下所述。对于群体中的每个代理(从 0 到 popSize),probe[p].a 加速度值重置为 0。这样做是为了从头开始计算,并根据与其他代理的交互来积累加速效果。对于每个代理,内部循环遍历所有其他代理(从 0 到 popSize)。如果当前 p 个代理的索引与另一个 k 个代理的索引匹配,则通过 “continue” 命令跳过该次迭代。计算两个代理适应度值的差异(massDiff = probe[k].f - probe[p].f)。该值用于确定一个代理比另一个代理 “更差”(或更好)多少。如果 massDiff 大于零,则表示索引为 k 的第二个代理比索引为 p 的当前代理具有更高的适应度。在这种情况下,将执行以下操作:

    1. 距离计算:使用 CalculateDistanceSquared 函数计算两个代理当前坐标之间距离的平方。如果该距离太小(小于最小正值),则跳过该次迭代。

    2. 确定引力的方向:如果距离大于 DBL_EPSILON,则计算实际距离。对于每个 c 坐标,确定从当前代理指向适应度更高的代理的力的方向。

    3. 加速度公式: 当前代理的加速度根据以下公式更新:probe [p].a [c] += g * MathPow (massDiff, alpha) * direction / MathPow (distance, beta),该公式考虑了质量差异(适应度值)、探测器之间的距离以及影响交互强度的某些比率(g、alpha、beta)

    该方法允许每个代理考虑其他代理对其加速的影响,这是优化中的关键因素。以这种方式计算的加速度稍后将用于更新解空间中代理的位置。

    //——————————————————————————————————————————————————————————————————————————————
    //--- Calculate accelerations
    void C_AO_CFO::CalculateAccelerations ()
    {
      for (int p = 0; p < popSize; p++)
      {
        // Reset the acceleration for the current probe
        ArrayInitialize (probe [p].a, 0.0);
    
        // Summarize the influence of all other probes
        for (int k = 0; k < popSize; k++)
        {
          if (k == p) continue;
    
          // Difference in masses (fitness values)
          double massDiff = probe [k].f - probe [p].f;
    
          // Check the condition of the U(...) unit function
          if (massDiff > 0) // Strict condition for the unit function
          {
            // Calculate the distance between probes
            double distSquared = CalculateDistanceSquared (probe [k].c, probe [p].c);
            if (distSquared < DBL_EPSILON) continue;
    
            double distance = MathSqrt (distSquared);
    
            for (int c = 0; c < coords; c++)
            {
              // Force direction
              double direction = (probe [k].c [c] - probe [p].c [c]) / distance;
    
              // Acceleration equation
              probe [p].a [c] += g * MathPow (massDiff, alpha) * direction / MathPow (distance, beta);
            }
          }
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_CFO 类的 UpdatePositions 方法用于更新解空间中代理(或样本)的位置,同时考虑其加速度、随机因素和边界约束。该方法包含以下几个步骤:

    降低随机噪声因子:
    • 分析当前随机噪声比 currentNoiseFactor 的当前值。该比率初始值等于 noiseFactor。
    • 如果 epoch 数大于 0,则该比率与 epochNow 当前 epoch 成比例减小。这意味着随着迭代次数的增加,噪声会减少,从而使算法能够逐渐更准确地找到最优解。

    该方法遍历群体中的所有个体(从 0 到 popSize),并且对于每个个体,该方法遍历其所有坐标(从 0 到 coords)。使用当前加速度 probe[p].a[c] 的公式更新代理位置。在这种情况下,可以使用一个简单的公式,其中新位置等于旧位置加上当前加速度的一半。 

    在更新后的位置上添加一个小的随机偏移量,该偏移量取决于当前的噪声因子、重力 g 值以及 -1 到 1 范围内的随机数。 原始算法是严格确定性的,所以我决定添加一个随机性元素,我将在下面讨论。这种偏移增加了随机性,有助于避免陷入局部最小值。如果新位置超出指定的限制(存储在 rangeMin 和 rangeMax 中的最小值和最大值),则调整位置,使其保持在允许的范围内;如果指定了采样步长,则使用 SeInDiSp 方法进一步调整代理的位置,使位置变为步长的最近倍数。 

    //——————————————————————————————————————————————————————————————————————————————
    //--- Update positions
    void C_AO_CFO::UpdatePositions ()
    {
      // Random noise ratio decreases with increasing epoch number
      double currentNoiseFactor = noiseFactor;
      if (epochs > 0) currentNoiseFactor *= (1.0 - (double)epochNow / epochs);
    
      for (int p = 0; p < popSize; p++)
      {
        for (int c = 0; c < coords; c++)
        {
          // Update the position by equation
          probe [p].c [c] += 0.5 * probe [p].a [c];
    
          // Add a small random offset directly to the position
          probe [p].c [c] += currentNoiseFactor * g * u.RNDfromCI (-1.0, 1.0);
    
          // Reposition when going out of bounds
          if (probe [p].c [c] < rangeMin [c]) probe [p].c [c] = rangeMin [c];
          if (probe [p].c [c] > rangeMax [c]) probe [p].c [c] = rangeMax [c];
    
          // Discretization if step is specified
          probe [p].c [c] = u.SeInDiSp (probe [p].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_CFO 类的 CalculateDistanceSquared 方法负责计算多维空间中两点之间距离的平方。该方法用于优化,因为返回平方距离值消除了计算平方根的需要,这大大降低了计算成本。该方法接受两个参数:x1 和 x2。它们是数组(const double &x1[] 和 const double &x2[]),表示空间中两个点的坐标,其维度等于 “coord” 坐标的数量。在方法开始时,创建一个名为 “sum” 的变量,并将其初始化为 0。该变量用于累加坐标平方差之和。该方法遍历所有坐标(从 0 到 coords-1),并对每个坐标执行以下操作:

    • 计算数组 x1 和 x2 的对应元素之间的差(diff = x1[i] - x2[i])。
    • 计算差值的平方(diff * diff)。
    • 将差值的平方加到 “sum” 变量中。
    循环结束后,该方法返回 “sum” 差值的平方和,即两点之间距离的平方。
    //——————————————————————————————————————————————————————————————————————————————
    //--- Calculate distance (returns squared distance for optimization)
    double C_AO_CFO::CalculateDistanceSquared (const double &x1 [], const double &x2 [])
    {
      double sum = 0.0;
      for (int i = 0; i < coords; i++)
      {
        double diff = x1 [i] - x2 [i];
        sum += diff * diff;
      }
      return sum;
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_CFO 类的 Revision() 方法负责更新优化过程中找到的最佳解决方案。该方法遍历 popSize 群体中的所有个体(或样本)。对于每个代理,检查其 a[p].f 适应度函数是否大于当前最佳 fB 值。如果满足条件,则将 fB 的值更新为等于代理的适应度函数,然后如果代理的解是最佳的,则复制代理的 cB 坐标。因此,Revision 方法会找到并存储所有代理中的最佳解决方案,一旦发现代理改进了结果,就会更新全局参数。

    //——————————————————————————————————————————————————————————————————————————————
    //--- Update the best solution
    void C_AO_CFO::Revision ()
    {
      for (int p = 0; p < popSize; p++)
      {
        if (a [p].f > fB)
        {
          fB = a [p].f;
          ArrayCopy (cB, a [p].c, 0, 0, WHOLE_ARRAY);
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    
    


    测试结果

    原始的、严格确定性的算法在其基本版本中显示出以下结果: 

    CFO|Central Force Optimization|30.0|1.0|0.1|0.1|0.9|0.1|
    =============================
    5 Hilly's; Func runs:10000; result:0.34508431921321436
    25 Hilly's;Func runs:10000; result:0.2826594689557952
    500 Hilly's;Func runs:10000; result:0.25174636412054047
    =============================
    5 Forest's; Func runs:10000; result:0.26234538930351947
    25 Forest's; Func runs:10000; result:0.1852230195779629
    500 Forest's; Func runs:10000; result:0.15353213276989314
    =============================
    5 Megacity's; Func runs:10000; result:0.24923076923076923
    25 Megacity's; Func runs:10000; result:0.1261538461538462
    500 Megacity's;Func runs:10000; result:0.09492307692307768
    =============================
    All score:1.95090 (21.68%)

    有了这样的结果,该算法就不能包含在我们的评级表中。我们需要改进。因此,正如我上面所说,这个字符串中添加了随机性元素。如果没有它,确定性搜索就缺乏多种解决方案。

    // Add a small random offset directly to the position
    probe [p].c [c] += currentNoiseFactor * g * u.RNDfromCI (-1.0, 1.0);
    

    加入一点随机性之后(在每个探测器的运动中加入少量随机偏差),该算法能够开始探索意想不到的方向。我们再进行一次测试。现在我们可以观察到完全不同的结果,这些结果已经记录在我们的评级表中。 

    CFO|Central Force Optimization|30.0|1.0|0.1|0.1|0.9|0.1|1.0|
    =============================
    5 Hilly's; Func runs:10000; result:0.6096110105488222
    25 Hilly's;Func runs:10000; result:0.5495761567207647
    500 Hilly's;Func runs:10000; result:0.27830861578120414
    =============================
    5 Forest's; Func runs:10000; result:0.6341793648294705
    25 Forest's; Func runs:10000; result:0.4683296629644541
    500 Forest's; Func runs:10000; result:0.22540930020804817
    =============================
    5 Megacity's; Func runs:10000; result:0.5723076923076923
    25 Megacity's; Func runs:10000; result:0.2347692307692307
    500 Megacity's;Func runs:10000; result:0.09586153846153929
    =============================
    All score:3.66835 (40.76%)

    现在我们可以看一下算法运行的可视化过程。可以看出,该算法在中维函数上表现良好,但在低维和高维函数上效果不够好。 

    Hilly

    Hilly 测试函数上的 CFO

    Forest

    Forest 测试函数上的 CFO

    Megacity 

    Megacity 测试函数上的 CFO

    根据测试结果,该算法是顶尖的群体优化算法之一,排名第 42 位。

    # 算法 描述 Hilly Hilly 最终 Forest Forest 最终 Megacity (discrete) Megacity 最终 最终结果 最大值的百分比
    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 跨邻里搜索(across neighbourhood search) 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(animal migration optimization 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) 演进战略((P+O) evolution strategies) 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(stochastic diffusion search 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(archery algorithm 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 ACS 人工协同搜索(artificial cooperative search) 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
    13 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
    14 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
    15 ASO 无政府社会优化(anarchy society optimization) 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
    16 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
    17 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
    18 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
    19 DE 差分进化(differential evolution) 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
    20 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
    21 CRO 化学反应优化(chemical reaction optimization) 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
    22 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
    23 BSA 鸟群算法(bird swarm algorithm) 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
    24 HS 和声搜索(harmony search) 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
    25 SSG 树苗播种和生长(saplings sowing and growing) 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
    26 BCOm 细菌趋化性优化 M(bacterial chemotaxis optimization 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
    27 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
    28 (PO)ES (PO) 进化策略((PO) evolution strategies) 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
    29 TSm 禁忌搜索 M(tabu search 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
    30 BSO 头脑风暴优化(brain storm optimization) 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
    31 WOAm Wale 优化算法 M(wale optimization algorithm 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
    32 AEFA 人工电场算法(artificial electric field algorithm) 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
    33 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
    34 ACOm 蚁群优化M(ant colony optimization 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
    35 BFO-GA 细菌觅食优化 - ga(bacterial foraging optimization - 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
    36 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
    37 ABHA 人工蜂巢算法(artificial bee hive algorithm) 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
    38 ACMO 大气云模型优化(atmospheric cloud model optimization) 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
    39 ADAMm 自适应矩估计 M 0.88635 0.44766 0.26613 1.60014 0.84497 0.38493 0.16889 1.39880 0.66154 0.27046 0.10594 1.03794 4.037 44.85
    40 CGO 混沌博弈优化 0.57256 0.37158 0.32018 1.26432 0.61176 0.61931 0.62161 1.85267 0.37538 0.21923 0.19028 0.78490 3.902 43.35
    41 ATAm 人工部落算法 M 0.71771 0.55304 0.25235 1.52310 0.82491 0.55904 0.20473 1.58867 0.44000 0.18615 0.09411 0.72026 3.832 42.58
    42 CFO 中心引力优化 0.60961 0.54958 0.27831 1.43750 0.63418 0.46833 0.22541 1.32792 0.57231 0.23477 0.09586 0.90294 3.668 40.76
    43 ASHA 人工喷淋算法(artificial showering algorithm) 0.89686 0.40433 0.25617 1.55737 0.80360 0.35526 0.19160 1.35046 0.47692 0.18123 0.09774 0.75589 3.664 40.71
    44 ASBO 适应性社会行为优化(adaptive social behavior optimization) 0.76331 0.49253 0.32619 1.58202 0.79546 0.40035 0.26097 1.45677 0.26462 0.17169 0.18200 0.61831 3.657 40.63
    45 MEC 思维进化计算(mind evolutionary computation) 0.69533 0.53376 0.32661 1.55569 0.72464 0.33036 0.07198 1.12698 0.52500 0.22000 0.04198 0.78698 3.470 38.55
    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


    总结

    CFO 算法基于物体间引力的原理,是一种将物理定律应用于解决复杂优化问题的有趣尝试。经过对我们的标准功能集的广泛测试,CFO 的效率达到了理论最大值的 40% 以上,在排名前 45 的基于群体的优化算法中排名第 42 位。应该指出的是,即使是这个适度的结果,也只能通过修改原始算法,在其最初的确定性中引入随机成分来实现。

    尽管绩效指标相对较低,但 CFO 有许多吸引人的特点。首先,它有一个非常清晰的物理解释,这使它变得直观。基本概念的简单性(代表潜在解决方案的探测器会被更好的解决方案所吸引,就像物质物体会被质量更大的物体所吸引一样)保证了算法运行的透明性和易于实现性。

    然而,测试也揭示了 CFO 存在一些重大局限性,需要进行修订或改进。过度依赖探测器的初始分布是关键问题之一 —— 由于确定性运动机制,探测器的初始位置在很大程度上预先决定了最终的搜索结果。 

    单向吸引机制,其中只有最好的解决方案会影响最差的解决方案,而不是相反,尽管它有逻辑依据,但会显著限制算法探索搜索空间的能力。引入一种自适应机制,允许较差解决方案的一些影响,特别是在搜索的早期阶段,可以增强算法检测解决方案空间中有前景区域的能力。

    图2.根据相应测试的算法颜色分级

    ​

    图 3.算法测试结果直方图(范围从 0 到 100,越高越好,其中 100 是可能的最大理论结果,存档中有一个用于计算评级表的脚本)

    CFO 的优缺点:

    优点:

    1. 对中等维函数效果很好。

    缺点:

    1. 对于低维和高维函数,效果都不够理想。
    2. 对离散函数的搜索能力较弱。

    这篇文章附有一个归档,其中包含当前版本的算法代码。文章作者不对规范算法描述的绝对准确性负责。为提高搜索能力,已对其中多项功能进行了修改。文章中提出的结论和判断都是基于实验结果。


    本文中用到的程序

    # 名称 类型 描述
    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_CFO.mq5
    脚本 CFO 测试台

    本文由MetaQuotes Ltd译自俄文
    原文地址: https://www.mql5.com/ru/articles/17167

    附加的文件 |
    CFO.zip (179.39 KB)
    MQL5自动化交易策略(第十九部分):包络线趋势反弹剥头皮交易——交易执行与风险管理(下篇) MQL5自动化交易策略(第十九部分):包络线趋势反弹剥头皮交易——交易执行与风险管理(下篇)
    我们将为MQL5中的包络线趋势反弹剥头皮策略实现交易执行模块与风险管理功能。我们实现了订单触发逻辑,并构建了包含止损设置与头寸规模计算在内的风险控制体系。最终在第十八部分的基础上完成策略回测与参数优化。
    您应当知道的 MQL5 向导技术(第 60 部分):推理学习(Wasserstein-VAE),配合移动平均线和随机振荡器形态 您应当知道的 MQL5 向导技术(第 60 部分):推理学习(Wasserstein-VAE),配合移动平均线和随机振荡器形态
    我们将目光转向 MA 与随机振荡器的互补配对,实证推理学习在后监督学习与强化学习状况中扮演的角色。显然,推理学习有多种途径可供选择,不过我们的方式是使用变分自编码器。我们先以 Python 探索这些,然后将训练好的模型以 ONNX 格式导出,可在 MetaTrader 中供向导汇编智能系统所用。
    使用机器学习开发趋势交易策略 使用机器学习开发趋势交易策略
    本研究介绍了一种开发趋势跟踪交易策略的新方法。本节介绍标注训练数据并利用它训练分类器的过程。这个过程获得了可在 MetaTrader 5 上运行的完全可操作的交易系统。
    MQL5自动化交易策略(第十八部分):基于包络线趋势反弹的剥头皮交易——核心架构与信号生成(1) MQL5自动化交易策略(第十八部分):基于包络线趋势反弹的剥头皮交易——核心架构与信号生成(1)
    本文中,我们将构建包络线趋势反弹剥头皮EA的核心架构。我们初始化包络线等信号生成所需的指标。同时,我们还将搭建回测环境,为下一篇文章中的交易执行环节做好准备。