English Русский Español Português
preview
梦境优化算法(DOA)

梦境优化算法(DOA)

MetaTrader 5 — 交易 |
46 1
Andrey Dik
Andrey Dik

内容

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


引言

在研究各类优化方法的过程中,我接触了大量算法,其中一种全新而独特的思路引起了我的注意 —— 它的灵感来自一项颇具争议、研究尚不充分的现象:梦境机制。

2025年3月,Y. Lang与Y. Gao向学术界提出了一种新型元启发式优化算法 —— 梦境优化算法(DOA),相关论文发表于《Computer Methods in Applied Mechanics and Engineering》 (《应用力学与工程中的计算机方法》)期刊第436卷。该算法借鉴人类梦境的独有特征,为求解复杂优化问题开辟了新思路,其中就包含交易系统的参数调优。

DOA模拟睡眠过程的三大核心特征:部分记忆留存、选择性遗忘并后续补充信息,以及种群内各个智能体之间的“梦境”信息交互。在算法交易场景下,上述机制能在探索参数空间新区域与利用已有最优解之间取得平衡。这一点,对于在非平稳金融市场环境下优化交易策略而言至关重要。

在本文中,我们将详细剖析该算法的数学原理,基于MQL5完成算法实现,并将其与其他基于种群的优化算法开展对比分析。


算法实现

睡眠时,我们的大脑会完成三项重要工作:记住白天的关键信息、遗忘无关的细节,以及将各类记忆重组形成新想法。

DOA借鉴上述原理来寻找最优解。种群初始化完成后,全部个体被划分为若干群组。每个群组拥有专属的“记忆方式”:从记忆保留程度最高的第一组,到遗忘程度最高的最后一组,各组会根据组别编号和问题总维度数D,修改不同数量的维度k。

算法的绝大部分运行时间都花在探索阶段。首先,每个群组执行记忆策略,将群组内所有智能体重置到该组当前找到的最优位置;随后按一定概率触发遗忘与补充策略。该策略会随机选取k个维度,借助余弦调制公式cos((i+T/10)π/T)对维度进行更新。该机制在迭代初期产生较大搜索步长,之后步长逐步衰减。对于剩余概率,则启用梦境交换策略:从种群中随机挑选一个智能体,复制其k个维度的参数。

迭代的最后,算法进入利用阶段。重置到全局最优解对应的位置,并以极小步长开展精细寻优。该阶段依靠余弦函数cos(iπ/T) —— 该函数在优化末期趋近于0。这有助于在通过群组记忆机制进行搜索空间探索与短时但精确的最终优化之间取得平衡。

DOA

图例1. DOA算法的运行过程

以上为DOA算法的结构示意图。图上方的中心元素是一个标注为“基于梦境的搜索”(Dream-Inspired Search)的思维云图。从云图分出三条分支,对应算法的三大核心策略:蓝色模块记住最优解(Remember Best)对应记忆策略;中间粉色模块遗忘探索(Forget Explore)对应遗忘与补充策略;右侧绿色模块共享梦境(Share Dreams)对应智能体之间的经验交换策略。

三大策略下方是双阶段(Two Phases)区域,用横向进度条展示算法的时间分配:绿色部分占99%,标注为探索(Explore);右侧红色条占剩余1%。直观突出了探索阶段与利用阶段之间极不对称的时间分配。

在示意图的最底部,给出了算法核心数学公式:位置更新 += 随机量 × 余弦波(迭代次数) —— 这一公式凸显了余弦调制在DOA运行中的核心地位。接下来我们编写伪代码。

初始化
DOA算法开始

设置参数:
- 种群规模 = 60个智能体
  - 群组数量 = 6
- 探索阶段占总迭代时长 = 99%
  - 遗忘策略概率 = 30%

在搜索空间内随机生成60个智能体的位置
将智能体均分为6组(每组10个)
将每组的最优解初始化为最劣值(负无穷)
主优化循环
对每一轮迭代,从1到最大迭代次数:

如果当前迭代轮数不超过探索阶段的迭代轮数上限(占总轮数的99%):
    执行探索阶段
否则:
 执行利用阶段
