English Русский Español Deutsch 日本語 Português
preview
海豚回波定位算法(DEA)

海豚回波定位算法(DEA)

MetaTrader 5交易 |
40 0
Andrey Dik
Andrey Dik

内容

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


引言

随着每一篇新文章的发表,我们都离目标更近了一步:为我们的交易机器人找到一种合适的算法来解决优化问题。今天我们将探索另一种受自然界启发的算法,它基于某些海洋动物的回声定位能力。

想象一只海豚在漆黑的深海中捕猎。能见度几乎为零,但这并不妨碍它成功地找到猎物。秘密在于一种惊人的能力:海豚发出一系列脉冲声,然后利用反射回来的回声,构建出周围空间的精确图像。一个有趣的事实是,这些脉冲声的频率是变化的:在大范围搜索时脉冲声稀疏,而当目标靠近时则变得密集。正是这种不同寻常的策略构成了 DEA(海豚回声定位算法)的基础。

在优化领域,我们经常面临类似的挑战:在广阔的可能性空间中寻找最优解,却无法掌握全局信息。就像海洋中的海豚一样,我们从广泛搜索开始,然后逐渐将注意力集中在最有前景的区域。


算法的实现

为了更好地理解算法的工作原理,让我们想象这样一个场景。你和朋友们在一片广阔的海滩上寻找黄金,每人都拿着金属探测器。搜索开始时,分散到整个区域是合理的 —— 这样你们更有可能偶然发现有趣的东西。但一旦其中一人听到强烈的信号,他就会通知其他人,渐渐地整个团队开始集中到有前景的地方。到搜索结束时,所有人都在最强信号附近挖掘。这就是海豚回声定位算法的精髓。

在算法中,海豚的角色由搜索代理扮演 —— 即解空间中的点。每只这样的 "海豚" 都代表问题的一个潜在解。例如,如果我们在寻找简单函数 y = x² 的最小值,那么一只海豚可能在点 x = -3 处(此处 y = 9),另一只在点 x = 1 处(此处 y = 1),第三只可能随机落在点 x = 0 处(此处 y = 0)—— 当前表现最好的海豚。

但海豚是如何交换信息的呢?这就引出了有效半径的概念,记为 "Re"。想想手电筒的光能传播多远。当 Re = 1 时,我们得到一束狭窄的光,只能照亮紧邻的区域。当 Re = 3 时,光线散布得更宽,覆盖更多空间。当 Re = 5 或更高时,搜索行为更像是泛光灯。在算法的语境中,这意味着关于优质解的信息会传播到邻近区域,且这种影响的强度随距离递减。

所有这些信息都以 "前景图" 的形式累积起来,算法称之为累积适应度(AF)。想象一下城市的热力图,其中 "热" 区表示高活动区域。在我们的例子中,"热" 区是海豚找到优质解(猎物)的区域。某个区域的成功发现越多,它就越 "热",从而吸引更多海豚。

然而,算法比简单地聚集在一个地方要聪明得多。它使用一个称为预定概率(PP)的参数来控制探索新区域与开发已发现优质地点之间的平衡。初始阶段,当 PP 较低(约 0.1)时,算法进行探索实验;随后,当 PP 约为 0.5 时,它更多地依赖经过验证的方法;而当 PP 接近 1 时,它只使用最有效的方法。

让我们来看一个算法工作原理的具体例子。假设我们正在寻找一片丘陵地带的最高点。搜索开始时,我们的海豚随机散布在整个区域 —— 有些在山谷,有些在山坡,有些则幸运地落在山顶。在评估了每个位置的高度后,算法确定位于山顶的海豚找到了最佳地点。现在有趣的事情发生了:这只幸运海豚周围的区域对其他海豚变得更有吸引力,但 —— 这是关键点 —— 领导者所在的那个点会被暂时 "清零"。这迫使其他海豚探索附近区域,而不是聚集在一个点上。这种方法有助于避免过早收敛,并找到其他潜在的优质解。

随着算法的运行,情况会发生变化。在第 50 次迭代时,我们看到海豚开始聚集在丘陵区域,但仍保持一定的多样性。到第 100 次迭代时,它们中的大多数都集中在地形的最高点,而到最后,所有海豚都聚集在全局最优点。算法的效率取决于参数的正确配置。

有效半径 Re 的选择对于平衡局部搜索和全局搜索至关重要。当 Re = 1 时,我们得到非常精确但范围狭窄的搜索 —— 就像用放大镜在干草堆里找针一样。将 Re 增加到 3-4 可以得到平衡的方法,类似于用手电筒搜索。而当 Re 大于 5 时,算法像探照灯一样工作,覆盖更大的区域,但会损失精度。

Power 参数决定了算法从探索切换到开发的剧烈程度。当 Power = 1 时,这种过渡是平滑且渐进的。Power 值为 2(推荐值)会产生更明显的过渡,而 Power 值为 3 及以上时会产生更急剧的过渡,这对某些类型的任务可能很有用。

