English Русский 中文 Español Deutsch Português
preview
夢最適化アルゴリズム(DOA)

夢最適化アルゴリズム(DOA)

MetaTrader 5 — トレーディング |
29 1
Andrey Dik
Andrey Dik

目次

  1. はじめに
  2. アルゴリズムの実装
  3. テスト結果


はじめに

最適化手法を幅広く研究するなかで、私は、議論の的となっており、まだ十分に研究されていない現象である「夢」のメカニズムに着想を得た、まったく新しい独特なアプローチに興味を引かれました。

2025年3月、Y. LangとY. Gaoは、学術誌「Computer Methods in Applied Mechanics and Engineering」(第436巻)に、新しいメタヒューリスティック最適化アルゴリズムである「Dream Optimization Algorithm」を発表しました。 人間の夢が持つ独特な特性に着想を得たこのアルゴリズムは、取引システムのパラメータ調整を含む、複雑な最適化問題を解決するための新たな可能性を開きます。

DOAは、睡眠プロセスに関連する3つの重要な側面をシミュレートします。すなわち、記憶の一部を保持すること、情報を選択的に忘却した後に新たな情報を補充すること、そして集団内のエージェント間で「夢」を交換することです。 アルゴリズム取引の文脈では、これらのメカニズムによって、パラメータ空間の新たな領域を探索することと、すでに見つかった良好な解を活用することのバランスを取ります。このバランスは、市場環境が時間とともに変化する非定常な金融市場において、取引戦略を最適化するうえで重要です。

本記事では、アルゴリズムの数学的な基礎を詳しく解説し、MQL5で実装したうえで、ほかの集団ベースの最適化手法との比較分析をおこないます。


アルゴリズムの実装

私たちの脳は、睡眠中に3つの重要な働きをすると考えられています。その日に得た重要な情報を記憶に定着させ、不要な細部を忘れ、さまざまな記憶を組み合わせて新しいアイデアを生み出すことです。

DOAは、これらと同様の原理を利用して最適解を探索します。 初期化の後、集団全体を複数のグループに分割します。 各グループには、それぞれ異なる「記憶スタイル」が割り当てられます。最も多くの次元を変更する第1グループから、変更する次元数が最も少ない第6グループまで、グループごとに変更する次元数(k)が異なります。この数は、グループ番号と問題の全次元数(D)に応じて決まります。

アルゴリズムの実行時間の大部分は、探索フェーズに割り当てられています。 まず、各グループで記憶戦略を適用し、グループ内のすべてのエージェントを、そのグループで見つかった最良の位置に戻します。その後、一定の確率で忘却・補充戦略を適用します。 この場合、ランダムに選択したk個の次元を、余弦変調を用いて変更します。使用する式は「cos((i+T/10)π/T)」です。これにより、探索の初期には大きなステップを取り、時間の経過とともにステップを徐々に小さくします。 残りの確率では、夢交換戦略を適用します。この戦略では、集団内からランダムに選んだ別のエージェントの値を、k個の次元についてコピーします。

最終段階では、アルゴリズムは活用フェーズに移行します。このフェーズでは、すべてのエージェントを大域最良解の位置に戻し、ごく小さなステップで微調整します。微調整には、最適化の終了時にゼロに近づく余弦関数「cos(iπ/T)」を使用します。この仕組みにより、グループの記憶機構を通じた探索空間の探索と、最終段階における短時間かつ精密な最適化とのバランスを実現します。

DOA

図1: DOAアルゴリズムの動作

上の図は、DOAアルゴリズムの構造図を示しています。 図の上部中央には、「Dream-Inspired Search(夢に着想を得た探索)」と記された雲形の吹き出しがあります。 そこから3本の線が伸び、アルゴリズムの3つの主要な戦略につながっています。青いブロックの「Remember Best」は記憶戦略を表し、中央のピンクのブロック「Forget Explore」は忘却と探索の戦略を、右側の緑のブロック「Share Dreams」はエージェント間で経験を共有する戦略を表しています。

その下の「Two Phases(2つのフェーズ)」では、アルゴリズムの実行時間の配分が横長のバーで示されています。緑色の「Explore(探索)」が全体の99%を占め、右端の赤色の部分が残りの1%を占めています。この図は、探索フェーズと活用フェーズの間に大きな時間配分の偏りがあることを強調しています。