探索阶段
对每个群组m,从1到6:
  
找出第m组内最优的智能体
  更新该群组的最优解
  
计算待修改维度数量:
最小维度数k_min = 向上取整(D/8/m)
    最大维度数k_max = 向上取整(D/3/m)
 k = 在k_min与k_max之间取随机数
    
 注释:第1组修改更多维度(记忆能力更强)
    第6组修改更少维度(遗忘程度更高)
  
  对群组m内的每一个智能体j:
    
 步骤1 - 记忆策略:
      将本组最优智能体的位置复制到当前智能体
      (组内所有智能体“记住”本组找到的最优解)
    
 步骤2 - 随机挑选k个待修改维度
    
  步骤3 - 遗忘策略或梦境共享策略:
      
      如果随机数小于0.3(30%概率):
        //遗忘与补充策略
  对选中的k个维度逐个执行:
   新值 = 当前值 + 随机数 × 余弦波
          
          其中余弦波 = [cos ((迭代次数 + T/10) × π / T) + 1] / 2
          
  注释:余弦函数在迭代初期产生较大步长,
                 在整个优化过程接近尾声时,步长逐渐减小
      
  否则(70%概率):
        //梦境交换策略
  对选中的k个维度逐个执行:
          从种群中随机选取一个智能体,复制其对应维度数值
  (该智能体“借鉴”其他智能体的解,即梦境交换)
    
 校验并修正所有维度的边界(防止参数超出允许范围)
利用阶段
对全部60个智能体中的每一个智能体j:
  
  步骤1 - 重置至全局最优:
 将全局最优解复制到当前智能体
(所有智能体汇聚至当前寻优得到的最优“峰值”)
  
步骤2 - 精细调优:
计算待修改维度数量:
  k = 在2和max(2, 向上取整(D/3))之间取随机数
    
随机选出k个维度
    
 对选中的k个维度逐个执行:
新值 = 当前值 + 随机数 × 余弦波
      
  其中,余弦波 = [cos (迭代次数 × π / T) + 1] / 2
      
  注释:在算法末期,余弦值趋近于0,
  这使得搜索步长极小
  
校验并修正维度边界
结果更新
每次智能体位置更新完成后:
  
计算每个智能体的目标函数值
  
更新全局最优解:
    如果找到优于当前全局最优的解:
  将其保存为新的全局最优
  
在探索阶段还需要:
 更新每个群组的最优解

单次迭代结束

让我们开始编写算法代码。该类用于实现DOA优化算法,并继承自C_AO基类(各类优化算法的统一接口)。构造函数与析构函数为标准形式,析构函数内不执行额外操作。构造函数指定算法核心参数及其取值,并将参数关联至params参数数组,便于外部统一管理参数。

