English Русский Español Deutsch 日本語 Português
preview

大气云模型优化(ACMO):实战

MetaTrader 5测试者 | 26 五月 2025, 11:41
100 0
Andrey Dik
Andrey Dik

内容

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


概述

在科学的世界里,技术和自然相互交织,一种用于优化复杂问题的独特元启发式ACMO(大气云模型优化)算法应运而生。在上一篇文章中,我们从技术角度分析了该算法的实现,该算法基于各种气象参数模拟大气中云的形成和移动过程。在第一部分中,我们创建了一个用于管理云模拟的类,其中包含初始化、云移动、更新区域属性以及其他过程的方法。

我们已经将搜索空间划分为区域。这些区域的初始湿度和压力值已经确定。我们设置了模型的参数,例如:初始熵、超熵、云形成的阈值湿度等。下一步是通过选择湿度高的区域来生成云。计算云层的中心、熵和超熵。在云层生成后,我们更新了区域中的湿度和压力等气象参数。此外,我们还实现了云向低压区域的移动以及根据云层在区域间的移动来更新云的特征,以及云的消散。 

接下来还需要做什么?我们需要实现水滴的随机放置及其在云层中的分布,完成降雨过程并更新全局解,同时在我们的测试函数上以不同参数测试模型,以评估其性能和准确性。我们将对降雨和水滴形成过程进行改进,以实现种群中更具潜力区域相关信息的更全面交换。


算法实现

让我们以伪代码的形式来描述整个气象过程,这样就能基于此构建出算法的最终版本:

1. 在第一个周期(epoch)中,云层被随机放置:
   EnCk = EnM0;
   HeCk = HeM0;
//------------------------------------------------------------------------------
1.1 云层向气压较低的区域移动:
   β = deltaP / normP
   d = Tck.x - Cck.c
   VC = β * d;
   Ck = Ck + VC

移动后水滴数量的变化:
   nk = nk × (1 - γ)

熵与超熵的变化:
   α = ΔP / ΔPmax;
   EnCk = EnCk * (1 + α)
   HeCk = HeCk * (1 - α)
//------------------------------------------------------------------------------
2. 降雨过程,即水滴的降落:
云层间水滴的分布与该区域的湿度成正比
云层中水滴的数量增加
//------------------------------------------------------------------------------
3. 计算水滴的适应度函数
//------------------------------------------------------------------------------
4. 在水滴降落的区域更新全局解和最低气压
//------------------------------------------------------------------------------
5. 检查云层的消散情况,并在超过阈值的区域创建新的云层以替代已消散的云层: 因膨胀超过允许值(云层破裂)而消散的规则:
   En > 5 * EnM0_t
因湿度含量低于阈值(云层干燥)而消散的规则:   dCk < dMin

可能形成云层区域的阈值:   HT = H_min + λ * (H_max - H_min);
//------------------------------------------------------------------------------
6. 计算新云朵的熵与超熵:
   En = EnM0 / (1 + 2.72 ^ (-(8 - 16 * (t / maxT))))
   He = HeM0 / (1 + 2.72 ^ ((8 - 16 * (t / maxT))))

我们继续。让我们看一下C_AO_ACMO类的Moving方法。该方法执行两个操作:MoveClouds (revision)负责云层的移动,RainProcess (revision)处理降雨,这取决于云层的状态和revision参数。Moving方法执行与云动力学和降雨相关的两个主要动作。它封装了云层如何移动以及与降雨过程交互的逻辑。因此,Moving方法用于更新云层和降雨的状态,作为天气模拟的一部分。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ACMO::Moving ()
{
  MoveClouds       (revision);
  RainProcess      (revision);
}
//——————————————————————————————————————————————————————————————————————————————

让我们审视C_AO_ACMO类的MoveClouds方法:

1. 第一块(如果revfalse):该方法创建具有随机中心的云层。对于每朵云和每个坐标:

  • 在给定范围内生成云层中心的随机值(使用RNDfromCI函数)。
  • 使用SeInDiSp调整中心,以规范化数值。
  • 通过GetRegionIndex确定云所在的区域索引。
  • 设置云的熵和初始熵值。
  • 将超熵的初始值设置为hyperEntropy
该方法执行终止。

2. 第二块(如果revtrue):

  • 如果revtrue,该方法开始寻找压力最低的区域。
  • 创建数组以存储最低湿度区域的索引lHind和用于规范化压力的normP

3. 循环寻找压力最低的区域:

  • 对于每个c坐标,确定所有区域中的最小和最大压力。
  • 将压力最低区域的索引存储在lHind数组中。
  • 将每个坐标的规范化压力存储在normP中。

4. 每朵云和每个坐标的移动:

  • 如果云层已经处于压力最低的区域,则跳过迭代。
  • 随机选择一个压力更低的目标区域。
  • 计算当前区域和目标区域之间的压力差。
  • 规范化压力值,并计算云的移动速度VC
  • 根据移动速度更新云层的中心。
  • 更新区域索引。
  • 根据压力的变化更新云的熵。
  • 当云层中的湿度减少时,更新超熵,将该值限制在最大值8以内。