初始概率 PP1 设定了探索与利用之间的起始平衡。低值(0.05)意味着对空间进行激进探索,标准值 0.1 提供了良好的平衡,而高值(0.5)则导致快速聚焦于已找到的解。

DEA 的主要优势在于其适应性 —— 算法自动调整探索新区域与深入有前景区域之间的平衡。同时它保持相对简单,只需要配置四个参数。通过累积适应度传播信息的机制可以高效利用关于搜索空间的知识,而对最佳位置的 AF 清零则有助于避免陷入局部最优。

当然,该算法也有一些局限性。它需要额外的内存来存储所有备选方案的累积适应度,这对于非常大的搜索空间可能是个问题。还有一点:算法的效率取决于有效半径 Re 的正确选择。以下是算法运行原理的示意图。

dea_algorithm

图 1. DEA 算法运行示意

 图中展示了 DEA 算法的主要阶段:

  1. 初始探索—— 海豚(搜索代理)处于随机位置,具有 Re 回声定位半径。
  2. 累积适应度(AF)分布——"AF" 如何在海豚位置周围累积。
  3. 收敛过程由三个子阶段组成,展示了从探索到开发的过渡。

该图示帮助我们理解海豚如何利用回声定位找到最优解、信息如何通过累积适应度传播、参数 "Re" 如何影响搜索宽度,以及 "PP" 如何控制探索与利用之间的平衡。核心概念——PP(预定概率)和 AF 的公式。算法流程图—— 带循环的基本步骤。Re 参数的影响—— 窄、中、宽三种影响半径的可视化。

图中的配色方案:蓝色 —— 普通海豚,红色 —— 最优解,蓝色渐变 —— 累积适应度水平,灰色 —— 搜索空间。 

现在我们对算法有了一定了解,可以开始编写详细的伪代码了。 

输入:

  • 海豚数量(搜索代理)
  • 有效回声定位半径
  • 收敛曲线幂次
  • 初始预定概率
  • 搜索空间边界和每个维度的采样步长

初始化:

创建备选方案空间:

  1. 对搜索空间的每个维度,生成一组可能的备选方案
  2. 如果指定了采样步长,则按该步长从最小值到最大值创建备选方案
  3. 如果未指定步长,则生成 500 个均匀分布的备选方案
  4. 检查有效半径:不应超过备选方案数量的四分之一

海豚初始放置:

  1. 将所有海豚随机放置在搜索空间中
  2. 对每只海豚,计算其位置的质量(适应度)
  3. 将所有备选方案的累积适应度初始化为零

基本优化循环:

重复指定次数的迭代:

阶段 1. 计算预定概率

计算当前迭代中保持最佳位置的概率。在过程开始时,该概率等于初始值,然后根据幂函数逐渐增加,到优化结束时达到 1。

阶段 2. 计算动态自适应系数

计算决定局部搜索与全局搜索之间平衡的系数。该系数等于最优解与最差解之差除以所有解与最优解偏差之和。当种群多样化时,系数较高;当种群收敛时,系数较低。

阶段 3. 累积适应度信息

对每只海豚:

  • 相对于当前种群中的范围,对其适应度进行归一化。
  • 对每个坐标,找到最接近海豚位置的备选方案
  • 在该备选方案的回声定位半径内传播质量信息
  • 影响强度随距中心的距离线性递减
  • 在空间边界处应用反射以保留信息
  • 向所有累积适应度添加一个小值以避免零概率

步骤 4. 重置最佳位置的信息

找到具有最优解的海豚,并将其坐标对应备选方案的累积适应度重置。这防止搜索过于集中在一个点上。

步骤 5. 选择新位置

对每只海豚及其每个坐标:

  • 如果这是最佳海豚且有机会保留位置,则保持坐标不变
  • 否则,如果有累积的适应度信息,则使用轮盘赌法按比例选择新的备选方案
  • 如果累积信息缺失或不足:
    • 以等于动态系数的概率,在回声定位半径内执行局部搜索
    • 否则,通过选择随机备选方案执行全局搜索
  • 应用搜索空间约束和离散化

阶段6. 评估新位置

计算每只海豚在新位置的解的质量。

阶段 7. 全局信息更新

更新种群中最优解和最差解的记录。如果找到的解优于当前全局最优解,则应保留。

结束:

完成所有迭代后,返回找到的最优解及其质量。

现在让我们着手实现 DEA。

让我们编写 S_Alternative 结构体。它存储优化决策过程中一个备选方案的信息,包含 "value"(表征该备选方案的值,即有效性评估)和 "AF"—— 该备选方案的累积适应度,这在需要应用迭代过程时评估备选方案质量是必要的。

