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

カオス最適化アルゴリズム(COA)

MetaTrader 5 |
21 0
Andrey Dik
Andrey Dik


内容

  1. はじめに
  2. アルゴリズムの実装


はじめに

現代の計算問題、特に金融市場やアルゴリズム取引の分野では、高効率な最適化手法が極めて重要な役割を果たしています。数多く存在する大域最適化アルゴリズムの中でも、自然現象に着想を得たアプローチは特に注目されています。カオス最適化アルゴリズム(COA)は、カオス理論と大域最適化を融合した革新的な手法のひとつです。

本記事では、エルゴード性、初期値鋭敏性、そして擬似確率的挙動といった、カオスシステムが持つ本質的な特性を活用した改良型カオス最適化アルゴリズムを紹介します。このアルゴリズムでは複数種類のカオス写像を利用し、探索空間を効率的に走査するとともに、最適化アルゴリズムで頻繁に発生する局所最適解への停滞回避を試みています。

提案手法の大きな特徴は、カオス探索に加えて、重み付き勾配法および動的パラメータ調整を可能にする適応メカニズムを組み合わせている点にあります。ロジスティック写像、正弦写像、テント写像といった複数のカオス写像を利用することで、本アルゴリズムは局所解への停滞に強く、複雑な多峰性関数においてもグローバル最適解を見つける能力を備えています。本記事では、アルゴリズム全体の構造を解析したうえで、お馴染みの標準テスト関数を用いた性能検証もおこないます。 


アルゴリズムの実装

アルゴリズムは、以下の3つの主要フェーズで構成されています。

  • 第1搬送波によるカオス探索:初期カオス変数を生成し、ロジスティック写像を用いて逐次的にカオス系列を生成します。その後、カオス変数を最適化対象の探索空間へ写像し、最良解を保存します。
  • 重み付き勾配方向探索:重み付き勾配を利用して、有望領域への探索を進めます。
  • 第2搬送波によるカオス探索:最良解周辺に対して局所探索を実施し、フラクタル構造に基づくステップサイズ制御を利用して探索精度を向上させます。 

以下の図では、カオス最適化アルゴリズムの本質を視覚的に表現しています。本アルゴリズムの中心的なアイデアは、「カオスを単なるランダム性ではなく、有効な探索メカニズムとして利用する」ことにあります。中央で明るく発光している黄色の領域が大域最適解を表しています。青く発光する粒子は探索エージェントであり、非線形な軌道に沿って移動します。これらの軌跡は滑らかな発光ラインとして描かれ、カオス的探索の性質を示しています。

カオス挙動の主要な特性、すなわち決定論性(軌道はランダムではなく滑らかであること)、エルゴード性(粒子が空間全体を探索すること)、初期値鋭敏性(異なる粒子が異なる軌道を描くこと)が表現されています。また、探索ダイナミクスは発光強度の違いによって可視化されており、これは異なる領域における探索強度を示しています。最適解の周囲に描かれた同心円は吸引領域を象徴しており、ぼかしやグラデーションは探索空間の連続性を表現しています。以下は、アルゴリズムの主な段階です。

  • 中心から離れた領域での広域探索(遠方の粒子)
  • 有望な領域への段階的な接近(中間的な軌道)
  • 最適解近傍での局所探索(中心付近の粒子)

この画像全体は、「混沌」ではなく「制御された探索過程」としてのカオス最適化を描いた「カオス最適化の概念図」として捉えることができます。

カオス_1

図1:カオス最適化の可視化

次の図は、COAアルゴリズムの3段階構造を示しています。

  1. 第1搬送波探索(青):カオス写像を利用した大域探索を実行し、カオス変数を探索空間へ変換します。
  2. 重み付き勾配探索(橙):制約条件を重み比率で処理する重み付き勾配法を実行します。
  3. 第2搬送波探索(紫):最良解周辺での局所探索と、αパラメータの適応調整を実施します。           

カオス

 図2:カオス最適化アルゴリズムの動作構成図

この図は、COA (CHAOS)アルゴリズムの基本的な3段階構造を表しています。私の実装(実際には複数の実装を試しました)では、より実践的な実装を採用することにしました。その過程で、エージェント構造を拡張し、移動カウンタ、停滞カウンタ、さらに各次元ごとのカオス変数を追加しました。また、カオス写像に多様性を持たせることも重要でした。元の著者らはロジスティック写像のみを使用していましたが、私のバージョンではこれに加えて正弦写像とテント写像を導入しています。この実装には、ペナルティパラメータの自動適応機構や、慣性調整を伴う成功率ベースの探索パラメータ適応も含まれています。さらに、ラテン超方格を用いた、より高度な初期化手法も追加しました。

それでは、アルゴリズムの擬似コードを書き始めましょう。