MoveClouds方法负责将云层移动到压力较低的区域,并更新它们的参数,如熵和超熵。该方法实现了一个动态模型,反映了大气的变化。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ACMO::MoveClouds (bool &rev)
{
  //----------------------------------------------------------------------------
  if (!rev)
  {
    //creating clouds with random centers---------------------------------------
    for (int i = 0; i < cloudsNumber; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        clouds [i].center [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        clouds [i].center [c] = u.SeInDiSp  (clouds [i].center [c], rangeMin [c], rangeMax [c], rangeStep [c]);

        clouds [i].regionIndex [c] = GetRegionIndex (clouds [i].center [c], c);

        clouds [i].entropy      [c] = entropy [c] * EnM0;
        clouds [i].entropyStart [c] = clouds [i].entropy [c];
      }

      clouds [i].hyperEntropy = HeM0;
    }

    return;
  }

  //search for the region with the lowest pressure------------------------------
  int targetRegion = 0;

  int lHind []; //lowest humidity index
  ArrayResize     (lHind, coords);
  ArrayInitialize (lHind, 0);

  double normP [];
  ArrayResize (normP, coords);
  double minP;
  double maxP;

  for (int c = 0; c < coords; c++)
  {
    minP =  DBL_MAX;
    maxP = -DBL_MAX;

    for (int r = 0; r < regionsNumber; r++)
    {
      if (areas [c].regions [r].pressure < areas [c].regions [lHind [c]].pressure)
      {
        lHind [c] = r;
      }

      if (areas [c].regions [r].pressure < minP) minP = areas [c].regions [r].pressure;
      if (areas [c].regions [r].pressure > maxP) maxP = areas [c].regions [r].pressure;
    }

    normP [c] = maxP - minP;
  }

  //moving the cloud to a region with less pressure-----------------------------
  int    clRegIND = 0;
  double deltaP   = 0.0;
  double α        = 0.0; // Entropy factor
  double β        = 0.0; // Atmospheric pressure factor
  double VC       = 0.0; // Cloud velocity
  double d        = 0.0; // Cloud direction

  for (int i = 0; i < cloudsNumber; i++)
  {
    for (int c = 0; c < coords; c++)
    {
      //find a region with lower pressure---------------------------------------
      if (clouds [i].regionIndex [c] == lHind [c]) continue;

      clRegIND = clouds [i].regionIndex [c];

      do targetRegion = u.RNDminusOne (regionsNumber);
      while (areas [c].regions [clRegIND].pressure < areas [c].regions [targetRegion].pressure);

      //------------------------------------------------------------------------
      deltaP = areas [c].regions [clRegIND].pressure - areas [c].regions [targetRegion].pressure;

      β = deltaP / normP [c];
      d = areas [c].regions [targetRegion].x - areas [c].regions [clRegIND].centre;

      VC = β * d;

      clouds [i].center      [c] += VC;
      clouds [i].center      [c] = u.SeInDiSp (clouds [i].center [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      clouds [i].regionIndex [c] = GetRegionIndex (clouds [i].center [c], c);

      α = β;
      clouds [i].entropy [c] *=(1 + α);
    }

    clouds [i].droplets     *=(1 - γ);
    clouds [i].hyperEntropy *=(1 + α);
    if (clouds [i].hyperEntropy > 8) clouds [i].hyperEntropy = 8;
  }
}
//——————————————————————————————————————————————————————————————————————————————

接下来,让我们分析C_AO_ACMO类的GetRegionIndex方法。方法描述:

1. 计算区域的位置。使用指定的point计算 regPos区域索引,并使用floor函数向下取整到最接近的整数。
2. 检查边界。该块检查计算出的regPos索引是否超出允许的值(其不能超过允许的区域数量)。
3. 此方法返回点所在的区域索引。

GetRegionIndex方法旨在确定给定点在某个范围内所在的区域索引。它考虑了区域的数量,并正确处理了点位于范围边界的情况。 

//——————————————————————————————————————————————————————————————————————————————
int C_AO_ACMO::GetRegionIndex (double point, int ind)
{
  int regPos = (int)floor ((point - rangeMin [ind]) / ((rangeMax [ind] - rangeMin [ind]) / regionsNumber));

  if (regPos >= regionsNumber) regPos = regionsNumber - 1;

  return regPos;
}
//——————————————————————————————————————————————————————————————————————————————

描述C_AO_ACMO类的下一个方法RainProcess

2. 初始化数组:

  • 创建两个数组: cloud用于存储云的值,drops用于存储每朵云的水滴数量。
  • 这两个数组的大小取决于云层的数量(cloudsNumber)。

3. 初始化cloud数组:

  • 如果revfalse,则使用1.0的值初始化所有cloud数组元素。
  • 否则,初始化cloud数组值为0.0,然后计算每朵云的湿度。

4. 湿度计算:

  • 对于每朵云和每个坐标,根据区域计算湿度。
  • 如果湿度不等于-DBL_MAX,则将其添加到对应的cloud数组元素中。否则,添加minGp的最小水滴值。

5. 水滴分布:

  • 调用DropletsDistribution方法,根据cloud数组中的值分布水滴。

6. 主要的水滴处理循环,对于每朵云和每滴水滴:

  • 计算distcentrexMinxMax的值。
  • 使用正态(高斯)分布生成x值。
  • 如果 x值超出范围,则使用RNDfromCI方法进行修正。
  • 使用SeInDiSp方法对x值进行规范化,并将其保存在a数组中。

在处理完每朵云的所有水滴后,更新该云层中的水滴总数。因此,RainProcess方法模拟了从云中落下的雨,考虑了湿度和水滴的分布。初始化数组,计算每朵云的湿度,分配水滴,并为每滴水滴生成值,假设其符合正态分布。 

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ACMO::RainProcess (bool &rev)
{
  //to shed drops from every cloud----------------------------------------------
  double cloud [];
  int    drops [];
  ArrayResize (cloud, cloudsNumber);
  ArrayResize (drops, cloudsNumber);

  if (!rev)
  {
    ArrayInitialize (cloud, 1.0);
  }
  else
  {
    ArrayInitialize (cloud, 0.0);

    double humidity;

    for (int i = 0; i < cloudsNumber; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        for (int r = 0; r < regionsNumber; r++)
        {
          humidity = areas [c].regions [clouds [i].regionIndex [r]].humidity;
          if (humidity != -DBL_MAX) cloud [i] += humidity;
          else                      cloud [i] += minGp;
        }
      }
    }
  }

  DropletsDistribution (cloud, drops);

  double dist   = 0.0;
  double centre = 0.0;
  double xMin   = 0.0;
  double xMax   = 0.0;
  double x      = 0.0;
  int    dCNT   = 0;

  for (int i = 0; i < cloudsNumber; i++)
  {
    for (int dr = 0; dr < drops [i]; dr++)
    {
      for (int c = 0; c < coords; c++)
      {
        dist   = clouds [i].entropy [c];
        centre = clouds [i].center  [c];
        xMin   = centre - dist;
        xMax   = centre + dist;

        x = u.GaussDistribution (centre, xMin, xMax, clouds [i].hyperEntropy);

        if (x < rangeMin [c]) x = u.RNDfromCI (rangeMin [c], centre);
        if (x > rangeMax [c]) x = u.RNDfromCI (centre, rangeMax [c]);

        x = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]);

        a [dCNT].c [c] = x;
      }

      dCNT++;
    }

    clouds [i].droplets += drops [i];
  }
}
//——————————————————————————————————————————————————————————————————————————————