//————————————————————————————————————————————————————————————————————
struct S_Alternative
{
    double value;     // alternative value
    double AF;        // accumulated fitness for this alternative
};
//————————————————————————————————————————————————————————————————————

S_Coordinate 结构体用于表示与特定坐标关联的一组备选方案,"alt" 是备选方案数组,每个对应一个坐标,"count" 是一个变量,表示 "alt" 数组中实际存储的当前备选方案数量。

//————————————————————————————————————————————————————————————————————
struct S_Coordinate
{
    S_Alternative alt [];  // array of alternatives for a given coordinate
    int           count;   // number of alternatives
};
//————————————————————————————————————————————————————————————————————

接下来,我们描述直接实现优化算法(DEA)的类。C_AO_DEA 类是 C_AO 基类的继承者。创建类对象时,初始化算法的主要参数:

  • popSize —— 初始化种群大小("海豚" 或位置的数量)。
  • Re —— 有效搜索半径的初始值。
  • Power —— 收敛曲线的幂次。
  • PP1—— 初始化第一次迭代的收敛系数。
然后初始化 "params" 数组,用于存储算法的用户参数。具有相应名称的初始值 popSize、Re、Power、PP1 被复制到其中。

SetParams 方法用于根据 params 数组中写入的值更新算法的内部参数。提取 popSize、Re、Power、PP1 值后,执行验证检查。

方法

  • Init用于初始化算法,接受每个参数的最小值、最大值和步长,以及总世代数(迭代次数)。
  • Moving负责在搜索空间中移动 "海豚";这是主优化循环的一部分。
  • Revision在移动后调整种群位置和参数。

以下字段在内部用于运行算法,从类外部不可访问。

  • PP是一个浮点数,表示当前迭代的预定概率,用于随机决策,currentEpoch是跟踪算法当前世代(迭代)的值。
  • totalEpochs是存储计划总世代数的值。
  • coeffA是用于选择位置的动态系数。
  • coordData是结构体数组,其中每个 S_Coordinate 包含特定坐标的备选方案数组及其数量。 
方法
  • CalculatePP用于计算当前迭代的 PP 值(预定概率)。
  • CalculateAccumulativeFitness计算每个备选方案的累积适应度。
  • ResetAFforBestLocation重置最佳位置的累积适应度值。
  • SelectNextLocations根据当前状态为海豚选择下一个位置。
  • FindNearestAlternative根据给定的坐标和值找到最近的备选方案。
  • CalculateCoefficientA计算 coeffA 动态系数。

总的来说,C_AO_DEA 类已准备好用于在多维空间中寻找解。它具有用于初始化和执行主要优化步骤的公共方法,以及实现算法内部逻辑的私有方法和数据。

//————————————————————————————————————————————————————————————————————
class C_AO_DEA : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_DEA () { }
  C_AO_DEA ()
  {
    ao_name = "DEA";
    ao_desc = "Dolphin Echolocation Algorithm";
    ao_link = "https://www.mql5.com/en/articles/18495";

    popSize = 100;    // NL - number of locations (dolphins) 
    Re      = 2;      // effective search radius
    Power   = 2.0;    // convergence curve power
    PP1     = 1.0;    // first iteration convergence factor

    ArrayResize (params, 4);

    params [0].name = "popSize"; params [0].val = popSize;
    params [1].name = "Re";      params [1].val = Re;
    params [2].name = "Power";   params [2].val = Power;
    params [3].name = "PP1";     params [3].val = PP1;
  }

  void SetParams ()
  {
    popSize = (int)params [0].val;
    Re      = (int)params [1].val;
    Power   = params      [2].val;
    PP1     = params      [3].val;

    // Check the parameters validity
    if (Re < 0) Re = 0;
    if (PP1 < 0.0) PP1 = 0.0;
    if (PP1 > 1.0) PP1 = 1.0;
    if (Power < 0.1) Power = 0.1;
  }

  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 ();

  //------------------------------------------------------------------
  int    Re;           // effective search radius
  double Power;        // convergence curve power
  double PP1;          // first iteration convergence factor

  private: //---------------------------------------------------------
  double PP;           // predefined probability for the current iteration
  int    currentEpoch; // current epoch
  int    totalEpochs;  // total number of epochs
  double coeffA;       // dynamic coefficient used to select positions

  S_Coordinate coordData []; // coordinate data (alternatives and AF)

  void CalculatePP ();
  void CalculateAccumulativeFitness ();
  void ResetAFforBestLocation ();
  void SelectNextLocations    ();
  int  FindNearestAlternative (int coord, double value);
  void CalculateCoefficientA  ();
};
//————————————————————————————————————————————————————————————————————

C_AO_DEA 类的 Init 方法用于初始化 DEA 算法。它接受每个变量的最小值和最大值、每个变量的变化步长,以及优化的总世代数作为输入。该方法首先调用基类的 StandardInit 方法来执行算法通用参数的标准初始化,如果此初始化失败,则返回 'false'。然后初始化跟踪算法执行的当前世代和总世代的变量。