アルゴリズムの初期化

  1. アルゴリズムパラメータを設定します。
    • 集団サイズ(popSize)
    • 第1フェーズの反復回数(S1)
    • 第2フェーズの反復回数(S2)
    • ペナルティパラメータ(sigma)
    • 調整係数(t3)
    • 重み比率用の微小定数(eps)
    • 慣性パラメータ(inertia)
    • 社会的影響パラメータ(socialFactor)
    • 突然変異確率(mutationRate)
  2. エージェントを初期化します。
    • 各エージェントについて以下を実行します。
      • 異なる初期値を用いてカオス変数(gamma)を初期化
      • 速度ベクトル(velocity)をリセット
      • 停滞カウンタを0に設定
  3. 探索パラメータ(alpha)を初期化します。
    • 探索空間のサイズに応じて適応的にパラメータを設定します。
    • alpha[c] = 0.1 * (rangeMax[c] - rangeMin[c]) / sqrt(coords)

フェーズ1:カオス分布による初期集団生成

  1. ラテン超方格を用いて初期集団を生成します。
    • 各次元の値を生成
    • シャッフルすることで均一な分布を確保
    • 値を探索変数の範囲へマッピング
  2. エージェントには複数の初期化戦略を適用します。
    • 最初の1/4のエージェントには一様分布を使用
    • 次の1/4では、複数のクラスタ中心周辺に集中して配置
    • 3番目の1/4では、境界方向へ偏らせたランダム配置を使用
    • 最後の1/4では、異なるカオス写像を用いたカオス的配置を生成

フェーズ2:第1搬送波によるカオス探索

  1. S1回の反復まで、以下を実行します。
    • 各エージェントについて以下を実行します。
      • 突然変異確率条件を満たした場合は突然変異を適用します。
      • それ以外の場合は、各座標について以下を実行します。
        • 選択されたカオス写像(ロジスティック写像、正弦写像、テント写像)を用いてカオス変数を更新
        • 最適化フェーズに応じて、大域探索または局所探索を選択
        • 大域探索では、カオス値を利用して新しい位置を決定
        • 局所探索では、最良解への引力とカオス的摂動を組み合わせる
        • 慣性を考慮して速度を更新
        • 速度制限および位置制限を適用
        • 制約違反を確認し、必要に応じて補正
    • 評価と更新をおこないます。
      • 各エージェントのペナルティ関数を計算
      • 個体最良解および全体最良解を更新
      • ペナルティパラメータを動的に更新
      • 成功率に基づいて探索パラメータ(alpha)を適応更新

フェーズ3:第2搬送波によるカオス探索

  1. S1からS1+S2までの各反復に対して、以下を実行します。
    • 収束判定を実施
    • 集団内の各エージェントに対して以下を実行します。
      • 収束が検出された場合、一部のエージェントにランダム変異を適用します。
      • それ以外の場合は、各座標について以下を実行します。
        • カオス変数を更新
        • 探索半径を適応的に縮小
        • 最良解を優先して基準点を選択
        • カオス的摂動およびLévyノイズを加え、ランダムな長距離ジャンプを実現
        • 慣性を考慮して速度および位置を更新
        • 位置制約を適用
    • 評価と更新をおこないます。
      • 各エージェントのペナルティ関数を計算
      • 個体最良解および全体最良解を更新
      • 大域最良解の履歴を更新し、収束判定に使用
      • 必要に応じて停滞したエージェントをリセット

補助関数

  1. カオス写像
    • LogisticMap(x):x[n+1] = r x[n] (1-x[n])を用いて更新し、値の妥当性検証をおこなう
    • SineMap(x):x[n+1] = sin(π*x[n])を用いて更新し、正規化をおこなう
    • TentMap(x):x[n+1] = μ*min(x[n], 1-x[n])を用いて更新し、値の妥当性検証をおこなう
    • SelectChaosMap(x, type):指定されたtypeに応じてカオス写像を選択する
  2. 制約およびペナルティ処理
    • CalculateConstraintValue(agent, coord):制約違反量を計算
    • CalculateConstraintGradient(agent, coord):制約勾配を計算
    • CalculateWeightedGradient(agent, coord):重み付き勾配を計算
    • CalculatePenaltyFunction(agent):ペナルティ関数値を計算
  3. 停滞および収束処理
    • IsConverged():最良解履歴に基づいて収束判定をおこなう
    • ResetStagnatingAgents():停滞しているエージェントをリセットする
    • ApplyMutation(agent):複数種類の変異処理を適用する
    • UpdateSigma():ペナルティパラメータを動的に更新する
    • UpdateBestHistory(newBest):最良値履歴を更新する

これで、COA (CHAOS)アルゴリズムの実装説明へ進む準備が整いました。本記事では、主要な実装メソッドをすべて詳しく解説します。次回の記事では、アルゴリズムのテストおよび性能評価結果について取り上げます。まず、S_COA_Agent構造体と、そのフィールドを実装します。

  • gamma []:擬似乱数型カオス変数の集合です。エージェント動作にランダム性と多様性を導入するために使用します。
  • velocity[]:速度配列です。慣性を考慮しながら、探索空間内をより動的に移動できるようにします。
  • stagnationCounter:解の改善が見られない場合に増加するカウンタです。停滞時の再始動戦略を実装するために使用します。