C_AO_ACMO类的下一个方法DropletsDistribution旨在根据云层的湿度在云层之间分配水滴。让我们来仔细看一下。

2. 变量初始化:

  • 初始化minHumidity为最大值,以便能够找到最小湿度。
  • indMinHumidity存储具有最小湿度的云索引。
  • totalHumidity用于存储所有云层的湿度总和。

3. 通过计算所有云层的湿度总和,确定湿度最低的云层。

4. 按比例分配水滴——对于每朵云,根据其湿度与总湿度的比例计算水滴数量。将该值存储在droplets数组中。

5. 剩余水滴的分配:

  • 首先,计算已分配水滴的总数totalDrops
  • 然后计算剩余水滴的数量(remainingDrops)。
  • 如果有剩余的水滴,它们将被添加到湿度最低的云中。

DropletsDistribution方法根据云层的湿度有效地在云层之间分配水滴。它首先按比例分配水滴,然后通过将剩余的水滴添加到湿度最低的云层中来调整分配。这使得降雨的模拟过程更加逼真,同时保持水滴数量的恒定,其对应于算法外部参数中的种群大小。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ACMO::DropletsDistribution (double &cloud [], int &droplets [])
{
  double minHumidity    = DBL_MAX;
  int    indMinHumidity = -1;
  double totalHumidity  = 0; //total amount of humidity in all clouds

  for (int i = 0; i < ArraySize (cloud); i++)
  {
    totalHumidity += cloud [i];

    if (cloud [i] < minHumidity)
    {
      minHumidity = cloud [i];
      indMinHumidity = i;
    }
  }

  // Filling the droplets array in proportion to the value in clouds
  for (int i = 0; i < ArraySize (clouds); i++)
  {
    droplets [i] = int((cloud [i] / totalHumidity)*popSize); //proportional distribution of droplets
  }

  // Distribute the remaining drops, if any
  int totalDrops = 0;

  for (int i = 0; i < ArraySize (droplets); i++)
  {
    totalDrops += droplets [i];
  }

  // If not all drops are distributed, add the remaining drops to the element with the lowest humidity
  int remainingDrops = popSize - totalDrops;

  if (remainingDrops > 0)
  {
    droplets [indMinHumidity] += remainingDrops; //add the remaining drops to the lightest cloud
  }
}
//——————————————————————————————————————————————————————————————————————————————