之后,创建 coordData 数据结构,用于存储搜索空间每个坐标(变量)的信息。对每个坐标,确定可能的备选值数量。如果为某个坐标指定了步长变化,则根据边界和步长计算备选方案数量。如果未指定步长(等于零),则假定该坐标是连续的,并为其设置固定数量的备选方案。

然后检查 Re 参数(搜索半径),必要时进行调整,使其不超过每个坐标备选方案数量的四分之一。之后,分配内存以存储每个坐标的备选值。

最后,循环填充每个坐标的备选值数组,在最小值和最大值之间均匀分布。如果指定了步长,则使用该步长计算备选值。如果未指定步长,则使用指定边界之间的空间离散化。同时,对每个备选方案,将关联的 AF(累积适应度)值初始化为零。如果所有初始化步骤都成功完成,该方法返回 'true'。

//————————————————————————————————————————————————————————————————————
//--- Initialization
bool C_AO_DEA::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;

  // Initialize the data structure for coordinates
  ArrayResize (coordData, coords);

  // Create alternatives for each coordinate
  for (int c = 0; c < coords; c++)
  {
    if (rangeStep [c] != 0)
    {
      coordData [c].count = (int)((rangeMax [c] - rangeMin [c]) / rangeStep [c]) + 1;
    }
    else
    {
      coordData [c].count = 500;
    }

    // Check that Re is not too large for the number of alternatives
    if (Re > coordData [c].count / 4) Re = coordData [c].count / 4;

    ArrayResize (coordData [c].alt, coordData [c].count);

    // Fill in the alternatives
    for (int i = 0; i < coordData [c].count; i++)
    {
      if (rangeStep [c] != 0)
      {
        coordData [c].alt [i].value = rangeMin [c] + i * rangeStep [c];
      }
      else
      {
        coordData [c].alt [i].value = rangeMin [c] + (rangeMax [c] - rangeMin [c]) * i / (coordData [c].count - 1);
      }
      coordData [c].alt [i].AF = 0.0;
    }
  }

  return true;
}
//————————————————————————————————————————————————————————————————————

Moving 方法实现了 DEA 算法的一个主要步骤,对应于优化过程的某次迭代。如果通过 "revision" 标志检测到这是第一次迭代,则进行种群坐标的初始随机分配。对每个个体(种群元素)以及范围内的每个变量,分配随机值。这些值会根据边界和变化步长进行调整,以确保解的可接受性。之后,设置标志,初始化完成,方法退出该循环。

初始化完成后,当前世代(迭代)计数器加一。然后执行算法的关键步骤:确定当前种群中每个解的性能指标,这些指标表明它们对问题的适应程度。计算 "a" 系数。基于质量指标,确定每个解适应度的广义度量,并对最佳位置对应的 AF 信息进行重置,这使得搜索能够集中在最优解的区域。基于当前信息和计算出的系数,选择下一个位置,用于在搜索空间中更新和移动候选解。

总的来说,Moving 方法实现了 DEA 算法的一个迭代周期,在寻找最优解的过程中依次执行更新和改进解的关键步骤。