図の最下部には、アルゴリズムの重要な数式「Position += Random × Cosine_Wave(iteration)」が示されています。これは、位置の更新に乱数と余弦波による係数を使用することを表しており、DOAの動作における余弦変調の重要性を示しています。 それでは、擬似コードを見ていきましょう。

初期化
DOAアルゴリズムの開始

パラメータを設定する:
- 集団サイズ = 60エージェント
- グループ数 = 6
- 探索の割合 = 総時間の99%
- 忘却確率 = 30%

探索空間内のランダムな位置に60のエージェントを作成する
エージェントを6つの等しいグループ(各10エージェント)に分割する
各グループの最良解を、考え得る最悪の解で初期化する
メインの最適化ループ
反復番号1から最大反復回数まで、各反復について:

反復番号が総反復回数の99%以下の場合:
探索フェーズを実行する
それ以外の場合:
活用フェーズを実行する
探索
グループm(1から6まで)について:

グループmの最良のエージェントを見つける
グループ最良解を更新する

変更する次元数を計算する:
k_minimum = ceiling(D / 8 / m)
k_maximum = ceiling(D / 3 / m)
k = k_minimumからk_maximumまでの範囲の乱数

注:グループ1はより多くの次元を変更する(記憶力が高い)
グループ6はより少ない次元を変更する(より多く忘れる)

グループmの各エージェントjについて:

ステップ1 - 記憶戦略:
グループ内の最良のエージェントの位置を現在のエージェントにコピーする
(グループ内のすべてのエージェントが、見つかった最良解を「記憶」する)

ステップ2 - 変更するk個の座標成分をランダムに選択する

ステップ3 - 忘却戦略または夢交換戦略:

random_number < 0.3の場合(30%の確率):
// 忘却・補充戦略
選択したk個の座標成分それぞれについて:
new_value = current + random × cosine_wave

ここで、cosine_wave = (cos((iteration + T/10) × π / T) + 1) / 2

注:余弦関数により、探索の初期には大きなステップとなり、
終盤に向けて小さなステップとなる

それ以外の場合(70%の確率):
// 夢交換戦略
選択したk個の座標成分それぞれについて:
集団内からランダムに選んだエージェントの値をコピーする
(そのエージェントは、別のエージェントの解を「夢見る」)

すべての座標成分が範囲内にあるか確認し、必要に応じて補正する
活用
全60エージェントの各エージェントjについて:

ステップ1 - 大域最良解にリセットする:
大域最良解を現在のエージェントにコピーする
(すべてのエージェントが、見つかった「頂点」に集まる)

ステップ2 - 微調整:
変更する次元数を計算する:
k = 2からmaximum(2, ceiling(D/3))までの範囲の乱数

k個の座標成分をランダムに選択する

選択したk個の座標成分それぞれについて:
new_value = current + random × cosine_wave

ここで、cosine_wave = (cos(iteration × π / T) + 1) / 2

注:アルゴリズムの終了時には余弦による係数がほぼ0となり、
非常に小さなステップとなる

範囲内にあるか確認し、必要に応じて補正する
結果の更新
位置を変更するたびに:

各エージェントの目的関数の値を計算する

大域最良解を更新する:
現在の大域最良解よりも優れた解が見つかった場合:
それを新たな大域最良解として保存する

探索フェーズでは、さらに:
各グループの最良解を更新する

反復の終了

それでは、アルゴリズムのコードを書き始めましょう。 今回実装するクラスはDOA最適化アルゴリズムを担い、さまざまな最適化アルゴリズムに共通するインターフェースを提供する基底クラスC_AOを継承します。 コンストラクタとデストラクタは標準的な構成とし、デストラクタには特別な処理を追加しません。 コンストラクタでは、アルゴリズムの主要なパラメータと初期値を設定するとともに、それらをparamsパラメータ配列に関連付けます。これにより、クラスの外部からパラメータを容易に管理できるようになります。