C_AO_ACMO类的Revision方法执行系统状态更新。让我们来审视它:

1. 遍历a数组的元素(优化智能体的种群)该循环遍历大小为popSizea数组的所有元素:

  • 如果当前元素的f适应度值大于当前的fB最大适应度值,则更新fB ,同时将ind索引设置为当前索引。
  • 同时在数组的所有元素中寻找最小的f适应度值,如果当前值小于minGp,则更新minGp

2. 复制数据:如果找到了具有最大f适应度值的元素(ind不是-1),则将数据(即a[ind])从c数组复制到cB数组。

3. 更新区域属性:调用UpdateRegionProperties方法。它更新不同区域的湿度和压力参数。

4. 云的生成:调用负责旧云消散和新云形成的GenerateClouds方法。

5. 状态更新:

  • 设置revision标识为true,表示系统已更新初始状态。
  • 增加epochNow计数器以跟踪迭代次数。

Revision方法负责更新与云相关的系统状态。它找到最大的f适应度值,更新相关参数,初始化新的云,并更新区域属性。该方法是保持模型数据最新、使系统能够适应变化的关键。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ACMO::Revision ()
{
  //----------------------------------------------------------------------------
  int ind = -1;

  for (int i = 0; i < popSize; i++)
  {
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ind = i;
    }

    if (a [i].f < minGp) minGp = a [i].f;
  }

  if (ind != -1) ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY);

  //----------------------------------------------------------------------------
  UpdateRegionProperties (); //updating humidity and pressure in the regions
  GenerateClouds         (); //disappearance of clouds and the creation of new ones

  revision = true;
  epochNow++;
}
//——————————————————————————————————————————————————————————————————————————————

C_AO_ACMO类的GenerateClouds方法负责根据各种因素(如湿度和熵)形成云并管理它们的状态。方法描述:

1. 计算湿度阈值:调用CalculateHumidityThreshold函数,返回云形成的所需湿度阈值。

2. 存储区域索引的结构:

  • 定义了S_Areas结构。该结构包含一个能够形成云的区域索引数组。 
  • ar方法的大小初始化为等于coords坐标的数量。

3. 收集区域信息:通过双重循环测试每个区域,看它是否达到湿度阈值。如果一个区域的湿度大于阈值,该区域的索引将被添加到相应的S_Areas结构的regsIND数组中。

4. 检查云消散条件:

  • 对于每朵云,检查其熵是否超过一定限度(初始熵的5倍)。如果满足此条件,则认为云已经解体。
  • 然后检查云层的湿度是否小于最小值dMin,这也可能导致云的解体。

5. 在最潮湿的区域形成新云:

  • 如果云解体了,将在最潮湿的区域之一形成一朵新云对于每个坐标,随机选择一个区域索引,为云层指定新的中心坐标和区域索引。
  • 然后调用CalculateNewEntropy函数。该函数根据当前迭代次数重新计算新云的熵。