//————————————————————————————————————————————————————————————————————
//--- The main step of the algorithm (according to Algorithm 1)
void C_AO_DEA::Moving ()
{
  // Initial setup
  if (!revision)
  {
    for (int p = 0; p < popSize; p++)
    {
      for (int c = 0; c < coords; c++)
      {
        a [p].c  [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        a [p].c  [c] = u.SeInDiSp (a [p].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
        a [p].cB [c] = a [p].c [c];
      }
    }

    revision = true;
    return;
  }

  // Increase the epoch counter
  currentEpoch++;

  // Steps of the DEA algorithm according to Algorithm 1:

  // 1. Calculate PP for the current iteration
  CalculatePP ();

  // 2. We calculate the 'a' dynamic coefficient
  CalculateCoefficientA ();

  // 3. Calculate accumulated fitness
  CalculateAccumulativeFitness ();

  // 4. Find the best location and reset AF for it
  ResetAFforBestLocation ();

  // 5. Select the next positions
  SelectNextLocations ();
}
//————————————————————————————————————————————————————————————————————

CalculatePP 方法计算预定概率(PP),该概率用于算法内部的决策过程。如果总世代数(迭代数)等于或小于 1,则将概率设置为等于预定的初始值(PP1)。在这种情况下,无需进一步计算,方法终止。

如果世代数大于 1,则根据给定公式计算 PP 值,该公式考虑了当前世代,并包含以下逻辑:计算幂函数值,即当前世代的 Power 次方减 1,同样计算总世代数的幂函数值。

PP 值使用一个逐渐趋近于 1 的公式进行更新,同时考虑了当前世代的进度。具体来说,向初始 PP1 值添加了与进度成比例的部分,该进度由次数以及给定的 Power 和 totalEpochs 参数进行调整。如果总指数不为零,则进行除法运算以获得当前概率;否则,该值保持等于初始 PP1。

最终,该方法在算法执行过程中动态改变概率值,确保随着世代推进,在初始值和更 "进取" 的值之间保持平衡。

//————————————————————————————————————————————————————————————————————
//--- Calculate the predetermined probability (according to the formula 5)
void C_AO_DEA::CalculatePP ()
{
  if (totalEpochs <= 1)
  {
    PP = PP1;
    return;
  }

  // PP = PP1 + (1 - PP1) * ((Loop^Power - 1) / (LoopsNumber^Power - 1))
  double iterPower  = MathPow ((double)currentEpoch, Power) - 1.0;
  double totalPower = MathPow ((double)totalEpochs,  Power) - 1.0;

  if (totalPower != 0)
  {
    PP = PP1 + (1.0 - PP1) * iterPower / totalPower;
  }
  else
  {
    PP = PP1;
  }
}
//————————————————————————————————————————————————————————————————————

CalculateCoefficientA 方法用于计算调节搜索过程的动态系数 ‘a’。该方法遍历当前解的整个种群,对每个解计算最大可能适应度 fB 与特定解当前适应度(a [i].f)之间的差值。这些差值累加到一个总和中。

在累加值的总和之后,'a' 系数通过 fB 与最小适应度 fW 之差除以所得总和得到。这使我们能够定义一个动态系数,它根据种群的当前状态进行自适应,并有助于平衡搜索空间的收敛速度或探索程度。

    因此,该方法创建了一个缩放参数,该参数考虑了解的适应度分布,并影响它们在算法后续迭代中的更新和移动策略。

    //————————————————————————————————————————————————————————————————————
    //--- Calculate the dynamic 'a' coefficient
    void C_AO_DEA::CalculateCoefficientA ()
    {
      double sumFitness = 0.0;
    
      for (int i = 0; i < popSize; i++)
      {
        sumFitness += fB - a [i].f;
      }
    
      coeffA = (fB - fW) / sumFitness;
    }
    //————————————————————————————————————————————————————————————————————
    

    FindNearestAlternative 方法用于在特定坐标内找到与给定值最接近的备选方案,它接受两个参数:"coord" 坐标索引和我们需要为其找到最近备选方案的 "value" 值。初始化并设置 "nearest"(最近备选方案的索引)和 "minDist"(最小距离)的初始值。

    循环遍历与给定 coord 坐标关联的所有备选方案。对每个备选方案,计算给定 "value" 与备选方案值(coordData [coord].alt [i].value)之间的距离,如果计算出的距离小于当前最小距离(minDist),则用新的更小距离值更新 "minDist",并用当前备选方案的索引更新 "nearest"。

    循环完成后,返回在给定坐标内与给定 "value" 最接近的备选方案的索引。因此,该方法确定指定坐标的可用备选方案中,哪一个最接近给定的实数值。

    //————————————————————————————————————————————————————————————————————
    //--- Search for the closest alternative
    int C_AO_DEA::FindNearestAlternative (int coord, double value)
    {
      int nearest = 0;
      double minDist = DBL_MAX;
    
      for (int i = 0; i < coordData [coord].count; i++)
      {
        double dist = MathAbs (value - coordData [coord].alt [i].value);
        if (dist < minDist)
        {
          minDist = dist;
          nearest = i;
        }
      }
    
      return nearest;
    }
    //————————————————————————————————————————————————————————————————————
    

    CalculateAccumulativeFitness 方法用于根据算法 2 计算备选方案的累积适应度(AF)。在开始计算之前,清除每个坐标中每个备选方案的所有累积适应度值并设置为零。找到当前解种群中的适应度范围,计算为最大可能适应度(fB)与最小适应度(fW)之差。

    然后,对每个代理(海豚),将其适应度相对于范围进行归一化,并对每个搜索坐标,确定最近的备选方案,在该备选方案周围半径 "Re" 内,考虑边界的反射特性,更新备选方案的累积适应度。这是通过加权累加贡献来实现的,其中权重基于距最近备选方案的距离(考虑反射边界)形成,并包含与半径 "Re" 中当前步长相关的系数。

    作为该方法运行的结果,每个坐标中每个备选方案的累积适应度值得以形成,同时考虑了邻近备选方案的贡献。

    //————————————————————————————————————————————————————————————————————
    //--- Calculate the accumulated fitness (according to Algorithm 2)
    void C_AO_DEA::CalculateAccumulativeFitness ()
    {
      // Clear the accumulated fitness for all alternatives
      for (int c = 0; c < coords; c++)
      {
        for (int i = 0; i < coordData [c].count; i++)
        {
          coordData [c].alt [i].AF = 0.0;
        }
      }
    
      double rangeFF = fB - fW;
      if (rangeFF == 0.0) rangeFF = DBL_EPSILON;
    
      // For each agent (dolphin)
      for (int i = 0; i < popSize; i++)
      {
        // Normalize 'fitness' for this agent
        double normalizedFitness = (a [i].f - fW) / rangeFF;
    
        for (int c = 0; c < coords; c++)
        {
          // Find the closest alternative for the current coordinate
          int nearestAlt = FindNearestAlternative (c, a [i].c [c]);
    
          // Update the accumulated fitness in Re radius
          // According to Algorithm 2: AF(A+k)j = (1/Re) * (Re - |k|) * fitness(i) + AF(A+k)j
          for (int k = -Re; k <= Re; k++)
          {
            int altIndex = nearestAlt + k;
    
            // Boundary check taking into account reflection (reflective characteristic)
            if (altIndex < 0)
            {
              altIndex = -altIndex; // reflection from the lower border
            }
            else if (altIndex >= coordData [c].count)
            {
              altIndex = 2 * (coordData [c].count - 1) - altIndex; // reflection from the upper border
            }
    
            if (altIndex >= 0 && altIndex < coordData [c].count)
            {
              double weight = (1.0 / (double)(Re + 1)) * (Re - MathAbs (k) + 1);
              coordData [c].alt [altIndex].AF += weight * normalizedFitness;
            }
          }
        }
      }
    
      // Add a small epsilon value to all AFs to avoid zero probabilities
      double epsilon = 0.0001;
      for (int c = 0; c < coords; c++)
      {
        for (int i = 0; i < coordData [c].count; i++)
        {
          coordData [c].alt [i].AF += epsilon;
        }
      }
    }
    //————————————————————————————————————————————————————————————————————
    
    

    ResetAFforBestLocation 方法用于重置与种群中最优解(代理)位置对应的备选方案的累积适应度(AF)。首先,该方法确定当前种群中最优解(代理)的索引。它遍历所有解,找到适应度值最大的解。具有最高适应度的解的索引存储在 bestIndex 变量中。

    对最优解的每个 'c' 坐标,该方法使用 FindNearestAlternative 函数确定与最优解在该坐标上的值最接近的备选方案,如果找到的备选方案存在(索引在可接受范围内),则将该特定备选方案的累积适应度值 AF 重置为零。

    因此,该方法仅对与最优解坐标最匹配的那些备选方案重置 AF。这样做是为了减少这些备选方案对搜索后续阶段的影响,有可能促进对搜索空间其他区域的探索。

    结果是,与最优解最匹配的那些坐标的适应度被 "清零 / 重置",以刺激寻找其他可能更优的解。

    //————————————————————————————————————————————————————————————————————
    //--- Reset AF for the best location (according to Algorithm 3)
    void C_AO_DEA::ResetAFforBestLocation ()
    {
      // Find the index of the best solution
      int bestIndex = 0;
      double bestFitness = a [0].f;
    
      // Search for a solution with maximum fitness (since we always maximize normalized fitness)
      for (int i = 1; i < popSize; i++)
      {
        if (a [i].f > bestFitness)
        {
          bestFitness = a [i].f;
          bestIndex = i;
        }
      }
    
      // Reset AF for ALL alternatives corresponding to the coordinates of the best solution
      for (int c = 0; c < coords; c++)
      {
        // Find the closest alternative to the coordinate of the best solution
        int nearestAlt = FindNearestAlternative (c, a [bestIndex].c [c]);
    
        // Reset AF only for this alternative
        if (nearestAlt >= 0 && nearestAlt < coordData [c].count)
        {
          coordData [c].alt [nearestAlt].AF = 0.0;
        }
      }
    }
    //————————————————————————————————————————————————————————————————————
    

    SelectNextLocations 方法用于基于概率考量,并同时考虑累积适应度(AF)和保持最优位置的策略,为种群中的每个解(每只海豚)选择下一个位置。该方法首先根据适应度值确定当前种群中的最优解。最优解的索引被保存下来供后续使用。

    对每个解及其每个坐标,执行以下操作:如果当前解是最优解,且随机数小于 PP 概率,则保持该解的当前坐标不变。这保留了之前的最优解。然而,如果当前解不是最优解,或者在 PP 策略下位置未被保留,则对当前坐标的备选方案的所有 AF 值进行求和。如果 AF 总和大于一个小数(ε),表明存在非零适应度值,则执行轮盘赌:选择一个与 AF 总和成比例的随机数,并根据备选方案的累积 AF 总和来选择解的坐标,从而使解向适应度更高的位置移动。

    如果 AF 总和接近零(所有 AF 值都非常小),则在随机数小于 coeffA 动态系数的情况下执行局部搜索。在这种情况下,选择相对于当前值的随机偏移(-Re...+Re),并用最接近的值更新坐标。同时考虑边界。

    如果随机数大于 coeffA,则执行全局搜索(随机选择)。在这种情况下,为坐标选择一个完全随机的备选方案。在方法的最后,将获得的坐标值限制在允许范围(rangeMin, rangeMax)内,并用给定的 rangeStep 步长进行离散化,以确保该值在允许范围内并对应于允许值。

    该方法运行的结果是,每个解的坐标都基于累积适应度的概率权重、PP(保持最优)策略以及动态的局部和全局搜索进行了更新,这使得算法能够有效地探索搜索空间并找到最优解。

    //————————————————————————————————————————————————————————————————————
    //--- Select next positions based on probabilities
    void C_AO_DEA::SelectNextLocations ()
    {
      // First we find the index of the best solution
      int bestIndex = 0;
      double bestFitness = a [0].f;
    
      for (int i = 1; i < popSize; i++)
      {
        if (a [i].f > bestFitness)
        {
          bestFitness = a [i].f;
          bestIndex = i;
        }
      }
    
      for (int i = 0; i < popSize; i++)
      {
        for (int c = 0; c < coords; c++)
        {
          // For the best agent, apply PP
          if (i == bestIndex && u.RNDprobab () < PP)
          {
            // Save the current value of the coordinate of the best solution with PP probability
            continue;
          }
    
          // Select based on accumulated fitness
          double totalAF = 0.0;
          for (int alt = 0; alt < coordData [c].count; alt++)
          {
            totalAF += coordData [c].alt [alt].AF;
          }
    
          if (totalAF > DBL_EPSILON) // Check that there are non-zero AFs
          {
            // Choose an alternative based on roulette
            double rnd = u.RNDprobab () * totalAF;
            double cumSum = 0.0;
    
            for (int alt = 0; alt < coordData [c].count; alt++)
            {
              cumSum += coordData [c].alt [alt].AF;
              if (cumSum >= rnd)
              {
                a [i].c [c] = coordData [c].alt [alt].value;
                break;
              }
            }
          }
          else
          {
            // If all AFs are almost zero, use random selection
            // with the dynamic coeffA coefficient for the local search probability
            if (u.RNDprobab () < coeffA) // Use the dynamic coefficient
            {
              // Local search - stay close to the current position
              int currentAlt = FindNearestAlternative (c, a [i].c [c]);
              int shift = u.RNDminusOne (2 * Re + 1) - Re; // random offset within Re
              int newAlt = currentAlt + shift;
    
              // Check boundaries
              if (newAlt < 0) newAlt = 0;
              if (newAlt >= coordData [c].count) newAlt = coordData [c].count - 1;
    
              a [i].c [c] = coordData [c].alt [newAlt].value;
            }
            else
            {
              // Global search - completely random selection
              int randAlt = u.RNDminusOne (coordData [c].count);
              a [i].c [c] = coordData [c].alt [randAlt].value;
            }
          }
    
          // Bounds checking and discretization
          a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //————————————————————————————————————————————————————————————————————
    

    Revision 方法用于更新当前种群中最优解和最差解的信息。确定最优解的索引,并将存储最差适应度的变量赋值为当前最优解的值。这为在当前种群中寻找新的最差值创建了初始条件。搜索当前种群中的所有解:确定具有最大适应度值的解(最优解)。保存其值并更新存储的索引。我们还搜索具有最小适应度值的解(最差解)。如果确实找到了最优解,则将其坐标复制到专门用于存储当前最优解的数组或变量中。

    该方法执行的结果是,在当前时刻精确知晓种群中最优解和最差解的坐标。这使算法能够跟踪搜索动态,并在决策中使用这些数据,以更好地找到最优解。

    //————————————————————————————————————————————————————————————————————
    //--- Update the best solution
    void C_AO_DEA::Revision ()
    {
      int bestIND = -1;
      fW = fB;
    
      // Find the best and worst solutions in the current population
      for (int i = 0; i < popSize; i++)
      {
        // Update the best solution
        if (a [i].f > fB)
        {
          fB = a [i].f;
          bestIND = i;
        }
    
        // Update the worst solution
        if (a [i].f < fW)
        {
          fW = a [i].f;
        }
      }
    
      // Copy the coordinates of the best solution
      if (bestIND != -1)
      {
        ArrayCopy (cB, a [bestIND].c, 0, 0, WHOLE_ARRAY);
      }
    }
    //————————————————————————————————————————————————————————————————————
    


    测试结果

    现在让我们看看结果。可以立即注意到,该算法能够很好且快速地处理各种类型的任务。

    DEA|海豚回声定位算法|100.0|2.0|2.0|1.0|

    =============================
    5 Hilly's;函数运行次数:10000;结果:0.7599517883429889
    25 Hilly's;函数运行次数:10000;结果:0.6757192867862007
    500 Hilly's;函数运行次数:10000;结果:0.34170057553968197
    =============================
    5 Forest's;函数运行次数:10000;结果:0.8958173952258406
    25 Forest's;函数运行次数:10000;结果:0.6422393144820473
    500 Forest's;函数运行次数:10000;结果:0.23940903266305935
    =============================
    5 Megacity's;函数运行次数:10000;结果:0.6153846153846154
    25 Megacity's;函数运行次数:10000;结果:0.4403076923076923
    500 Megacity's;函数运行次数:10000;结果:0.15115384615384736
    =============================
    总得分:4.76168 (52.91%)

    可视化结果显示,低维函数(绿色)的结果分布较分散,而中维函数(蓝色)的结果表现良好。

    Hilly

    海豚回声定位算法(DEA)在 Hilly 函数上的表现

    Forest

    海豚回声定位算法(DEA)在 Forest 函数上的表现

    Megacity

    海豚回声定位算法(DEA)在 Megacity 函数上的表现

    根据测试结果,DEA 算法在排名表中位列第 25 名。

    # 算法 说明 Hilly Hilly
    最终结果
    Forest Forest
    最终结果
    Megacity (离散) Megacity
    最终结果
    最终
    结果
    % of
    MAX
    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 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
    13 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
    14 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
    15 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
    16 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
    17 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
    18 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
    19 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
    20 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
    21 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
    22 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
    23 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
    24 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
    25 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
    26 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
    27 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
    28 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
    29 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
    30 (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
    31 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
    32 TSm 禁忌搜索优化算法 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
    33 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
    34 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
    35 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
    36 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
    37 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
    38 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
    39 CMAES covariance_matrix_adaptation_evolution_strategy 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
    40 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
    41 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
    42 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
    43 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
    44 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
    45 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
    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


    总结

    该算法的主要优势包括:自适应搜索控制:预定概率逐渐增加,将平衡从探索转向开发; 动态自适应;集体记忆:累积适应度保存并传播关于有前景区域的信息;多样性机制:对最佳位置的信息重置刺激了对新区域的探索。

    该算法的优势在于其自适应性。动态系数使算法能够对种群状态做出响应,并适应任务的节奏。当海豚分散在搜索空间中时,算法鼓励局部探索。当它们开始向目标收敛时,算法推动它们走向新的视野,防止它们困在局部成功的错觉中。

    累积适应度是种群的集体记忆,每一次发现都会在解空间中留下回声。但与简单的累积不同,该算法能够遗忘 —— 将最佳位置清零反而会带来更好的发现,这看似矛盾却真实有效。

    这不仅仅是一个比喻 —— 这是一种优化哲学,其中回声定位器的每一次脉冲声都承载着关于可能性空间的信息。

    tab

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

    图表

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

    DEA算法的优缺点:

    优点:

    1. 在中维和高维函数上收敛性良好。
    2. 外部参数数量少。

    缺点:

    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_DEA.mq5
    脚本 DEA 测试程序

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

    附加的文件 |
    DEA.zip (236.09 KB)
    神经网络在交易中的应用:用于多元时间序列预测的 LSTM 优化(终篇) 神经网络在交易中的应用:用于多元时间序列预测的 LSTM 优化(终篇)
    我们继续实现 DA-CG-LSTM 框架,该框架为时间序列分析和预测提供了创新方法。使用 CG-LSTM 和双重注意力机制可以更准确地检测数据中的长期和短期依赖关系,这在金融市场分析中尤其有用。
    您应该了解的MQL5向导技巧(第七十四部分):结合监督学习运用一目均衡表与ADX-Wilder形态 您应该了解的MQL5向导技巧(第七十四部分):结合监督学习运用一目均衡表与ADX-Wilder形态
    本文为前一篇文章的延续,之前我们介绍了一目均衡表(Ichimoku)与ADX指标组合,本篇将探讨如何通过监督学习对这一指标组合进行优化改进。一目均衡表与ADX是一组互补型指标组合,前者侧重支撑/阻力判断,后者侧重趋势识别。我们采用的监督学习方案基于深度谱混合核神经网络,对该指标组合的预测结果进行精细调优。与之前相同,相关逻辑封装在自定义信号类文件中,可配合MQL5向导直接组装成智能交易系统(EA)。
    从基础到中级:继承 从基础到中级:继承
    毫无疑问,要理解本文所述材料的工作原理和原因,你需要投入大量时间。这是因为这里将要展示的所有内容最初都是面向对象编程的,但实际上它们是基于结构化编程的原理。
    神经网络在交易中的应用:用于多元时间序列预测的 LSTM 优化(DA-CG-LSTM) 神经网络在交易中的应用:用于多元时间序列预测的 LSTM 优化(DA-CG-LSTM)
    本文介绍了 DA-CG-LSTM 算法,该算法为时间序列分析和预测提供了新的方法。它解释了创新的注意力机制和模型灵活性如何提高预测准确性。