Init()メソッドでは、各配列の生成および初期値設定をおこないます。gamma[]には、カオス変数の初期条件に多様性を与えるため、0.1 ~ 0.9の一様分布を使用します。velocity[]の初期速度はすべて0に設定し、stagnationCounterも0に初期化します。

//——————————————————————————————————————————————————————————————————————————————
// Improved agent structure with additional fields
struct S_COA_Agent
{
    double gamma    [];       // chaotic variables
    double velocity [];       // movement velocity (to increase inertia)
    int    stagnationCounter; // stagnation counter

    void Init (int coords)
    {
      ArrayResize (gamma,    coords);
      ArrayResize (velocity, coords);

      // Uniform distribution of gamma values
      for (int i = 0; i < coords; i++)
      {
        // Use different initial values for better variety
        gamma [i] = 0.1 + 0.8 * (i % coords) / (double)MathMax (1, coords - 1);

        // Initialize velocity with zeros
        velocity [i] = 0.0;
      }

      stagnationCounter = 0;
    }
};
//——————————————————————————————————————————————————————————————————————————————

C_AO_COA_chaosクラスは、C_AO基底クラスから派生したCOA (CHAOS)アルゴリズムの実装クラスです。本クラスには、アルゴリズムの動作に必要なメソッドおよびパラメータに加え、カオス探索の概念に基づいてエージェントの挙動を制御するための追加機能が含まれています。クラス構成は以下の通りです。

  • SetParams ():アルゴリズムパラメータを設定するメソッド
  • Init():アルゴリズム動作に必要な探索範囲および各種パラメータを受け取る初期化メソッド
  • Moving():解空間内でエージェントを移動させる処理を担当するメソッド
  • Revision():エージェント位置を補正するメソッド
アルゴリズムのパラメータは次の通りです。
  • S1、S2:アルゴリズムの2段階探索フェーズにおける反復回数
  • sigma、t3、eps、inertia、socialFactor、mutationRate:エージェントの挙動およびアルゴリズム全体の動作に影響を与えるパラメータ
  • alpha []:探索処理に使用されるパラメータ配列
  • agent []:アルゴリズム集団を構成するエージェント配列
protectedメソッド
  • 勾配、制約値、ペナルティ関数を計算する各種メソッド、および解の実行可能性を判定するIsFeasible()メソッド
  • カオス写像関連メソッド(LogisticMap、SineMap、TentMap、SelectChaosMap)