GenerateClouds方法根据湿度和熵管理云的形成和解体。其收集能够形成云层的区域信息,检查现有云层是否解体,并在合适的区域创建新云层。该方法是动态控制模型中云状态的关键。                                                                                                                                                    

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ACMO::GenerateClouds ()
{
  //Collecting statistics of regions capable of creating clouds-----------------
  double Ht = CalculateHumidityThreshold ();

  struct S_Areas
  {
      int regsIND []; //index of the potential region
  };

  S_Areas ar [];
  ArrayResize (ar, coords);

  int sizePr = 0;

  for (int i = 0; i < coords; i++)
  {
    for (int r = 0; r < regionsNumber; r++)
    {
      if (areas [i].regions [r].humidity > Ht)
      {
        sizePr = ArraySize (ar [i].regsIND);
        sizePr++;
        ArrayResize (ar [i].regsIND, sizePr, coords);
        ar [i].regsIND [sizePr - 1] = r;
      }
    }
  }

  //Check the conditions for cloud decay----------------------------------------
  bool   cloudDecay = false;

  for (int i = 0; i < cloudsNumber; i++)
  {
    cloudDecay = false;

    //checking the cloud for too much entropy-----------------------------------
    for (int c = 0; c < coords; c++)
    {
      if (clouds [i].entropy [c] > 5.0 * clouds [i].entropyStart [c])
      {
        //Print ("Disintegration of cloud #", i, " - tore at epoch ", epochNow);
        cloudDecay = true;
        break;
      }
    }

    //checking the cloud for decay----------------------------------------------
    if (!cloudDecay)
    {
      if (clouds [i].droplets < dMin)
      {
        //Print ("Disintegration of cloud #", i, " - dried up at epoch ", epochNow);
        cloudDecay = true;
      }
    }

    //if the cloud has decayed--------------------------------------------------
    int regIND = 0;

    if (cloudDecay)
    {
      //creating a cloud in a very humid region---------------------------------
      for (int c = 0; c < coords; c++)
      {
        regIND = u.RNDminusOne (ArraySize (ar [c].regsIND));
        regIND = ar [c].regsIND [regIND];

        clouds [i].center      [c] = areas [c].regions [regIND].x;
        clouds [i].regionIndex [c] = regIND;
      }

      CalculateNewEntropy (clouds [i], epochNow);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

C_AO_ACMO类的CalculateHumidityThreshold方法负责计算云形成所需的湿度阈值。以下是详细步骤:

1. 通过双重循环以寻找最小湿度。外层循环遍历所有coords坐标,而内层循环在每个坐标中迭代所有区域(regionsNumber)。如果区域的湿度不等于-DBL_MAX,则进行检查:如果当前湿度小于当前的H_min,则更新H_min

2. 该方法返回H_min加上λH_maxH_min之差的乘积,表示云形成所需的湿度阈值。

CalculateHumidityThreshold方法根据所有区域中的最小湿度计算湿度阈值,并根据最大湿度和λ比例进行调整。这使得可以根据环境状态确定云形成的条件。

//——————————————————————————————————————————————————————————————————————————————
double C_AO_ACMO::CalculateHumidityThreshold ()
{
  double H_max = fB;
  double H_min = DBL_MAX;

  for (int c = 0; c < coords; c++)
  {
    for (int r = 0; r < regionsNumber; r++)
    {
      if (areas [c].regions [r].humidity != -DBL_MAX)
      {
        if (areas [c].regions [r].humidity < H_min)
        {
          H_min = areas [c].regions [r].humidity;
        }
      }
    }
  }

  return H_min + λ * (H_max - H_min);
}
//——————————————————————————————————————————————————————————————————————————————

C_AO_ACMO类的CalculateNewEntropy方法负责计算由S_ACMO_Cloud结构表示的云的新熵和超熵。让我们详细了解一下:

1. 计算熵:

  • 循环遍历所有的coords坐标。
  • 对于每个坐标,使用以下公式计算新的熵值"cl.entropy [c]":En = (entropy[c] * EnM0) / (1 + e^(-(8 - 16 * (t / epochs))))
  • cl.entropyStart [c]cl.entropy [c]被初始化为entropy [c]的值,这有助于保留熵的初始值。

2. 计算超熵:He = 1 / (1 + e^(8 - 16 * (t / epochs)))

3. 使用u对象的Scale方法对超熵进行缩放,其允许我们使用HeM0参数和8.0将超熵值缩放到给定范围(从0到8)。

CalculateNewEntropy方法根据当前时间t和指定的参数更新云的熵和超熵值。 

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ACMO::CalculateNewEntropy (S_ACMO_Cloud &cl, int t)
{
  //----------------------------------------------------------------------------
  //En: 1/(1+2.72^(-(8-16*(t/maxT))))
  for (int c = 0; c < coords; c++)
  {
    cl.entropy      [c] = entropy [c] * EnM0 / (1.0 + pow (M_E, (-(8.0 - 16.0 * (t / epochs)))));
    cl.entropyStart [c] = cl.entropy [c] = entropy [c];
  }

  //----------------------------------------------------------------------------
  //He: 1/(1+2.72^((8-16*(t/maxT))))
  cl.hyperEntropy = 1.0 / (1.0 + pow (M_E, ((8.0 - 16.0 * (t / epochs)))));

  cl.hyperEntropy = u.Scale (cl.hyperEntropy, 0.0, 8.0, HeM0, 8.0);
}
//——————————————————————————————————————————————————————————————————————————————

En和He 2

图例1. 根据当前迭代次数计算ζ比率的不同方程变体我们可以选择一个方程,并使用每一个方程来测试算法(已对代码行进行批注)


测试结果

让我们继续进行算法测试。云形成的大气模型,按照作者的构想,其工作原理如下:

//original version
ACMO|Atmospheric Cloud Model Optimization|50.0|5.0|10.0|0.2|5.0|5.0|0.9|0.2|
=============================
5 Hilly's; Func runs: 10000; result: 0.6017884495404766
25 Hilly's; Func runs: 10000; result: 0.3426222382089618
500 Hilly's; Func runs: 10000; result: 0.2526410178225118
=============================
5 Forest's; Func runs: 10000; result: 0.4780554376190664
25 Forest's; Func runs: 10000; result: 0.261057831391174
500 Forest's; Func runs: 10000; result: 0.17318135866144563
=============================
5 Megacity's; Func runs: 10000; result: 0.3507692307692307
25 Megacity's; Func runs: 10000; result: 0.16153846153846158
500 Megacity's; Func runs: 10000; result: 0.09632307692307775
=============================
总分:2.71798 (30.20%)

遗憾的是,结果远低于预期。我认为,尽管云形成原理的模型非常完美,包含了大量不同的方程和旨在避免陷入极端情况的逻辑操作,但算法的收敛性仍然很低。该算法缺乏智能体之间关于最佳解决方案的直接交互和信息交换,而这种交互的存在通常会提高算法的搜索质量。因此,我决定通过从最佳水滴向最差水滴的概率性信息传递来增加信息交换。现在让我们看一下结果如何:

ACMOm|Atmospheric Cloud Model Optimization|50.0|4.0|10.0|0.2|0.2|2.0|0.9|0.9|
=============================
5 Hilly's; Func runs: 10000; result: 0.9032099148349984
25 Hilly's; Func runs: 10000; result: 0.48545807643133143
500 Hilly's; Func runs: 10000; result: 0.30403284557071203
=============================
5 Forest's; Func runs: 10000; result: 0.8026793420899985
25 Forest's; Func runs: 10000; result: 0.3785708322859447
500 Forest's; Func runs: 10000; result: 0.1917777390119122
=============================
5 Megacity's; Func runs: 10000; result: 0.6230769230769231
25 Megacity's; Func runs: 10000; result: 0.244
500 Megacity's; Func runs: 10000; result: 0.10795384615384714
=============================
总分:4.04076 (44.90%)

结果有了显著的提升。这个理念取得了成功。其核心思想是:如果其他水滴的湿度更高(在算法的上下文中,这对应于适应度),那么就以一定的概率将关于最优解的信息传递给云层中的水滴。 

最后,对于每朵云,通过从drops数组中添加相应的值来更新从该云落下的水滴总数(对于云来说,这是一个表示湿度的指标)。RainProcess方法实现了一个机制,该机制模拟了降雨,考虑了湿度、水滴分布以及与种群的交互。代码中更改的部分以绿色突出显示。

对于每个生成的x值,从种群中随机选择一个p索引。根据概率(95%),更新a数组中的值。这个值代表一个种群或一组解。最后,通过从drops数组中添加相应的值,更新每朵云落下的水滴总数。

//——————————————————————————————————————————————————————————————————————————————
void C_AO_ACMO::RainProcess (bool &rev)
{
  //to shed drops from every cloud----------------------------------------------
  double cloud [];
  int    drops [];
  ArrayResize (cloud, cloudsNumber);
  ArrayResize (drops, cloudsNumber);

  if (!rev)
  {
    ArrayInitialize (cloud, 1.0);
  }
  else
  {
    ArrayInitialize (cloud, 0.0);

    double humidity;

    for (int i = 0; i < cloudsNumber; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        humidity = areas [c].regions [clouds [i].regionIndex [c]].humidity;
       
        if (humidity != -DBL_MAX) cloud [i] += humidity;

        else                      cloud [i] += minGp;
      }
    }
  }

  DropletsDistribution (cloud, drops);
  //ArrayPrint (drops);

  double dist   = 0.0;
  double centre = 0.0;
  double xMin   = 0.0;
  double xMax   = 0.0;
  double x      = 0.0;

  int    dCNT   = 0;


  for (int i = 0; i < cloudsNumber; i++)
  {
    for (int dr = 0; dr < drops [i]; dr++)
    {
      for (int c = 0; c < coords; c++)
      {
        dist   = clouds [i].entropy [c];
        centre = clouds [i].center  [c];
        xMin   = centre - dist;
        xMax   = centre + dist;

        x = u.GaussDistribution (centre, xMin, xMax, clouds [i].hyperEntropy);

        if (x < rangeMin [c]) x = u.RNDfromCI (rangeMin [c], centre);
        
        if (x > rangeMax [c]) x = u.RNDfromCI (centre, rangeMax [c]);

        x = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]);

        int p = u.RNDminusOne (popSize);

        if (a [p].f > a [dCNT].f)
        {
          if (u.RNDprobab () < 0.95) a [dCNT].c [c] = a [p].c [c];
        }

        else
        {
          a [dCNT].c [c] = x;
        }
      }
      dCNT++;
    }

    clouds [i].droplets += drops [i];
  }
}
//——————————————————————————————————————————————————————————————————————————————