主要参数:
    • popSize —— 种群规模,即每次迭代中候选解的数量;
    • numGroups —— 分组数量,种群将被划分为若干组以实现信息并行交互;
    • explorationRate —— 探索阶段迭代占比,算法在此阶段搜索解空间的新区域;
    • forgettingProb —— “遗忘”策略触发概率,用于避免算法陷入局部极小值。
    方法:
      • SetParams() —— 从"params"数组加载并设置类参数;
      • Init() —— 通过设置指定的搜索范围和迭代次数来初始化算法;
      • Moving() —— 执行单次优化迭代;
      • Revision() —— 更新并修正当前解状态。
      内部变量:
        • currentIteration、totalIterations、explorationIters —— 迭代计数器与阶段分界值;
        • groupBest [] —— 数组,存储每个分组的最优解,支撑信息共享与解的迭代进化。
        内部辅助方法:
          • ExplorationPhase() —— 负责探索搜索模式,拓展搜索范围;
          • ExploitationPhase() —— 利用阶段,基于已找到的优质解做进一步优化;
          • UpdateGroupBest () —— 更新指定分组内的最优解;
          • GetGroupStartIndex ()、GetGroupEndIndex () —— 获取每个分组内候选解索引区间。

          该类实现DOA算法,种群会被划分为固定数量的分组。迭代过程中,算法在探索阶段(搜寻新解)与利用阶段(优化已发现的最优解)之间分配计算资源。“遗忘”策略可帮助算法避开局部最优。整体设计旨在平衡新区域探索与已有优质解的精细优化,从而实现高效的全局寻优。

          //————————————————————————————————————————————————————————————————————
          class C_AO_DOA_dream : public C_AO
          {
            public: //----------------------------------------------------------
            ~C_AO_DOA_dream () { }
            C_AO_DOA_dream ()
            {
              ao_name = "DOA";
              ao_desc = "Dream Optimization Algorithm";
              ao_link = "https://www.mql5.com/en/articles/19177";
          
              popSize              = 60;    // population size
              numGroups            = 6;     // number of groups (fixed in the original)
              explorationRate      = 0.99;  // iteration rate for the exploration phase (9/10 in the original)
              forgettingProb       = 0.3;   // probability of using the main forgetting strategy
          
              ArrayResize (params, 4);
          
              params [0].name = "popSize";           params [0].val = popSize;
              params [1].name = "numGroups";         params [1].val = numGroups;
              params [2].name = "explorationRate";   params [2].val = explorationRate;
              params [3].name = "forgettingProb";    params [3].val = forgettingProb;
            }
          
            void SetParams ()
            {
              popSize          = (int)params [0].val;
              numGroups        = (int)params [1].val;
              explorationRate  = params      [2].val;
              forgettingProb   = params      [3].val;
            }
          
            bool Init (const double &rangeMinP  [],
                       const double &rangeMaxP  [],
                       const double &rangeStepP [],
                       const int     epochsP = 0);
          
            void Moving   ();
            void Revision ();
          
            //------------------------------------------------------------------
            int    numGroups;         // number of groups
            double explorationRate;   // iteration rate for the exploration phase
            double forgettingProb;    // probability of applying the main strategy
          
            private: //---------------------------------------------------------
            int    currentIteration;  // current iteration
            int    totalIterations;   // total number of iterations
            int    explorationIters;  // number of exploration iterations
          
            S_AO_Agent groupBest [];  // best solutions in each group
          
            void   ExplorationPhase      ();
            void   ExploitationPhase     ();
            void   UpdateGroupBest       (int groupNum);
            int    GetGroupStartIndex    (int groupNum);
            int    GetGroupEndIndex      (int groupNum);
          };
          //————————————————————————————————————————————————————————————————————
          

          C_AO_DOA_dream类的初始化方法负责执行优化算法运行前的准备工作,设置初始参数并建立后续迭代所需的内部状态。

          首先,调用通用的初始化方法,校验并设置搜索范围与步长,保证参数配置正确。如果该校验或配置过程失败,则终止初始化。

          随后设置各类计数器:将当前迭代次数重置为"0",由传入参数确定总迭代次数;再根据设定的探索占比(explorationRate),计算分配给探索阶段的迭代轮数。

          接下来,初始化分组最优解数组(groupBest),数组长度等于分组数量。对每一个分组,通过Init方法生成一个初始解,并将该解的适应度函数值设为理论最小值,保证后续可以正常进行对比与更新操作。

          执行完该方法后,算法即可基于已配置的参数、计数器和各组初始最优解,开始执行优化流程。

          //————————————————————————————————————————————————————————————————————
          //--- Initialization
          bool C_AO_DOA_dream::Init (const double &rangeMinP  [],
                                     const double &rangeMaxP  [],
                                     const double &rangeStepP [],
                                     const int     epochsP = 0)
          {
            if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
          
            //------------------------------------------------------------------
            currentIteration = 0;
            totalIterations  = epochsP;
            explorationIters = (int)(totalIterations * explorationRate);
          
            ArrayResize (groupBest, numGroups);
            for (int i = 0; i < numGroups; i++)
            {
              groupBest [i].Init (coords);
              groupBest [i].f = -DBL_MAX; // Initialize with the worst value
            }
          
            return true;
          }
          //————————————————————————————————————————————————————————————————————
          
          

          Moving方法是DOA算法的主迭代步骤,用于实现算法从一轮迭代到下一轮迭代的执行逻辑。首先,对currentIteration计数器自增,追踪算法的运行进度。

          初始种群初始化(仅首次运行) 检查"revision"标记;若标记为"false"(代表算法首次执行),则执行种群初始化:对种群内每一个智能体,以及智能体的每一个维度(坐标):

          • 调用u.RNDfromCI()函数,在给定上下限(rangeMin和rangeMax)范围内生成随机坐标值;
          • 调用u.SeInDiSp()函数,结合步长(rangeStep)对该数值进行修正,将其对齐至最近的合法取值(步长的整数倍);
          • 初始化完成后,将"revision"标记设置为 true,防止后续迭代重复执行初始化;
          • 至此,该方法执行完毕。
          判断算法所处阶段(初始化完成后):
          • 判断当前迭代是否属于探索阶段(currentIteration小于等于explorationIters):
          • 如果处于探索阶段,则调用ExplorationPhase()方法;
          • 否则(进入利用阶段),调用ExploitationPhase()方法。

            因此,Moving方法管控整个优化流程:完成种群的一次性初始化,之后依靠迭代计数器在探索阶段与利用阶段之间切换。初始化仅执行一次,此后算法进入循环,根据当前迭代轮数决定执行哪一个阶段。

            //————————————————————————————————————————————————————————————————————
            //--- The main step of the algorithm
            void C_AO_DOA_dream::Moving ()
            {
              currentIteration++;
            
              // Initial population setup
              if (!revision)
              {
                for (int i = 0; i < popSize; i++)
                {
                  for (int j = 0; j < coords; j++)
                  {
                    a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
                    a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]);
                  }
                }
            
                revision = true;
                return;
              }
            
              //------------------------------------------------------------------
              // Determine the phase of the algorithm
              if (currentIteration <= explorationIters)
              {
                ExplorationPhase ();
              }
              else
              {
                ExploitationPhase ();
              }
            }
            //————————————————————————————————————————————————————————————————————
            

            ExplorationPhase方法实现DOA算法的探索阶段。在该阶段,对各组智能体的候选解进行更新与多样化处理,以此搜寻解空间内新的可行区域。对于每一组智能体,更新该组的最优解,记录当前已搜索到的最优结果。接下来,根据当前分组编号与总维度数,确定遗忘操作需要修改的维度数量。计算每个分组内智能体的起始索引与结束索引。

            随后,对组内每一个智能体,将本组最优解复制为该智能体当前解(记忆策略),相当于保留一份可供回溯的历史优质解。生成一份待执行“遗忘”与“补充”操作的维度(坐标)列表。对维度数组进行随机打乱,以此随机挑选将要变更的维度。

            对于组内每个智能体,以参数forgettingProb设定的概率决定是否执行选中维度的更新策略;如果命中,则采用带余弦调制的遗忘策略。

            为选中维度生成取值范围内的随机偏移量。余弦调制由当前迭代次数与总迭代次数共同决定,可随迭代进程控制修改幅度大小。更新完成后,将数值修正至合法区间并对齐搜索步长。如果未触发遗忘策略,则执行“梦境共享”策略:从种群中的随机智能体复制其参数值,实现智能体之间的信息交互。

            因此,该阶段用于探索解空间,提升候选解多样性;借助随机扰动与智能体间的信息交换,帮助算法摆脱局部最优陷阱。

            //————————————————————————————————————————————————————————————————————
            //--- Exploration phase
            void C_AO_DOA_dream::ExplorationPhase ()
            {
              // Handle each group
              for (int m = 0; m < numGroups; m++)
              {
                // Update the best solution in the group
                UpdateGroupBest (m);
            
                // Calculate the number of dimensions to forget
                int kMin = (int)MathCeil ((double)coords / 8.0 / (m + 1));
                int kMax = (int)MathCeil ((double)coords / 3.0 / (m + 1));
                int k    = u.RNDintInRange (kMin, kMax);
            
                // Handle agents in the group
                int startIdx = GetGroupStartIndex (m);
                int endIdx   = GetGroupEndIndex   (m);
            
                for (int j = startIdx; j <= endIdx; j++)
                {
                  // Memory strategy - reset to the best group solution
                  ArrayCopy (a [j].c, groupBest [m].c, 0, 0, WHOLE_ARRAY);
            
                  // Select random dimensions to forget
                  int dims [];
                  ArrayResize (dims, coords);
                  for (int i = 0; i < coords; i++) dims [i] = i;
            
                  // Shuffle the array of dimensions
                  for (int i = coords - 1; i > 0; i--)
                  {
                    int idx    = u.RNDintInRange (0, i);
                    int temp   = dims [i];
                    dims [i]   = dims [idx];
                    dims [idx] = temp;
                  }
            
                  // Strategy of forgetting and replenishment
                  if (u.RNDprobab () < forgettingProb)
                  {
                    // Basic strategy with cosine modulation
                    for (int h = 0; h < k; h++)
                    {
                      int    dim              = dims [h];
                      double range            = rangeMax [dim] - rangeMin [dim];
                      double randomValue      = u.RNDprobab () * range + rangeMin [dim];
                      double cosineModulation = (MathCos ((1.0 * currentIteration + totalIterations / 10.0) * M_PI / totalIterations) + 1.0) / 2.0;
            
                      a [j].c [dim] = a [j].c [dim] + randomValue * cosineModulation;
                      a [j].c [dim] = u.SeInDiSp (a [j].c [dim], rangeMin [dim], rangeMax [dim], rangeStep [dim]);
                    }
                  }
                  else
                  {
                    // Dream sharing - copying from a random agent
                    for (int h = 0; h < k; h++)
                    {
                      int dim   = dims [h];
                      int donor = u.RNDintInRange (0, popSize - 1);
                      a [j].c [dim] = a [donor].c [dim];
                    }
                  }
                }
              }
            }
            //————————————————————————————————————————————————————————————————————
            

            ExploitationPhase方法实现该优化算法的利用阶段。其核心任务是引导搜索向当前已找到的最优解靠拢,以此优化现有结果。

            对种群内的每一个智能体,将其候选解重置为全局最优解,从而聚焦于解空间内最有潜力的区域。接下来,确定需要修改的维度数量。通常选取维度数量不少于2个,上限由总维度数决定。生成全部维度(坐标)列表,并对列表随机打乱,从中挑选待修改维度。针对每一个选中的维度:

            • 计算该参数的变动区间;
            • 在区间内生成随机数值;
            • 使用余弦函数对该数值做调制,余弦函数由当前迭代次数与总迭代次数决定,可随迭代进程控制扰动强度;
            • 结合生成的随机值与余弦调制结果,更新该参数;
            • 之后,将数值修正到合法区间,并按离散步长对齐,保证候选解有效。

            该方法的目标是通过可控随机扰动,对已发现的优质解做精细打磨,使算法进入解空间内更优区域,得到质量更高的候选解。

            //————————————————————————————————————————————————————————————————————
            //--- Exploitation phase
            void C_AO_DOA_dream::ExploitationPhase ()
            {
              // In the exploitation phase, all agents move towards the global best
              for (int j = 0; j < popSize; j++)
              {
                // Reset to global best solution
                ArrayCopy (a [j].c, cB, 0, 0, WHOLE_ARRAY);
            
                // Calculate the number of dimensions to modify
                int km = MathMax (2, (int)MathCeil ((double)coords / 3.0));
                int k = u.RNDintInRange (2, km);
            
                // Select random dimensions
                int dims [];
                ArrayResize (dims, coords);
                for (int i = 0; i < coords; i++) dims [i] = i;
            
                // Shuffle the array of dimensions
                for (int i = coords - 1; i > 0; i--)
                {
                  int idx = u.RNDintInRange (0, i);
                  int temp = dims [i];
                  dims [i] = dims [idx];
                  dims [idx] = temp;
                }
            
                // Apply the forgetting and addition strategy
                for (int h = 0; h < k; h++)
                {
                  int dim = dims [h];
                  double range = rangeMax [dim] - rangeMin [dim];
            
                  double randomValue = u.RNDprobab () * range + rangeMin [dim];
                  double cosineModulation = (MathCos (currentIteration * M_PI / totalIterations) + 1.0) / 2.0;
            
                  a [j].c [dim] = a [j].c [dim] + randomValue * cosineModulation;
                  a [j].c [dim] = u.SeInDiSp (a [j].c [dim], rangeMin [dim], rangeMax [dim], rangeStep [dim]);
                }
              }
            }
            //————————————————————————————————————————————————————————————————————
            

            UpdateGroupBest方法用于确定指定智能体分组内的最优解。其主要执行逻辑如下:

            1. 获取目标分组所含智能体的起始索引与结束索引;
            2. 遍历该分组内全部智能体;
            3. 对于每个智能体,将其评价函数值(即解的质量指标)与分组当前记录的最优值进行对比;
            4. 如果某个智能体的适应度函数值优于分组记录,则更新分组最优解,替换为当前更优的候选解。

            因此,该方法持续维护每个分组的最新最优解信息,对算法后续的搜索策略与解更新操作至关重要。

            //————————————————————————————————————————————————————————————————————
            //--- Update the best solution in the group
            void C_AO_DOA_dream::UpdateGroupBest (int groupNum)
            {
              int startIdx = GetGroupStartIndex (groupNum);
              int endIdx   = GetGroupEndIndex (groupNum);
            
              for (int i = startIdx; i <= endIdx; i++)
              {
                if (a [i].f > groupBest [groupNum].f)
                {
                  groupBest [groupNum].f = a [i].f;
                  ArrayCopy (groupBest [groupNum].c, a [i].c, 0, 0, WHOLE_ARRAY);
                }
              }
            }
            //————————————————————————————————————————————————————————————————————
            

            GetGroupStartIndex方法用于计算候选解 / 智能体数组中,指定分组内元素的起始索引。该方法基于分组在数组内均匀分布的计算假设。核心思路是根据分组编号、总分组数以及种群总规模,计算特定分组中第一个智能体(元素)的位置。

            计算公式为:分组编号 × 种群总规模 ÷ 总分组数,得到目标分组第一个元素的索引。该方法保证数据均匀划分至各个分组,方便后续对候选解执行分组相关操作。

            //————————————————————————————————————————————————————————————————————
            //--- Get the starting index of the group
            int C_AO_DOA_dream::GetGroupStartIndex (int groupNum)
            {
              return (int)((double)groupNum * popSize / numGroups);
            }
            //————————————————————————————————————————————————————————————————————
            

            GetGroupEndIndex方法用于计算指定分组所含元素的结束索引。该方法根据分组编号、种群总规模以及总分组数,计算分组内最后一个元素的位置。计算方式为:(分组编号 + 1) × 种群总规模 ÷ 总分组数 - 1,得到该分组最后一个元素的索引。

            还需要增加越界校验,防止数组下标越界:如果计算得到的索引超出种群规模,则将其修正为数组最后一个有效索引。该方法确保各智能体分组的边界划分正确。

            //————————————————————————————————————————————————————————————————————
            //--- Get the ending index of the group
            int C_AO_DOA_dream::GetGroupEndIndex (int groupNum)
            {
              int endIdx = (int)((double)(groupNum + 1) * popSize / numGroups) - 1;
              if (endIdx >= popSize) endIdx = popSize - 1;
              return endIdx;
            }
            //————————————————————————————————————————————————————————————————————
            

            Revision方法用于更新算法运行过程中找到的最优解信息。首先,遍历当前种群内所有候选解。在循环内,对每一个解,检查其目标函数值;如果该值优于当前最优值,则更新全局最优适应度fB为当前解的目标函数值,并将当前解设为全局最优解。

            随后判断当前迭代是否处于“探索”阶段(依据explorationIters):如果是,则除更新全局最优解外,还会一并考量各个分组内的最优解。实现方式为遍历所有分组,将每组最优解的目标函数值与当前全局最优值对比。

            如果该分组内的解更优,则更新fB,并将groupBest中存储的分组最优解复制到全局最优解缓冲区cB。因此,Revision方法会根据算法当前所处阶段(探索或利用)持续监测、更新最优解信息,保证始终保存截至当前迭代找到的最优候选解。

            //————————————————————————————————————————————————————————————————————
            //--- Update the best and worst solutions
            void C_AO_DOA_dream::Revision ()
            {
              // Update the global best solution
              for (int i = 0; i < popSize; i++)
              {
                if (a [i].f > fB)
                {
                  fB = a [i].f;
                  ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);
                }
              }
            
              // Update the best solutions of groups in the exploration phase
              if (currentIteration <= explorationIters)
              {
                for (int m = 0; m < numGroups; m++)
                {
                  if (groupBest [m].f > fB)
                  {
                    fB = groupBest [m].f;
                    ArrayCopy (cB, groupBest [m].c, 0, 0, WHOLE_ARRAY);
                  }
                }
              }
            }
            //————————————————————————————————————————————————————————————————————
            


            测试结果

            DOA算法实现完毕,下面直接在测试函数集上运行测试。可以看到,DOA算法得分53.62%,该结果将录入我们的算法评级表。

            DOA|梦境优化算法|60.0|6.0|0.99|0.3|
            =============================
            5 Hilly's;函数运行次数:10000;结果:0.8555594031110225
            25 Hilly's;函数运行次数:10000;结果:0.7008493263471764
            500 Hilly's;函数运行次数:10000;结果: 0.37279821121874124
            =============================
            5 Forest's;函数运行次数:10000;结果:0.7342194493052585
            25 Forest's;函数运行次数:10000;结果:0.48905397049976357
            500 Forest's;函数运行次数:10000;结果:0.24146681094197792
            =============================
            5 Megacity's;函数运行次数:10000;结果:0.7723076923076921
            25 Megacity's;函数运行次数:10000;结果:0.4735384615384616
            500 Megacity's;函数运行次数:10000;结果:0.18561538461538593
            =============================
            总分:4.82541(53.62%)

            由低维测试的可视化结果(绿色轨迹)可见,DOA的运行结果离散程度较大,在“Forest”与“Megacity”测试函数上尤为明显。 

            Hilly

            DOA在Hilly测试函数上

            Forest

            DOA在Forest测试函数上

            Megacity

            DOA在Megacity测试函数上

            根据测试结果,DOA算法在种群优化算法总排行榜中位列第26名。

            # 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密码锁算法(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
            26DOA梦境优化算法0.855560.700850.372801.929210.734210.489050.241471.464730.772310.473540.185611.431464.82553.62
            27BSA鸟群算法0.893060.649000.262501.804550.924200.711210.249391.884790.693850.326150.100121.120124.80953.44
            28DEA差分进化算法0.759950.675720.341711.777380.895820.642230.239411.777460.615380.440310.151151.206844.76252.91
            29HS和声搜索0.865090.687820.325271.878180.999990.680020.095901.775920.620000.422670.054581.097254.75152.79
            30SSG树苗播种和生长0.778390.649250.395431.823080.859730.624670.174291.658690.646670.441330.105981.193984.67651.95
            31BCOm细菌趋化性优化算法M0.759530.622680.314831.697040.893780.613390.225421.732590.653850.420920.144351.219124.64951.65
            32ABO非洲水牛优化0.833370.622470.299641.755480.921700.586180.197231.705110.610000.431540.132251.173784.63451.49
            33(PO)ES(PO) 进化策略0.790250.626470.429351.846060.876160.609430.195911.681510.590000.379330.113221.082554.61051.22
            34FBA基于分形的算法0.790000.651340.289651.730990.871580.568230.188771.628580.610770.460620.123981.195374.55550.61
            35TSm禁忌搜索M0.877950.614310.291041.783300.928850.518440.190541.637830.610770.382150.121571.114494.53650.40
            36BSO头脑风暴优化0.937360.576160.296881.810410.931310.558660.235371.725340.552310.290770.119140.962224.49849.98
            37WOAm鲸鱼优化算法M0.845210.562980.262631.670810.931000.522780.163651.617430.663080.411380.113571.188034.47649.74
            38AEFA人工电场算法0.877000.617530.252351.746880.927290.726980.180641.834900.666150.116310.095080.877544.45949.55
            39AEO基于人工生态系统的优化算法0.913800.467130.264701.645630.902230.437050.214001.553270.661540.308000.285631.255174.45449.49
            40CAm骆驼算法M0.786840.560420.351331.698590.827720.560410.243361.631490.648460.330920.134181.113564.44449.37
            41ACOm蚁群优化M0.881900.661270.303771.846930.858730.586800.150511.596040.596670.373330.024720.994724.43849.31
            42CMAES协方差矩阵自适应演化策略0.762580.720890.000001.483470.820560.796160.000001.616720.758460.490770.000001.249234.34948.33
            43DA_duelist决斗者算法0.927820.537780.277921.743520.869570.475360.181931.526860.621530.335690.117151.074374.34548.28
            44BFO-GA细菌觅食优化 - ga0.891500.551110.315291.757900.969820.396120.063051.428990.726670.275000.035251.036924.22446.93
            45SOA简单优化算法0.915200.469760.270891.655850.896750.374010.169841.440600.695380.280310.108521.084224.18146.45
            RW随机游走0.487540.321590.257811.066940.375540.219440.158770.753750.279690.149170.098470.527342.34826.09


            总结

            梦境优化算法(DOA)的表现中规中矩,在本次测试中拿下53.62%的得分,已计入我们的算法评级表。

            分析测试结果,可以看到许多元启发式算法的通病:随着问题维度升高,算法性能会出现明显下降。在低维问题上,DOA的测试结果在73%至86%之间;但换成高维问题后,受限于迭代次数,算法效率骤降至18%至37%。

            总体来看,该算法在众多优化方法中属于“中等”水平。感兴趣的读者可以尝试调参,该算法仍存在取得更优结果的潜力。 

            tab

            图例2. 算法对应各组测试下的色彩分级图

            图表

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

            DOA的优缺点:

            优点:

            1. 实现简单。
            2. 运行速度快。

            缺点:

            1. 低维测试函数上方差较高。

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


            文中所用的程序

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

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

            附加的文件 |
            DOArdream9.zip (265.3 KB)
            最近评论 | 前往讨论 (1)
            Chacha Ian Maroa
            Chacha Ian Maroa | 2 7月 2026 在 20:03
            哇!我迫不及待想看到仙女座星系旋转算法了!!
            神经网络在交易中的应用:概率时间序列预测(K²VAE) 神经网络在交易中的应用:概率时间序列预测(K²VAE)
            我们诚邀您探索 K²VAE 框架的原始实现 — 这是一个灵活的模型,能够线性近似潜在空间中的复杂动态。本文将演示如何在 MQL5 中实现关键组件,包括参数化矩阵,以及如何在标准神经网络层之外对这些矩阵进行管理。对于任何寻求构建可解释时间序列模型实用方法的人来说,本文材料都将大有裨益。
            重构经典策略(第十五部分):日线突破交易策略 重构经典策略(第十五部分):日线突破交易策略
            早在计算机出现之前,人工交易者就已投身金融市场,并总结出指导交易决策的经验法则。在本文中,我们重新研究一套知名的突破策略,来检验这种从实战中总结的市场逻辑,能否与系统化交易方法抗衡。研究结果表明:原始策略虽然胜率很高,但存在稳定性差、风险管控不足的问题。通过策略优化,我们展示如何将主观交易经验改造为更加稳健的算法交易策略。
            基于协整股票的统计套利(第三部分):数据库搭建 基于协整股票的统计套利(第三部分):数据库搭建
            本文提供一个 MQL5 Service(服务)的实现示例,用于保持新建数据库及时更新;该数据库作为数据分析以及一篮子协整股票交易的数据来源。文中详细阐述数据库的设计思路,并整理数据字典以供查阅。配套提供MQL5与Python脚本,用于数据库创建、表结构初始化以及行情数据写入。
            决斗者算法 决斗者算法
            如果您的交易策略能像真实的决斗者一样互相较量、彼此学习,会怎样?决斗者算法(Duelist Algorithm)是一种新的优化方法:在该算法中,不同的参数组合会像决斗者一样相互较量,争夺最优解的位置。