privateフィールド
  • 現在のエポック情報を保持する変数(epochs、epochNow)と動的ペナルティ値を保持するcurrentSigma
  • globalBestHistory[]:複数反復における大域最良値を保存する配列
  • historyIndex:最良値履歴配列内の位置を管理する履歴インデックス
  • エージェント集団の管理(InitialPopulation)、さまざまな探索フェーズの実行(FirstCarrierWaveSearch、SecondCarrierWaveSearch)、エージェントの変異(ApplyMutation)、ペナルティの更新(UpdateSigma)、収束の確認(IsConverged)、および停滞エージェントのリセット(ResetStagnatingAgents)をおこなうためのメソッド

    このように、C_AO_COA_chaosクラスは、エージェントベースで解探索をおこなう最適化システムの中核を構成する複合コンポーネントです。本クラスは、決定論的戦略とカオス戦略の双方を含むアルゴリズム内部において、エージェントを制御するために必要なパラメータ、メソッド、および制御ロジックを統合しています。 

    //——————————————————————————————————————————————————————————————————————————————
    class C_AO_COA_chaos : public C_AO
    {
      public: //--------------------------------------------------------------------
      ~C_AO_COA_chaos () { }
      C_AO_COA_chaos ()
      {
        ao_name = "COA(CHAOS)";
        ao_desc = "Chaos Optimization Algorithm";
        ao_link = "https://www.mql5.com/en/articles/16729";
    
        // Internal parameters (not externally configurable)
        inertia      = 0.7;
        socialFactor = 1.5;
        mutationRate = 0.05;
    
        // Default parameters
        popSize = 50;
        S1      = 30;
        S2      = 20;
        sigma   = 2.0;
        t3      = 1.2;
        eps     = 0.0001;
    
        // Initialize the parameter array for the C_AO interface
        ArrayResize (params, 6);
    
        params [0].name = "popSize"; params [0].val = popSize;
        params [1].name = "S1";      params [1].val = S1;
        params [2].name = "S2";      params [2].val = S2;
        params [3].name = "sigma";   params [3].val = sigma;
        params [4].name = "t3";      params [4].val = t3;
        params [5].name = "eps";     params [5].val = eps;
      }
    
      void SetParams ()
      {
        // Update internal parameters from the params array
        popSize = (int)params [0].val;
        S1      = (int)params [1].val;
        S2      = (int)params [2].val;
        sigma   = params      [3].val;
        t3      = params      [4].val;
        eps     = params      [5].val;
      }
    
      bool Init (const double &rangeMinP  [], // minimum search range
                 const double &rangeMaxP  [], // maximum search range
                 const double &rangeStepP [], // search step
                 const int     epochsP = 0);  // number of epochs
    
      void Moving   ();
      void Revision ();
    
      //----------------------------------------------------------------------------
      // External algorithm parameters
      int    S1;             // first phase iterations
      int    S2;             // second phase iterations
      double sigma;          // penalty parameter
      double t3;             // alpha correction ratio
      double eps;            // small number for weighting ratios
    
      // Internal algorithm parameters
      double inertia;        // inertia parameter for movement (internal)
      double socialFactor;   // social influence parameter (internal) 
      double mutationRate;   // mutation probability (internal) 
    
      S_COA_Agent agent [];  // array of agents
    
      private: //-------------------------------------------------------------------
      int    epochNow;
      double currentSigma;           // Dynamic penalty parameter
      double alpha             [];   // search parameters
      double globalBestHistory [10]; // History of global best solution values
      int    historyIndex;
    
      // Auxiliary methods
      double CalculateWeightedGradient   (int agentIdx, int coordIdx);
      double CalculateConstraintValue    (int agentIdx, int coordIdx);
      double CalculateConstraintGradient (int agentIdx, int coordIdx);
      double CalculatePenaltyFunction    (int agentIdx);
    
      // Method for checking the solution feasibility
      bool IsFeasible              (int agentIdx);
    
      // Chaotic maps
      double LogisticMap           (double x);
      double SineMap               (double x);
      double TentMap               (double x);
      double SelectChaosMap        (double x, int type);
    
      void InitialPopulation       ();
      void FirstCarrierWaveSearch  ();
      void SecondCarrierWaveSearch ();
      void ApplyMutation           (int agentIdx);
      void UpdateSigma             ();
      void UpdateBestHistory       (double newBest);
      bool IsConverged             ();
      void ResetStagnatingAgents   ();
    };
    //——————————————————————————————————————————————————————————————————————————————
    

    C_AO_COAクラスのInitメソッドは、アルゴリズムを動作させるための初期設定および準備を担当します。まず、StandardInit()メソッドを使用して基本的な初期化を実行し、探索範囲、ステップ、およびその他のパラメータを設定します。この処理が成功しない場合、メソッドは失敗として終了します。

    次に、エポック数、現在のエポック(epochNow)、およびペナルティ係数(currentSigma)に関連するパラメータが設定されます。また、進捗状況を追跡するために、最良解の履歴が初期化されます。さらに、探索対象となる最小値および最大値の範囲を格納する配列サイズを確認します。一致しない場合、または未指定の場合は、初期化処理が中断されます。

    その後、エージェント、アルファパラメータ、および発見された最良解を保存する配列が初期化されます。各エージェントには、さまざまな戦略に基づいて初期位置が割り当てられます。

    • 探索範囲全体に均等に配置される
    • 範囲内の複数の点の周辺に「クラスタリング」される
    • 境界条件を考慮しながらランダムに配置される
    • カオス写像関数を用いて初期解を生成する

    C_AO_COA_chaosクラスのInitメソッドは、最適解を探索するために必要な初期パラメータおよび配列を設定します。この処理では、入力データの正当性を確認し、探索範囲を設定し、異なる初期配置戦略を持つエージェント配列を初期化します。また、発見された最良解などのグローバル変数の値も設定されます。メソッドの実行中には、その後の反復的な最適化プロセスに必要なデータ構造が作成され、エージェントおよびアルゴリズム全体の挙動を制御する各種パラメータが設定されます。

    //——————————————————————————————————————————————————————————————————————————————
    bool C_AO_COA_chaos::Init (const double &rangeMinP  [],
                         const double &rangeMaxP  [],
                         const double &rangeStepP [],
                         const int     epochsP = 0)
    {
      if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
    
      //----------------------------------------------------------------------------
      epochNow     = 0;
      currentSigma = sigma;
      historyIndex = 0;
    
      // Initialize the history of best values
      for (int i = 0; i < 10; i++) globalBestHistory [i] = -DBL_MAX;
    
      // Check and initialize the main arrays
      int arraySize = ArraySize (rangeMinP);
      if (arraySize <= 0 || arraySize != ArraySize (rangeMaxP) || arraySize != ArraySize (rangeStepP))
      {
        return false;
      }
    
      ArrayResize (agent, popSize);
      ArrayResize (alpha, coords);
    
      // Adaptive alpha initialization depending on the search range
      for (int c = 0; c < coords; c++)
      {
        // alpha depends on the size of the search space
        double range = rangeMax [c] - rangeMin [c];
        alpha [c] = 0.1 * range / MathSqrt (MathMax (1.0, (double)coords));
      }
    
      // Initialize of agents with various strategies
      for (int i = 0; i < popSize; i++)
      {
        agent [i].Init (coords);
    
        for (int c = 0; c < coords; c++)
        {
          double position;
    
          // Different initialization strategies
          if (i < popSize / 4)
          {
            // Uniform distribution in space
            position = rangeMin [c] + (i * (rangeMax [c] - rangeMin [c])) / MathMax (1, popSize / 4);
          }
          else
            if (i < popSize / 2)
            {
              // Clustering around multiple points
              int cluster = (i - popSize / 4) % 3;
              double clusterCenter = rangeMin [c] + (cluster + 1) * (rangeMax [c] - rangeMin [c]) / 4.0;
              position = clusterCenter + u.RNDfromCI (-0.1, 0.1) * (rangeMax [c] - rangeMin [c]);
            }
            else
              if (i < 3 * popSize / 4)
              {
                // Random positions with an offset towards the boundaries
                double r = u.RNDprobab ();
                if (r < 0.5) position = rangeMin [c] + 0.2 * r * (rangeMax [c] - rangeMin [c]);
                else position = rangeMax [c] - 0.2 * (1.0 - r) * (rangeMax [c] - rangeMin [c]);
              }
              else
              {
                // Chaotic positions using different maps
                int mapType = i % 3;
                double chaosValue = SelectChaosMap (agent [i].gamma [c], mapType);
                position = rangeMin [c] + chaosValue * (rangeMax [c] - rangeMin [c]);
              }
    
          a [i].cB [c] = u.SeInDiSp (position, rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    
      return true;
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    LogisticMapメソッドは、カオス系列を生成するために使用されるロジスティック写像を実装しています。この関数は、解探索にランダム性と多様性を導入するためにアルゴリズム内で利用されます。メソッドの主な考え方は、カオス挙動を強化するためにわずかに変動するパラメータを用いたロジスティック写像の方程式に基づき、現在の状態値から新しい状態値を計算することです。

    計算する前に、入力値の妥当性および範囲チェックが実施されます。入力値が条件を満たしていない場合は、指定範囲内のランダムな値に置き換えられます。新しい値を計算した後も、その値が許容範囲内にあるかどうかが確認され、必要に応じてランダム値へ置き換えられます。これにより、関数の安定性が維持されます。結果として、この内部ロジックにより、許容範囲内を維持しながら次の状態が生成されるようになっています。

    //——————————————————————————————————————————————————————————————————————————————
    // Improved chaotic maps
    double C_AO_COA_chaos::LogisticMap (double x)
    {
      // Protection against incorrect inputs
      if (x < 0.0 || x > 1.0 || MathIsValidNumber (x) == false)
      {
        x = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      // x(n+1) = r*x(n)*(1-x(n))
      double r = 3.9 + 0.1 * u.RNDprobab (); // Slightly randomized parameter to avoid loops
      double result = r * x * (1.0 - x);
    
      // Additional validation
      if (result < 0.0 || result > 1.0 || MathIsValidNumber (result) == false)
      {
        result = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      return result;
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    SineMapメソッドは、正弦関数に基づくカオス写像を実装しています。このメソッドは現在の状態値を受け取り、その妥当性を確認します。値が不正である場合、または[0, 1]の範囲外にある場合には、その範囲内のランダムな値に置き換えられます。その後、正弦関数を用いて新しい値を計算し、再び[0, 1]の範囲内に収まるよう正規化します。さらに、計算後の値に対して追加のチェックが実施されます。

    最終的な値が範囲外である場合、または無効な値である場合には、[0.2, 0.8]の範囲内のランダムな数値に再度置き換えられます。その結果、このメソッドはカオス写像を利用して現在の状態から生成された新しい状態値を返します。

    //——————————————————————————————————————————————————————————————————————————————
    double C_AO_COA_chaos::SineMap (double x)
    {
      // Protection against incorrect inputs
      if (x < 0.0 || x > 1.0 || MathIsValidNumber (x) == false)
      {
        x = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      // x(n+1) = sin(π*x(n))
      double result = MathSin (M_PI * x);
    
      // Normalize the result to the range [0, 1]
      result = (result + 1.0) / 2.0;
    
      // Additional validation
      if (result < 0.0 || result > 1.0 || MathIsValidNumber (result) == false)
      {
        result = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      return result;
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    TentMapメソッドは、カオス系列を生成するためのテント写像を実装しています。このメソッドは、0から1の範囲内にあるべき入力値xを受け取り、その妥当性を確認します。必要に応じて、xは許容範囲内のランダムな値に置き換えられます。次に、2に近い値を持つパラメータmuを用いて、「テント」写像に特徴的な区分線形関数に基づき、新しい値が計算されます。

    計算後には、得られた値が有効であるかどうかを再度確認し、必要に応じてランダムな値を用いて正規化がおこなわれます。最後に、このメソッドは新しく生成されたランダム性を持つ値を返します。

    //——————————————————————————————————————————————————————————————————————————————
    double C_AO_COA_chaos::TentMap (double x)
    {
      // Protection against incorrect inputs
      if (x < 0.0 || x > 1.0 || MathIsValidNumber (x) == false)
      {
        x = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      // Tent map: x(n+1) = μ*min(x(n), 1-x(n))
      double mu = 1.99; // Parameter close to 2 for chaotic behavior
      double result;
    
      if (x <= 0.5) result = mu * x;
      else result = mu * (1.0 - x);
    
      // Additional validation
      if (result < 0.0 || result > 1.0 || MathIsValidNumber (result) == false)
      {
        result = 0.2 + 0.6 * u.RNDprobab ();
      }
    
      return result;
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    SelectChaosMapメソッドは、指定されたタイプに応じてカオス写像関数を選択し、適用するために設計されています。このメソッドは、値xと、特定のカオス写像の種類を指定するtypeパラメータを受け取ります。このメソッドの基本的な考え方は、typeを3で割った余りを利用して写像の種類を決定することにあります。これにより、3種類の異なるカオス写像(ロジスティック写像、正弦写像、テント写像)を循環的に選択できます。判定結果に応じて、対応する関数が呼び出され、選択されたカオスダイナミクスを用いて入力値xが新しい値へ変換されます。

    また、何らかの理由でtypeが想定される範囲(0、1、2)に該当しない場合には、デフォルトとしてロジスティック写像が適用されます。これらの写像はいずれもカオス挙動を模擬しており、最適化処理の一部として、多様で予測困難な数値を生成するために使用されます。

    //——————————————————————————————————————————————————————————————————————————————
    double C_AO_COA_chaos::SelectChaosMap (double x, int type)
    {
      // Select a chaotic map based on type
      switch (type % 3)
      {
        case 0:
          return LogisticMap (x);
        case 1:
          return SineMap (x);
        case 2:
          return TentMap (x);
        default:
          return LogisticMap (x);
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    InitialPopulationメソッドは、最適化アルゴリズムの初期集団をラテン超方格サンプリング(Latin Hypercube Sampling, LHS)を用いて初期化するために設計されています。LHSは多次元の探索空間をより均一にカバーできる層化サンプリング手法であり、ランダムサンプリングと比較して初期集団の品質を向上させます。

    本メソッドはまず、ラテン超方格の値および生成処理に使用される一時的な配列を宣言します。必要な配列のメモリ確保を試み、もしメモリ確保に失敗した場合には、代替処理として初期集団をランダムに生成するバックアップ処理が実行されます。これにより、メモリ不足によってプログラムがクラッシュすることを防ぎます。

    次に、ラテン超方格の値が生成されます。各次元ごとに順序付きの値配列が作成され、その後ランダムにシャッフルされます。シャッフルされた値はラテン超方格配列に割り当てられます。これらのラテン超方格の値は、探索空間における個体の座標へと変換されます。この計算は指定された範囲に基づいておこなわれ、得られた値は必要な範囲およびステップに制限されます。

    最後に、初期集団が生成または更新されたことを示すフラグが設定されます。この手法の利点は、より多様で分散された初期集団を生成できる点にあります。

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_COA_chaos::InitialPopulation ()
    {
      // Create Latin Hypercube for the initial population
      double latinCube []; // One-dimensional array for storing hypercube values
      double tempValues []; // Temporary array for storing and shuffling values
    
      ArrayResize (latinCube, popSize * coords);
      ArrayResize (tempValues, popSize);
    
      // Generate a Latin hypercube
      for (int c = 0; c < coords; c++)
      {
        // Create ordered values
        for (int i = 0; i < popSize; i++)
        {
          tempValues [i] = (double)i / popSize;
        }
    
        // Shuffle the values
        for (int i = popSize - 1; i > 0; i--)
        {
          int j = (int)(u.RNDprobab () * (i + 1));
          if (j < popSize)
          {
            double temp = tempValues [i];
            tempValues [i] = tempValues [j];
            tempValues [j] = temp;
          }
        }
    
        // Assign the mixed values
        for (int i = 0; i < popSize; i++)
        {
          latinCube [i * coords + c] = tempValues [i];
        }
      }
    
      // Convert the Latin hypercube values to coordinates
      for (int i = 0; i < popSize; i++)
      {
        for (int c = 0; c < coords; c++)
        {
          double x = rangeMin [c] + latinCube [i * coords + c] * (rangeMax [c] - rangeMin [c]);
          a [i].c [c] = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    FirstCarrierWaveSearchメソッドは、アルゴリズムにおける探索段階を実装しており、空間の大域探索と既知の良好解に対するローカルな活用とのバランスを取ることを目的としています。主な役割は、エージェントの位置と速度を更新し、潜在的な解を継続的に探索・改善することです。メソッドの開始時には、現在の探索エポックにおける探索度を制御する比率が定義されます。この比率はエポックの進行に伴って二次関数的に減少し、これにより大域探索から局所的改善へと徐々に重点が移行するよう設計されています。その後、各エージェントに対して処理がおこなわれ、一定の確率で突然変異テストが実施されます。これにより解の多様性が維持され、向上されます。続いて各探索方向(各座標)に対して以下の処理がおこなわれます。

    • 新しい潜在解を生成するために使用するカオス写像の種類が選択される
    • 大域探索または局所探索のいずれかの戦略が選択される

    大域探索では、カオス成分を用いてエージェントの位置が更新され、速度は慣性および運動方向を考慮して調整されます。一方、局所探索では、エージェントはこれまでに発見された最良解に重点を置き、それらへの重み付きの引力に従って移動しますが、ループに陥ることを防ぐために小さなランダム変動が加えられます。いずれの場合も、速度は過度に探索空間の外へ飛び出さないよう制限されます。エージェントの位置は探索空間の制約を考慮して更新され、制約違反が検出された場合には補正されます。この際、位置は調整され、速度は次のステップを安定化させるために減衰されます。

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_COA_chaos::FirstCarrierWaveSearch ()
    {
      // Adaptive balance between exploration and exploitation
      double globalPhase = (double)epochNow / S1;
      double explorationRate = 1.0 - globalPhase * globalPhase; // Quadratic decrease
    
      // For each agent
      for (int i = 0; i < popSize; i++)
      {
        // Apply mutations with some probability to increase diversity
        if (u.RNDprobab () < mutationRate * (1.0 + explorationRate))
        {
          ApplyMutation (i);
          continue;
        }
    
        for (int c = 0; c < coords; c++)
        {
          // Select a chaotic map with uniform distribution
          int mapType = ((i + c + epochNow) % 3);
    
          // Safely check access to the gamma array
          if (c < ArraySize (agent [i].gamma))
          {
            agent [i].gamma [c] = SelectChaosMap (agent [i].gamma [c], mapType);
          }
          else
          {
            continue; // Skip if the index is invalid
          }
    
          // Determine the relationship between global and local search
          double strategy = u.RNDprobab ();
          double x;
    
          if (strategy < explorationRate)
          {
            // Global search with a chaotic component
            x = rangeMin [c] + agent [i].gamma [c] * (rangeMax [c] - rangeMin [c]);
    
            // Add a velocity component to maintain the movement direction
            agent [i].velocity [c] = inertia * agent [i].velocity [c] +
                                     (1.0 - inertia) * (x - a [i].c [c]);
          }
          else
          {
            // Local search around the best solutions
            double personalAttraction = u.RNDprobab ();
            double globalAttraction = u.RNDprobab ();
    
            // Balanced attraction to the best solutions
            double attractionTerm = //personalAttraction * (agent [i].cPrev [c] - a [i].c [c]) +
                                    personalAttraction * (a [i].cB [c] - a [i].c [c]) +
                                    socialFactor * globalAttraction * (cB [c] - a [i].c [c]);
    
            // Chaotic disturbance to prevent being stuck
            double chaosRange = alpha [c] * explorationRate;
            double chaosTerm = chaosRange * (2.0 * agent [i].gamma [c] - 1.0);
    
            // Update velocity with inertia
            agent [i].velocity [c] = inertia * agent [i].velocity [c] +
                                     (1.0 - inertia) * (attractionTerm + chaosTerm);
          }
    
          // Limit the velocity to prevent too large steps
          double maxVelocity = 0.1 * (rangeMax [c] - rangeMin [c]);
          if (MathAbs (agent [i].velocity [c]) > maxVelocity)
          {
            agent [i].velocity [c] = maxVelocity * (agent [i].velocity [c] > 0 ? 1.0 : -1.0);
          }
    
          // Apply the velocity to the position
          x = a [i].c [c] + agent [i].velocity [c];
    
          // Apply search space restrictions
          a [i].c [c] = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]);
    
          // Check the constraints and apply a smooth correction
          double violation = CalculateConstraintValue (i, c);
          if (violation > eps)
          {
            double gradient = CalculateWeightedGradient (i, c);
            double correction = -gradient * violation * (1.0 - globalPhase);
            a [i].c [c] = u.SeInDiSp (a [i].c [c] + correction, rangeMin [c], rangeMax [c], rangeStep [c]);
    
            // Reset the velocity when correcting violations
            agent [i].velocity [c] *= 0.5;
          }
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    SecondCarrierWaveSearchメソッドは、初期探索の後に実行される最適化段階であり、得られた解をさらに深化させ、精緻化することを目的としています。このメソッドの主な目標は、前段階で得られた結果をより高度な探索戦略およびパラメータ適応を用いて改善することです。

    メソッドはまず、局所探索フェーズを反映するパラメータを計算することから開始されます。このパラメータは時間の経過とともに強化され、アルゴリズムが広範な探索から、既知の解の周辺に対するより詳細かつ精密な探索へと徐々に移行することを可能にします。次に、アルゴリズムが収束状態に達しているかどうかの確認がおこなわれます。もし安定状態に到達している場合、一部のエージェントに対して突然変異が適用され、解の多様性を確保し局所最適への停滞を回避します。

    その後、各エージェントに対して、その空間内で新しい解を探索する逐次処理が実行されます。この際、カオス写像が用いられ、ランダム性の要素が導入されます。探索パラメータは最適解に近づくにつれて減少し、現在の最良解の周辺ではより狭い範囲に集中した探索がおこなわれます。各位置の更新においては、エージェントの過去の成果も考慮されます。基準となるポイントは、絶対的なグローバル最適解、あるいはエージェント自身の個人的な最良解のいずれかであり、個別の経験と集団全体の成果の両方を反映する仕組みとなっています。

    位置更新にはカオス的バイアスに加え、レヴィノイズなどのランダムノイズが導入されており、これにより新しい潜在的に優れた解の探索が促進されます。また、速度更新には慣性が考慮されており、急激な変化を防ぎつつ安定した探索が維持されます。最終的に、更新された位置は定義された境界条件によって制限され、問題の制約を満たすように調整されます。

    このように、SecondCarrierWaveSearchメソッドは既存の解をより精密かつ深く最適化するための処理を担っています。

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_COA_chaos::SecondCarrierWaveSearch ()
    {
      // Refining local search with adaptive parameters
      double localPhase = (double)(epochNow - S1) / S2;
      double intensificationRate = localPhase * localPhase; // Quadratic increase in intensification
    
      // Check the algorithm convergence
      bool isConverged = IsConverged ();
    
      // For each agent
      for (int i = 0; i < popSize; i++)
      {
        // If convergence is detected, add a random mutation to some agents
        if (isConverged && i % 3 == 0)
        {
          ApplyMutation (i);
          continue;
        }
    
        for (int c = 0; c < coords; c++)
        {
          // Select a chaotic map with uniform distribution
          int mapType = ((i * c + epochNow) % 3);
          agent [i].gamma [c] = SelectChaosMap (agent [i].gamma [c], mapType);
    
          // Adaptive search radius with narrowing towards the end of optimization
          double adaptiveAlpha = alpha [c] * (1.0 - 0.8 * intensificationRate);
    
          // Select a base point with priority to the best solutions
          double basePoint;
          if (a [i].f > a [i].fB)
          {
            basePoint = a [i].c [c];  // The current position is better
          }
          else
          {
            double r = u.RNDprobab ();
    
            if (r < 0.7 * (1.0 + intensificationRate)) // Increase attraction to the global best
            {
              basePoint = cB [c];  // Global best
            }
            else
            {
              basePoint = a [i].cB [c];  // Personal best
            }
          }
    
          // Local search with a chaotic component
          double chaosOffset = adaptiveAlpha * (2.0 * agent [i].gamma [c] - 1.0);
    
          // Add Levy noise for random long jumps (heavy tailed distribution) 
          double levyNoise = 0.0;
          if (u.RNDprobab () < 0.1 * (1.0 - intensificationRate))
          {
            // Simplified approximation of Levy noise
            double u1 = u.RNDprobab ();
            double u2 = u.RNDprobab ();
    
            if (u2 > 0.01) // Protection against division by very small numbers
            {
              levyNoise = 0.01 * u1 / MathPow (u2, 0.5) * adaptiveAlpha * (rangeMax [c] - rangeMin [c]);
            }
          }
    
          // Update the velocity with inertia
          agent [i].velocity [c] = inertia * (1.0 - 0.5 * intensificationRate) * agent [i].velocity [c] +
                                   (1.0 - inertia) * (chaosOffset + levyNoise);
    
          // Apply the velocity to the position
          double x = basePoint + agent [i].velocity [c];
    
          // Limit the position
          a [i].c [c] = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //——————————————————————————————————————————————————————————————————————————————
    

    次回の記事では、アルゴリズムの残りのメソッドについて引き続き検討し、テストを実施したうえで結果をまとめます。 

    MetaQuotes Ltdによってロシア語から翻訳されました。
    元の記事: https://www.mql5.com/ru/articles/16729

    添付されたファイル |
    COAfCHAOSp.zip (203.55 KB)
    カオス最適化アルゴリズム(COA):続編 カオス最適化アルゴリズム(COA):続編
    引き続き、カオス最適化アルゴリズムの研究を進めていきます。記事の後半では、アルゴリズムの実装、テスト、および結論といった実践的な側面について述べます。
    価格変動の角度分析:金融市場予測のためのハイブリッドモデル 価格変動の角度分析:金融市場予測のためのハイブリッドモデル
    金融市場の角度分析とは何でしょうか。プライスアクションにおける「角度」をどのように活用すれば、機械学習によって67%の予測精度を達成できるのでしょうか。さらに、角度特徴量を用いた回帰モデルと分類モデルをどのように統合し、実用可能なアルゴリズムへと落とし込むことができるのでしょうか。ギャンはこれとどのような関係があるのでしょうか。価格変動の角度が機械学習において有効な指標である理由は何でしょうか。
    FX裁定取引:リスク管理を伴う公正価値への回帰を目指す行列取引システム FX裁定取引:リスク管理を伴う公正価値への回帰を目指す行列取引システム
    本記事では、クロスレート計算アルゴリズムの詳細な説明、不均衡マトリクスの可視化、さらに効率的な取引のためのMinDiscrepancyおよびMaxRiskパラメータの最適な設定方法について解説します。本システムは、クロスレートを用いて各通貨ペアの「公正価値」を自動的に算出し、価格が公正価値より低い方向へ乖離した場合には買いシグナルを、高い方向へ乖離した場合には売りシグナルを生成します。
    取引アルゴリズムにおけるゲーム理論的アプローチの活用 取引アルゴリズムにおけるゲーム理論的アプローチの活用
    DQN(Deep Q-Network)ベースの機械学習を用いた多次元的な因果推論に基づく自己学習型トレーディングEAを構築します。このEAは7つの通貨ペアを同時に取引し、異なる通貨ペア間のエージェントが相互に情報を交換します。