算法运行的可视化结果如下:算法的收敛性良好,收敛图上优化参数数量较少时的长平直部分表明算法有陷入局部极值的倾向,而随着参数数量的增加,这个缺陷消失。

在可视化中,云表现为密集的簇,但通过选择不同的外部参数设置(区域数量、云的数量、初始熵和干燥阈值),它们可以模拟天空中漂浮的云的外观,就像在自然界中一样。

Hilly值

ACMO在Hilly测试函数上

Forest值

ACMO在Forest测试函数上

Megacity

ACMO在Megacity测试函数上

根据修改后的版本的测试结果,该算法排名第27位,这是一个相当稳定的指标。我想强调的是,表格中现在始终包含45种算法,事实上,随着每一种新算法的加入,前一个和后一个算法之间的差距将逐渐缩小。因此,我们可以认为这个表格代表了已知的顶级算法。 

# AO 说明 Hilly值 Hilly最终值 Forest值 Forest最终值 Megacity (离散) 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)
1ANS跨邻域搜索0.949480.847760.438572.235811.000000.923340.399882.323230.709230.634770.230911.574916.13468.15
2CLA密码锁算法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彗星尾算法0.953460.863190.277702.094350.997940.857400.339492.194840.887690.564310.105121.557125.84664.96
6SDSm随机扩散搜索 M0.930660.854450.394762.179880.999830.892440.196192.088460.723330.611000.106701.441035.70963.44
7AAm射箭算法M0.917440.708760.421602.047800.925270.758020.353282.036570.673850.552000.237381.463235.54861.64
8ESG社会群体的进化0.999060.796540.350562.146161.000000.828630.131021.959650.823330.553000.047251.423585.52961.44
9SIA模拟各向同性退火0.957840.842640.414652.215130.982390.795860.205071.983320.686670.493000.090531.270205.46960.76
10ACS人工协同搜索0.755470.747440.304071.806981.000000.888610.224132.112740.690770.481850.133221.305835.22658.06
11ASO无序社会优化0.848720.746460.314651.909830.961480.791500.238031.991010.570770.540620.166141.277525.17857.54
12TSEA龟壳演化算法0.967980.644800.296721.909490.994490.619810.227081.841390.690770.426460.135981.253225.00455.60
13DE差分进化0.950440.616740.303081.870260.953170.788960.166521.908650.786670.360330.029531.176534.95555.06
14CRO化学反应优化0.946290.661120.298531.905930.879060.584220.211461.674730.758460.426460.126861.311784.89254.36
15BSA鸟群算法0.893060.649000.262501.804550.924200.711210.249391.884790.693850.326150.100121.120124.80953.44
16HS和声搜索0.865090.687820.325271.878180.999990.680020.095901.775920.620000.422670.054581.097254.75152.79
17SSG树苗播种和生长0.778390.649250.395431.823080.859730.624670.174291.658690.646670.441330.105981.193984.67651.95
18BCOm细菌趋化性优化算法M0.759530.622680.314831.697040.893780.613390.225421.732590.653850.420920.144351.219124.64951.65
19(PO)ES(PO) 进化策略0.790250.626470.429351.846060.876160.609430.195911.681510.590000.379330.113221.082554.61051.22
20TSm禁忌搜索M0.877950.614310.291041.783300.928850.518440.190541.637830.610770.382150.121571.114494.53650.40
21BSO头脑风暴优化0.937360.576160.296881.810410.931310.558660.235371.725340.552310.290770.119140.962224.49849.98
22WOAm鲸鱼优化算法M0.845210.562980.262631.670810.931000.522780.163651.617430.663080.411380.113571.188034.47649.74
23AEFA人工电场算法0.877000.617530.252351.746880.927290.726980.180641.834900.666150.116310.095080.877544.45949.55
24ACOm蚁群优化 M0.881900.661270.303771.846930.858730.586800.150511.596040.596670.373330.024720.994724.43849.31
25BFO-GA细菌觅食优化 - ga0.891500.551110.315291.757900.969820.396120.063051.428990.726670.275000.035251.036924.22446.93
26ABHA人工蜂巢算法0.841310.542270.263041.646630.878580.477790.171811.528180.509230.338770.103970.951974.12745.85
27ACMO大气云模型优化0.903210.485460.304031.692700.802680.378570.191781.373030.623080.244000.107950.975034.04144.90
28ASBO适应性社会行为优化0.763310.492530.326191.582020.795460.400350.260971.456770.264620.171690.182000.618313.65740.63
29MEC思维进化计算0.695330.533760.326611.555690.724640.330360.071981.126980.525000.220000.041980.786983.47038.55
30IWO入侵杂草优化0.726790.522560.331231.580580.707560.339550.074841.121960.423330.230670.046170.700173.40337.81
31Micro-AIS微型人工免疫系统0.795470.519220.308611.623300.729560.368790.093981.192330.376670.158670.028020.563353.37937.54
32COAm布谷鸟优化算法 M0.758200.486520.313691.558410.740540.280510.055991.077040.505000.174670.033800.713473.34937.21
33SDOm螺旋动力学优化 M0.746010.446230.296871.489120.702040.346780.109441.158260.428330.167670.036630.632633.28036.44
34NMmNelder-Mead方法 M0.738070.505980.313421.557470.636740.283020.082211.001970.446670.186670.040280.673623.23335.92
35FAm萤火虫算法 M0.586340.472280.322761.381380.684670.374390.109081.168140.286670.164670.047220.498553.04833.87
36GSA引力搜索算法0.647570.491970.300621.440160.539620.363530.099451.002600.326670.122000.019170.467832.91132.34
37BFO细菌觅食优化0.611710.432700.313181.357590.544100.215110.056760.815970.421670.138000.031950.591622.76530.72
38ABC人工蜂群0.633770.424020.308921.366710.551030.218740.056230.826000.340000.142000.031020.513022.70630.06
39BA蝙蝠算法0.597610.459110.352421.409150.403210.193130.071750.668100.210000.101000.035170.346172.42326.93
40AAA人工藻类算法0.500070.320400.255251.075720.370210.222840.167850.760890.278460.148000.097550.524022.36126.23
41SA模拟退火0.557870.421770.315491.295130.349980.152590.050230.552800.311670.100330.028830.440832.28925.43
42IWDm智能水滴 M0.545010.378970.301241.225220.461040.147040.043690.651770.258330.097000.023080.378422.25525.06
43PSO粒子群优化0.597260.369230.299281.265770.372370.163240.070100.605720.256670.080000.021570.358232.23024.77
44Boids算法虚拟生物算法0.433400.305810.254250.993460.357180.201600.157080.715860.278460.142770.098340.519572.22924.77
45MA猴群算法0.591070.426810.318161.336040.311380.140690.066120.518190.228330.085670.027900.341902.19624.40