主なパラメータ:
    • popSize:集団サイズ。各反復で保持する候補解の数です。
    • numGroups:集団を分割するグループ数。グループ間で情報を交換するために使用します。
    • explorationRate:全反復回数のうち、探索フェーズに割り当てる割合です。このフェーズでは、探索空間内の新しい領域を探索します。
    • forgettingProb:忘却戦略を適用する確率です。探索の多様性を保ち、局所最適解に陥ることを避けるのに役立ちます。
    メソッド:
      • SetParams ():params配列からクラスのパラメータを設定します。
      • Init ():探索範囲と反復回数を指定して、アルゴリズムを初期化します。
      • Moving ():最適化の1ステップを実行します。
      • 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/ja/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|Dream Optimization Algorithm|60.0|6.0|0.99|0.3|
            =============================
            5 Hilly's; Func runs: 10000; result: 0.8555594031110225
            25 Hilly's; Func runs: 10000; result: 0.7008493263471764
            500 Hilly's; Func runs: 10000; result: 0.37279821121874124
            =============================
            5 Forest's; Func runs: 10000; result: 0.7342194493052585
            25 Forest's; Func runs: 10000; result: 0.48905397049976357
            500 Forest's; Func runs: 10000; result: 0.24146681094197792
            =============================
            5 Megacity's; Func runs: 10000; result: 0.7723076923076921
            25 Megacity's; Func runs: 10000; result: 0.4735384615384616
            500 Megacity's; Func runs: 10000; result: 0.18561538461538593
            =============================
            All score: 4.82541 (53.62%)

            低次元におけるDOAアルゴリズムの性能を可視化した結果(緑色の線)を見ると、特にForest関数とMegacity関数で、結果にばらつきが見られます。

            Hilly

            Hillyテスト関数におけるDOAの動作

            Forest

            Forestテスト関数におけるDOAの動作

            Megacity

            Megacityテスト関数におけるDOAの動作

            テスト結果に基づくと、DOAアルゴリズムは集団ベースの最適化アルゴリズムの総合ランキングで26位となっています。

            #AO説明HillyHilly
            最終値
            ForestForest
            最終値
            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
            26DOAdream_optimization_algorithm0.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_duelistduelist_algorithm0.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)は、今回実施したテストで100%満点中53.62%のスコアを記録し、まずまずの性能を示しました。その結果、評価表にランクインしました。

            結果を分析すると、多くのメタヒューリスティックアルゴリズムに共通する傾向が見られます。それは、問題の次元数が増えるにつれて、性能が大幅に低下することです。 低次元の問題ではDOAは73~86%の結果を示しますが、高次元の問題では、反復回数が限られた条件下で、性能が18~37%まで低下します。

            総合的に見ると、このアルゴリズムは他の最適化手法と比較して、平均的な位置づけにあります。 興味のある方は、アルゴリズムの設定を変更して試してみるとよいでしょう。設定次第では、さらに良い結果が得られる可能性があります。

            タブ

            図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
            うわぁ!アンドロメダ銀河の回転アルゴリズムが待ちきれない!!
            線形自己回帰モデルの残差を用いた非線形U-Transformerの学習 線形自己回帰モデルの残差を用いた非線形U-Transformerの学習
            線形自己回帰モデルと、残差分析に用いるU-Transformerアーキテクチャを組み合わせた、為替レート予測のための革新的なハイブリッドシステムを紹介します。 このシステムは、シグナルの品質に応じて使用する予測ソースを自動的に切り替えるほか、ナンピンやピラミッディング戦略を含む、一連の取引ロジックを備えています。 このアプローチの最大の利点は、ニューラルネットワークを線形モデルの残差を使って学習させる点にあります。これにより、ニューラルネットワークが取り組むべき課題を単純化し、過学習のリスクを低減できます。 システム全体はMQL5で実装されており、市場環境の変化に自動的に適応しながら、実際の取引に利用できるよう設計されています。
            取引におけるニューラルネットワーク:Temporal Queryモデル(最終回) 取引におけるニューラルネットワーク:Temporal Queryモデル(最終回)
            TQNetフレームワークの開発とテストも、いよいよ最終段階を迎えました。今回は、理論を実際の取引環境に結び付け、過去のデータによる学習から、直近の市場データを用いたストレステストへと進みます。 モデルの頑健性と予測精度を評価し、最終的な結果を通じて、提案手法が実際の取引においてどのような価値を持つのかを明らかにします。 ここで得られるのは、単なる統計上の数値ではなく、実用性を具体的に示す結果です。
            エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法 エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
            この記事では、MT4において複数のEAの衝突をさける方法を扱います。ターミナルの操作、MQL4の基本的な使い方がわかる人にとって、役に立つでしょう。
            取引におけるニューラルネットワーク:スケーリングの代わりに分解する(最終回) 取引におけるニューラルネットワーク:スケーリングの代わりに分解する(最終回)
            時系列データを意味のある層に分解し、それらを活用して簡潔なモデルを構築するアルゴリズムをご紹介します。 そのアーキテクチャからMQL5/OpenCLによる具体的な実装、過去の市場データを用いた実証テストまで、体系的に解説します。