总结

本文介绍了两种版本的算法:原始版本和修改版本。后者进行了微小的调整,但由于在种群内部实现了信息交换,从而显著提高了性能。这表明,即使是算法逻辑中的一些小调整,也可能在各种任务中显著提高效率。

我非常喜欢这个算法的理念,因为它旨在避免陷入极端情况。该算法使用复杂的多阶段逻辑,将云从高压区域移动到低压区域,并进行降水。然而,这并不足以实现高收敛性。因此,我尝试通过引入种群中的信息交换来进行修改,这有助于改善收敛性,这是任何优化算法的关键方面之一。

该算法的特点是没有任何一朵云会在一个地方停留太久。区域中不断增加的压力最终会将云推入一个新的、未被探索的区域。作者将这种机制设想为对抗陷入局部极端情况的一种手段。然而,在试图改善算法的收敛性时,却增加了算法陷入局部最优的概率,遗憾的是,部分抵消了激发我将这种方法应用于其他优化算法的关键特性。任何优化算法的设计总是与在避免陷入局部极端情况和找到精确解之间找到平衡有关。如果需要,可以在代码中降低信息交换的概率。目前它是95%,这将增加算法的稳定性。

该算法是一个绝佳的基础,包含了一系列有趣的技巧(例如,区域湿度形成规则和区域间压力分布规则,此外还可以应用云的质量相关的加速度和惯性等物理定律,以及许多其他想法),是研究人员的真正福音。

标签

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

图表

图例3. 根据相关测试,将算法的颜色等级大于或等于0.99的结果以白色突出显示。

ACMO的优缺点:

优点:

  1. 内置的避免陷入局部最优的机制。
  2. 相对良好的收敛性。
  3. 相对良好的可扩展性。

缺点:

  1. 大量的外部参数。
  2. 复杂的实现难度。
  3. 在避免陷入局部最优和实现收敛之间找到正确平衡的难度。

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

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

附加的文件 |
ACMO.zip (39.7 KB)
基于MQL5和Python的自优化EA(第五部分):深度马尔可夫模型 基于MQL5和Python的自优化EA(第五部分):深度马尔可夫模型
在本次讨论中,我们将把一个简单的马尔可夫链应用于相对强弱指标(RSI),以观察指标穿过关键水平后的价格行为。我们得出结论,当RSI处于11-20区间时,会产生最强的买入信号;而当RSI处于71-80区间时,会产生最强的卖出信号,这在新西兰元兑日元(NZDJPY)货币对上表现得尤为明显。我们将展示如何通过对数据的处理和分析,直接从您所拥有的数据中构建出最优的交易策略。此外,我们还将展示如何训练一个深度神经网络,使其能够最优地利用转移矩阵。
创建 MQL5-Telegram 集成 EA 交易(第 4 部分):模块化代码函数以增强可重用性 创建 MQL5-Telegram 集成 EA 交易(第 4 部分):模块化代码函数以增强可重用性
在本文中,我们将现有的用于从 MQL5 向 Telegram 发送消息和截图的代码重构为可重复使用的模块化函数。这将简化流程,实现跨多个实例的更高效执行和更轻松的代码管理。
重构经典策略(第九部分):多时间框架分析(第二部分) 重构经典策略(第九部分):多时间框架分析(第二部分)
在今天的讨论中,我们探讨了多时间框架分析的策略,以确定我们的人工智能(AI)模型在哪个时间框架上表现最优。分析结果表明,在欧元兑美元(EURUSD)货币对上,月度和小时时间框架生成的模型具有相对较低的误差率。我们利用这一优势,开发了一个交易算法,该算法在月度时间框架上进行人工智能预测,并在小时时间框架上执行交易。
您应当知道的 MQL5 向导技术(第 36 部分):依据马尔可夫(Markov)链的 Q-学习 您应当知道的 MQL5 向导技术(第 36 部分):依据马尔可夫(Markov)链的 Q-学习
强化学习是机器学习的三大信条之一,并肩两个是监督学习和无监督学习。因此,它在意的是最优控制,或学习最适合目标函数的最佳长期政策。正是在这种背衬下,我们探索其向一款由向导组装的智能系统中 MLP 中通知学习过程的可能作用。