English Русский Español Deutsch Português
preview
効率的な最適化のバックボーンとしての母集団アルゴリズムの基本クラス

効率的な最適化のバックボーンとしての母集団アルゴリズムの基本クラス

MetaTrader 5テスター | 12 8月 2024, 13:16
61 0
Andrey Dik
Andrey Dik

内容

1.はじめに:母集団アルゴリズムの基本クラスからの継承の展望と機会
2.母集団アルゴリズムの基本クラスの実装
3.子孫アルゴリズムのコード
4.全アルゴリズムの統一テストスタンドのコード
5. 一般的テスト関数の追加
6.3Dテスト関数の構築
7.終わりに


1.はじめに:母集団アルゴリズムの基本クラスからの継承の展望と機会

現代のコンピューティングと人工知能の世界では、最適化アルゴリズムを最終的なソフトウェアソリューションに統合するために設計された基本クラスが、開発者にとって技術的可能性の無限の地平を切り開く重要な要素となっています。母集団アルゴリズムの文脈におけるこの基本クラスからの継承は、新たな最適化手法の開発における利便性と効率性を提供するだけでなく、多種多様な問題に適応できるハイブリッドアルゴリズムを生み出す可能性を広げます。

基本クラス内で最適化アルゴリズムを組み合わせることで、さまざまな手法の長所を組み合わせた革新的なソリューションを生み出す道が開けます。このアプローチから生まれたハイブリッドアルゴリズムは、個々の手法の限界を効果的に克服し、複雑な最適化問題を解く上で新たな高みに到達することができます。

さらに、母集団アルゴリズム用の基本クラスは、開発したアルゴリズムの使いやすさと、標準的なテスト関数セットでのテストを保証します。これにより、研究者や開発者は、既存のソリューションと性能を比較することで、新しい最適化手法の効率を迅速に評価することができます。

最適化と解決策の探索の世界を素晴らしい料理の世界、最適化手法をそれぞれ料理に独自の味を与えるユニークな食材として想像してみましょう。ここでいうハイブリダイゼーションとは、異なる食材を巧みに組み合わせて、より美味しく、より興味深い新しい料理を作り出すようなものです。

遺伝的アルゴリズム、進化戦略、蟻アルゴリズム、粒子群最適化、そしてその他多数の、さまざまな最適化手法があります。それぞれに長所と能力があるが、限界もあります。

そこで登場するのがハイブリダイゼーションです。熟練したシェフのように、それぞれの手法から最良のものを取り入れ、ユニークな組み合わせにすることができます。このように、ハイブリッド最適化手法は、異なるアプローチの長所を組み合わせ、それぞれの短所を補い、最適解を見つけるための、より効率的で強力なツールを作り出すことができます。

遺伝的アルゴリズムと局所探索検索の組み合わせは、料理でスパイシーな唐辛子と甘い蜂蜜が絶妙に組み合わされ、深みのある豊かな風味を与えるようなものだと考えてください。同様に、母集団アルゴリズムのハイブリダイゼーションによって、工学的問題、金融分析、人工知能など、さまざまな分野で最適解を迅速かつ正確に見つけることができる革新的な手法を生み出すことができます。

このように、最適化におけるハイブリダイゼーションとは、単に手法を混ぜ合わせることではなく、それぞれの手法の可能性を最大限に引き出し、卓越した結果を達成できる新しいアプローチを生み出す技術なのです。最終的には、ハイブリダイゼーションを通じて、より効率的で革新的かつ強力な最適化手法を生み出し、最も複雑な問題を解決し、さまざまな分野で新たな発見と進歩につなげることができます。

さらに、統一された基本クラスにより、各アルゴリズムの個々の要素をカスタムソリューションに統合し、新しくユニークで強力な最適化手法の設計に使用することができます。


2.母集団アルゴリズムの基本クラスの実装

母集団アルゴリズムの基本クラスを継承し、ハイブリッド最適化手法を作成するという記事の文脈では、いくつかの興味深い組み合わせを例として考えることができます。

  • 遺伝的アルゴリズムと強化した局所探索:この組み合わせでは、遺伝的アルゴリズムが最適解を大域的に探索するために使用され、局所探索が近傍で発見された解を改良するために使用されます。これにより、大域的探索と局所的探索の利点を組み合わせることができ、アルゴリズムの精度と収束速度が向上します。
  • 進化戦略と蟻アルゴリズム:ここでは、進化戦略はモデルパラメータを変更するために使用することができ、蟻アルゴリズムはパラメータ空間内の最適経路を見つけるために使用することができます。この組み合わせは、パラメータの最適な組み合わせを見つける必要がある複雑な問題の最適化に有効です。
  • 群粒子と遺伝的プログラミング:この組み合わせでは、解空間を探索するために群粒子を使用し、最適化問題を解くプログラム構造を進化させるために遺伝的プログラミングを使用することができます。これにより、パラメータ空間と解構造の両方を効率的に探索することができます。
  • 焼きなまし探索と遺伝的アルゴリズム:ここでは、焼きなましを用いて温度レジームを考慮した解空間を探索し、遺伝的アルゴリズムを用いて与えられた空間内の最適解を見つけることができます。この組み合わせは、解空間のより深い探索を提供し、アルゴリズムの収束を改善することができます。

また、1つの基本クラスに組み合わされた集団アルゴリズムの能力を使用する上で、次のような方向性も考えられます。

  • 複合メタヒューリスティクス手法:この手法では、遺伝的アルゴリズム、蟻アルゴリズム、粒子群最適化アルゴリズム、焼きなましなど、複数の異なるメタヒューリスティクスアルゴリズムを組み合わせることができます。これらのアルゴリズムは並列または逐次的に動作し、情報を交換し、それぞれの強みを組み合わせることで、より効率的に最適解を見つけることができます。
  • 探索戦略の適応制御によるハイブリッド法:このアプローチでは、適応制御メカニズムを使用して、問題の特性に応じて異なる最適化戦略を動的に組み合わせることができます。例えば、現在の最適化段階でのパフォーマンスに応じて、各手法の重みやパラメータを変更することができます。
  • 人工ニューラルネットワークとのハイブリッド法:このアプローチでは、人工ニューラルネットワークを使用して、集団アルゴリズムのパラメータと最適化戦略を適応的に制御することができます。ニューラルネットワークはその場で学習し、探索空間の変化に適応し、各最適化手法に最適なパラメータを提案することができます。
  • 共同最適化と強化学習法:このアプローチは、母集団アルゴリズムと強化学習技術を組み合わせて、複雑な解空間を効率的に探索し最適化できるハイブリッドシステムを構築することができます。強化学習エージェントは、集団アルゴリズムの結果から学習することができ、またその逆も可能で、異なる最適化手法間の相互作用を生み出します。


各最適化アルゴリズムにおける外部パラメータの数が異なると、継承や統一的な適用において問題が生じる可能性があります。この問題を解決するために、広範なテストの結果に基づいて、デフォルトアルゴリズムの外部パラメータをコンストラクタで指定することにしました。同時に、アルゴリズムを初期化する前に、これらのパラメータを変更することも可能です。したがって、各アルゴリズムのオブジェクトは、使用可能な最終解を表すことになります。以前は、アルゴリズムとそのパラメータは別個のものでした。

では、アルゴリズムのパラメータから見ていきましょう。パラメータ名と値を含むS_AlgoParam構造体を用いて各パラメータを記述するのが便利です。したがって、この構造体のオブジェクトの配列は、アルゴリズムの外部パラメータのセットを表すことになります。

struct S_AlgoParam
{
    double val;
    string name;
};

各最適化アルゴリズムには探索エージェントがあります。これはホタルアルゴリズムではホタル、蜂アルゴリズムでは勤勉な蜂、蟻アルゴリズムでは勤勉な蟻など、探索戦略の基本単位であり、不可欠な参加者です。彼らは最適化の迷宮におけるユニークな芸術家であり、成功への最適な道を探し、発見する技術の素晴らしさを明らかにしています。彼らの努力と志は、まるで魔法のように、データの混沌をソリューションの調和へと変え、理想的な最適化という新たな地平への道を照らします。


したがって、各エージェントは最適化問題に対する特定の解を表し、探索空間における座標(最適化されたパラメータ)と解の質(適応度関数)という2つの必須特性を持ちます。機能と能力を拡張し、アルゴリズムの特定のプロパティを実装できるようにするために、C_AO_Agentクラスの形式でエージェントを形式化します。その後、それを継承することができます。

class C_AO_Agent
{
  public:
  ~C_AO_Agent () { }

  double c []; //coordinates
  double f;    //fitness
};

最適化アルゴリズムにおけるほとんどの論理演算は繰り返されるため、C_AO_Utilitiesクラスの関数セットとして個別に設計することができ、そのオブジェクトはアルゴリズムクラスとエージェントの両方で使用することができます。

C_AO_Utilitiesクラスには以下のメソッドがあります。

  • Scale:[InMIN、InMAX]の範囲から[OutMIN、OutMAX]の範囲に 「In」入力をスケーリングするオーバーロードされたメソッド。また、reversパラメータがtrueに設定されていれば、逆方向のスケーリングも可能
  • RNDfromCI:指定された[min, max]範囲内の乱数(実数)を生成
  • RNDintInRange:指定された[min, max]範囲内の乱数(整数)を生成
  • RNDbool:ランダムなブール値(true/false)を生成
  • RNDprobab:ランダムな確率(0から1の間の実数)を生成
  • SeInDiSp:[InMin、InMax]の範囲内で、指定されたStepを考慮した値を計算
  • DecimalToGray、 IntegerToBinary、 GrayToDecimal、 BinaryToInteger、 GetMaxDecimalFromGray各メソッド:10進数、2進数、グレイコード間の変換をおこなう
  • GaussDistributionメソッドとPowerDistributionメソッド:それぞれ正規分布とベキ分布の計算をおこなう
  • Sortingメソッド(テンプレートメソッド):T型の配列pを降順に並び替え
  • S_Roulette構造体:範囲を表すstartフィールドとendフィールドを含む
  • PreCalcRouletteメソッドとSpinRouletteメソッド:PreCalcRouletteはT型オブジェクトの範囲を計算し、roulette配列に保存。SpinRouletteは、aPopSizeの母集団サイズに基づいてルーレットのスピンを実行
//——————————————————————————————————————————————————————————————————————————————
class C_AO_Utilities
{
  public: //--------------------------------------------------------------------
  double Scale                 (double In, double InMIN, double InMAX, double OutMIN, double OutMAX);
  double Scale                 (double In, double InMIN, double InMAX, double OutMIN, double OutMAX,  bool revers);
  double RNDfromCI             (double min, double max);
  int    RNDintInRange         (int min, int max);
  bool   RNDbool               ();
  double RNDprobab             ();
  double SeInDiSp              (double In, double InMin, double InMax, double Step);
  void   DecimalToGray         (ulong decimalNumber, char &array []);
  void   IntegerToBinary       (ulong number, char &array []);
  ulong  GrayToDecimal         (const char &grayCode [], int startInd, int endInd);
  ulong  BinaryToInteger       (const char &binaryStr [], const int startInd, const int endInd);
  ulong  GetMaxDecimalFromGray (int digitsInGrayCode);
  double GaussDistribution     (const double In, const double outMin, const double outMax, const double sigma);
  double PowerDistribution     (const double In, const double outMin, const double outMax, const double p);

  //----------------------------------------------------------------------------
  template<typename T>
  void Sorting (T &p [], T &pTemp [], int size)
  {
    int    cnt = 1;
    int    t0  = 0;
    double t1  = 0.0;
    int    ind [];
    double val [];

    ArrayResize (ind, size);
    ArrayResize (val, size);

    for (int i = 0; i < size; i++)
    {
      ind [i] = i;
      val [i] = p [i].f;
    }

    while (cnt > 0)
    {
      cnt = 0;
      for (int i = 0; i < size - 1; i++)
      {
        if (val [i] < val [i + 1])
        {
          t0 = ind [i + 1];
          t1 = val [i + 1];
          ind [i + 1] = ind [i];
          val [i + 1] = val [i];
          ind [i] = t0;
          val [i] = t1;
          cnt++;
        }
      }
    }

    for (int u = 0; u < size; u++) pTemp [u] = p [ind [u]];
    for (int u = 0; u < size; u++) p [u] = pTemp [u];
  }

  //----------------------------------------------------------------------------
  struct S_Roulette
  {
      double start;
      double end;
  };
  S_Roulette roulette [];

  template<typename T>
  void PreCalcRoulette (T &agents [])
  {
    int aPopSize = ArraySize (agents);
    roulette [0].start = agents [0].f;
    roulette [0].end   = roulette [0].start + (agents [0].f - agents [aPopSize - 1].f);

    for (int s = 1; s < aPopSize; s++)
    {
      if (s != aPopSize - 1)
      {
        roulette [s].start = roulette [s - 1].end;
        roulette [s].end   = roulette [s].start + (agents [s].f - agents [aPopSize - 1].f);
      }
      else
      {
        roulette [s].start = roulette [s - 1].end;
        roulette [s].end   = roulette [s].start + (agents [s - 1].f - agents [s].f) * 0.1;
      }
    }
  }
  int  SpinRoulette (int aPopSize);
};
//——————————————————————————————————————————————————————————————————————————————

確率最適化アルゴリズムは乱数の生成に基づいているため、各解を得るためにこの操作を何百回、あるいは何千回もおこなうことができます。したがって、特定のタスクを分離して乱数生成を最適化することが望ましいです。標準的な生成器が整数を生成することを考えれば、この処理を高速化できる可能性があります。

与えられた["min", "max"]の範囲内で乱数(実数)を生成するRNDfromCIメソッドはすでに紹介しました。

double C_AO_Utilities ::RNDfromCI (double min, double max)
{
  if (min == max) return min;
  if (min > max)
  {
    double temp = min;
    min = max;
    max = temp;
  }
  return min + ((max - min) * rand () / 32767.0);
}

例えば、母集団内のエージェントをランダムに選択するために、乱数(整数)を生成する必要があることがよくあります。これにはRNDintInRangeメソッドが役立ちます。

int C_AO_Utilities :: RNDintInRange (int min, int max)
{
  if (min == max) return min;
  if (min > max)
  {
    int temp = min;
    min = max;
    max = temp;
  }
  return min + rand () % (max - min + 1);
}

RNDboolメソッドを使えば、上記の2つのメソッドに比べて非常に素早くランダムなブール変数を得ることが可能であり、だからこそランダム変数をタスクに応じて別々のメソッドに分ける意味があります。

bool C_AO_Utilities :: RNDbool ()
{
  return rand () % 2 == 0;
}

[0.0,1.0]の範囲のランダムな実数を得るためのRNDprobabメソッドもあります。遺伝的アルゴリズムにおける交叉の確率など、特定の操作の確率を実行するのに適しています。このような操作も頻繁におこなわれています。

double C_AO_Utilities :: RNDprobab ()
{
  return (double)rand () / 32767;
}
次に、母集団最適化アルゴリズムの基本クラス「C_AO」を見てみましょう。このクラスは、以下のようなすべての母集団アルゴリズムに必要な属性を記述します。

  • C_AOクラスのメソッドとプロパティ
- SetParams:アルゴリズムのパラメータを設定する仮想メソッド
- Init:アルゴリズムを初期化する仮想メソッドで、最小探索範囲、最大探索範囲、ステップ、エポック数を渡す
- Moving:アルゴリズムステップを実行するための仮想的なメソッド
- Revision:アルゴリズムの改訂をおこなうための仮想メソッド
- GetName:アルゴリズム名を取得するメソッド
- GetDesc:アルゴリズムの説明を取得するメソッド
- GetParams:アルゴリズムのパラメータを文字列として取得するメソッド
  • C_AOクラスのprotectedプロパティ
- ao_name:アルゴリズム名
- ao_desc:アルゴリズムの説明
- rangeMin、rangeMax、 rangeStep:検索範囲の最小値と最大値、およびステップを格納する配列
- coords:座標の数
- popSize:母集団のサイズ
- revision:リビジョンフラグ
- u:補助機能を実行するためのC_AO_Utilitiesクラスオブジェクト
#include "#C_AO_Utilities.mqh"

//——————————————————————————————————————————————————————————————————————————————
class C_AO
{
  public: //--------------------------------------------------------------------
  C_AO () { }
  ~C_AO () { for (int i = 0; i < ArraySize (a); i++) delete a [i];}

  double      cB     []; //best coordinates
  double      fB;        //FF of the best coordinates
  C_AO_Agent *a      []; //agents
  S_AlgoParam params []; //algorithm parameters

  virtual void SetParams () { }
  virtual bool Init (const double &rangeMinP  [], //minimum search range
                     const double &rangeMaxP  [], //maximum search range
                     const double &rangeStepP [], //step search
                     const int     epochsP = 0)   //number of epochs
  { return false;}

  virtual void Moving   () { }
  virtual void Revision () { }

  string GetName   () { return ao_name;}
  string GetDesc   () { return ao_desc;}
  string GetParams ()
  {
    string str = "";
    for (int i = 0; i < ArraySize (params); i++)
    {
      str += (string)params [i].val + "|";
    }
    return str;
  }


  protected: //-----------------------------------------------------------------
  string ao_name;      //ao name;
  string ao_desc;      //ao description

  double rangeMin  []; //minimum search range
  double rangeMax  []; //maximum search range
  double rangeStep []; //step search

  int    coords;       //coordinates number
  int    popSize;      //population size
  bool   revision;

  C_AO_Utilities u;     //auxiliary functions

  bool StandardInit (const double &rangeMinP  [], //minimum search range
                     const double &rangeMaxP  [], //maximum search range
                     const double &rangeStepP []) //step search
  {
    MathSrand ((int)GetMicrosecondCount ()); //reset of the generator
    fB       = -DBL_MAX;
    revision = false;

    coords  = ArraySize (rangeMinP);
    if (coords == 0 || coords != ArraySize (rangeMaxP) || coords != ArraySize (rangeStepP)) return false;

    ArrayResize (rangeMin,  coords);
    ArrayResize (rangeMax,  coords);
    ArrayResize (rangeStep, coords);
    ArrayResize (cB,        coords);

    ArrayCopy (rangeMin,  rangeMinP,  0, 0, WHOLE_ARRAY);
    ArrayCopy (rangeMax,  rangeMaxP,  0, 0, WHOLE_ARRAY);
    ArrayCopy (rangeStep, rangeStepP, 0, 0, WHOLE_ARRAY);

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

E_AO列挙は最適化アルゴリズムのIDを含み、SelectAO関数は対応するアルゴリズムのインスタンスを作成し、そのポインタを取得することができます。

#include "AO_BGA_Binary_Genetic_Algorithm.mqh"
#include "AO_(P_O)ES_Evolution_Strategies.mqh"
#include "AO_DE_Differential_Evolution.mqh"
#include "AO_SDSm_Stochastic_Diffusion_Search.mqh"
#include "AO_ESG_Evolution_of_Social_Groups.mqh";

//——————————————————————————————————————————————————————————————————————————————
enum E_AO
{
  AO_BGA,
  AO_P_O_ES,
  AO_SDSm,
  AO_ESG,
  AO_DE,
  AO_NONE
};
C_AO *SelectAO (E_AO a)
{
  C_AO *ao;
  switch (a)
  {
    case  AO_BGA:
      ao = new C_AO_BGA (); return (GetPointer (ao));
    case  AO_P_O_ES:
      ao = new C_AO_P_O_ES (); return (GetPointer (ao));
    case  AO_SDSm:
      ao = new C_AO_SDSm (); return (GetPointer (ao));
    case  AO_ESG:
      ao = new C_AO_ESG (); return (GetPointer (ao));
    case  AO_DE:
      ao = new C_AO_DE (); return (GetPointer (ao));

    default:
      ao = NULL; return NULL;
  }
}
//——————————————————————————————————————————————————————————————————————————————


3.子孫アルゴリズムのコード

基本クラスからの継承の例として、確率拡散探索アルゴリズム(SDSm)を考えてみましょう。このアルゴリズムのC_SDS_Agentは、基となるC_AO_Agentから継承します。エージェントの初期化メソッドには「c」座標と「f」適応度が含まれていますが、C_SDS_Agentクラスでは宣言されていないことに注意してください。なぜなら、これらの属性はすべての最適化アルゴリズムエージェントに必要であり、基本アルゴリズムから継承されるため、改めて宣言する必要がないからです。

//——————————————————————————————————————————————————————————————————————————————
class C_SDS_Agent : public C_AO_Agent
{
  public: //--------------------------------------------------------------------
  ~C_SDS_Agent () { }

  int    raddr     []; //restaurant address
  int    raddrPrev []; //previous restaurant address
  double cPrev     []; //previous coordinates (dishes)
  double fPrev;        //previous fitness

  void Init (int coords)
  {
    ArrayResize (c,         coords);
    ArrayResize (cPrev,     coords);
    ArrayResize (raddr,     coords);
    ArrayResize (raddrPrev, coords);
    f        = -DBL_MAX;
    fPrev    = -DBL_MAX;
  }
};
//——————————————————————————————————————————————————————————————————————————————

SDSmアルゴリズムのC_AO_SDSmクラスは、C_AOクラスから派生したものです。コンストラクタでクラスオブジェクトを宣言する際、アルゴリズムの外部パラメータを初期化します。これは、必要に応じてユーザーが後で変更することができます。パラメータは配列の形で用意され、テストベンチとの互換性を心配する必要はありません。

//——————————————————————————————————————————————————————————————————————————————
class C_AO_SDSm : public C_AO
{
  public: //--------------------------------------------------------------------
  ~C_AO_SDSm () { }
  C_AO_SDSm ()
  {
    ao_name = "SDSm";
    ao_desc = "Stochastic Diffusion Search";

    popSize    = 100; //population size

    restNumb   = 100;  //restaurants number
    probabRest = 0.05; //probability restaurant choosing

    ArrayResize (params, 3);

    params [0].name = "popSize";    params [0].val  = popSize;

    params [1].name = "restNumb";   params [1].val  = restNumb;
    params [2].name = "probabRest"; params [2].val  = probabRest;
  }

  void SetParams ()
  {
    popSize    = (int)params [0].val;

    restNumb   = (int)params [1].val;
    probabRest = params      [2].val;
  }

  bool Init (const double &rangeMinP  [], //minimum search range
             const double &rangeMaxP  [], //maximum search range
             const double &rangeStepP [], //step search
             const int     epochsP = 0);  //number of epochs

  void Moving   ();
  void Revision ();

  //----------------------------------------------------------------------------
  int    restNumb;          //restaurants number
  double probabRest;        //probability restaurant choosing

  C_SDS_Agent *agent []; //candidates

  private: //-------------------------------------------------------------------
  struct S_Riverbed //river bed
  {
      double coordOnSector []; //coordinate on the sector (number of cells: number of sectors on the coordinate, cell value: specific coordinate on the sector)
  };

  double restSpace [];      //restaurants space
  S_Riverbed    rb [];      //riverbed

  void Research  (const double  ko,
                  const int     raddr,
                  const double  restSpace,
                  const double  rangeMin,
                  const double  rangeStep,
                  const double  pitOld,
                  double       &pitNew);
};
//——————————————————————————————————————————————————————————————————————————————
次に、C_AO_SDSmクラスの初期化メソッドInitについて考えてみましょう。このメソッドは次をおこないます。

1.最初から、基本クラスのメソッドStandardInitを呼び出し、rangeMinP、rangeMaxP、rangeStepPを渡す必要があります。このメソッドがfalseを返した場合、Init関数もfalseを返します。これは、アルゴリズムの初期化に失敗したことを示します。
2.deleteによるエージェントの削除これはアルゴリズムオブジェクトを再利用するときに必要です。
3.次に、SDSmアルゴリズムのagent配列と基本クラスの「a」のサイズをpopSizeに変更し、型キャストを実行します。
4.次に、SDSmに関する記事で説明されているアルゴリズムと同様のアクションを実行します。
//——————————————————————————————————————————————————————————————————————————————
bool C_AO_SDSm::Init (const double &rangeMinP  [], //minimum search range
                      const double &rangeMaxP  [], //maximum search range
                      const double &rangeStepP [], //step search
                      const int     epochsP = 0)   //number of epochs
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //----------------------------------------------------------------------------
  for (int i = 0; i < ArraySize (agent); i++) delete agent [i];

  ArrayResize (agent, popSize);
  ArrayResize (a,     popSize);

  for (int i = 0; i < popSize; i++)
  {
    a     [i] = new C_SDS_Agent ();
    agent [i] = (C_SDS_Agent *)a [i];

    agent [i].Init (coords);
  }

  ArrayResize (restSpace, coords);
  ArrayResize (rb,        coords);
  for (int i = 0; i < coords; i++)
  {
    ArrayResize     (rb [i].coordOnSector, restNumb);
    ArrayInitialize (rb [i].coordOnSector, -DBL_MAX);
  }

  for (int i = 0; i < coords; i++)
  {
    restSpace [i] = (rangeMax [i] - rangeMin [i]) / restNumb;
  }

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


4.全アルゴリズムの統一テストスタンドのコード

今のところテストスタンドを「増殖」させる必要はありませんが、それでもテストスタンドのすべての機能をC_TestStandクラスに移すことにします。これでスタンドの機能を便利にカプセル化できます。標準関数に大きな変更はないので、詳細な説明は省きます。その現状を簡単に見てみましょう。

#include <Canvas\Canvas.mqh>
#include <\Math\Functions.mqh>

//——————————————————————————————————————————————————————————————————————————————
class C_TestStand
{
  public: void Init (int width, int height)
  {
    W = width;  //750;
    H = height; //375;

    WscrFunc = H - 2;
    HscrFunc = H - 2;

    //creating a table ---------------------------------------------------------
    string canvasName = "AO_Test_Func_Canvas";

    if (!Canvas.CreateBitmapLabel (canvasName, 5, 30, W, H, COLOR_FORMAT_ARGB_RAW))
    {
      Print ("Error creating Canvas: ", GetLastError ());
      return;
    }

    ObjectSetInteger (0, canvasName, OBJPROP_HIDDEN, false);
    ObjectSetInteger (0, canvasName, OBJPROP_SELECTABLE, true);

    ArrayResize (FunctScrin, HscrFunc);

    for (int i = 0; i < HscrFunc; i++) ArrayResize (FunctScrin [i].clr, HscrFunc);

  }

  struct S_CLR
  {
    color clr [];
  };

  //----------------------------------------------------------------------------
  public: void CanvasErase ()
  {
    Canvas.Erase (XRGB (0, 0, 0));
    Canvas.FillRectangle (1,     1, H - 2, H - 2, COLOR2RGB (clrWhite));
    Canvas.FillRectangle (H + 1, 1, W - 2, H - 2, COLOR2RGB (clrWhite));
  }

  //----------------------------------------------------------------------------
  public: void MaxMinDr (C_Function & f)
  {
    //draw Max global-------------------------------------------------------------
    int x = (int)Scale(f.GetMaxFuncX(), f.GetMinRangeX(), f.GetMaxRangeX(), 1, W/2 - 1, false);
    int y = (int)Scale(f.GetMaxFuncY(), f.GetMinRangeY(), f.GetMaxRangeY(), 1, H   - 1, true);

    Canvas.Circle(x, y, 12, COLOR2RGB(clrBlack));
    Canvas.Circle(x, y, 13, COLOR2RGB(clrBlack));
    Canvas.Circle(x, y, 14, COLOR2RGB(clrBlack));
    Canvas.Circle(x, y, 15, COLOR2RGB(clrBlack));

    //draw Min global-------------------------------------------------------------
    x = (int)Scale(f.GetMinFuncX(), f.GetMinRangeX(), f.GetMaxRangeX(), 0, W/2 - 1, false);
    y = (int)Scale(f.GetMinFuncY(), f.GetMinRangeY(), f.GetMaxRangeY(), 0, H - 1, true);

    Canvas.Circle(x, y, 12, COLOR2RGB(clrBlack));
    Canvas.Circle(x, y, 13, COLOR2RGB(clrBlack));
  }

  //----------------------------------------------------------------------------
  public: void PointDr (double &args [], C_Function & f, int shiftX, int shiftY, int count, bool main)
  {
    double x = 0.0;
    double y = 0.0;

    double xAve = 0.0;
    double yAve = 0.0;

    int width  = 0;
    int height = 0;

    color clrF = clrNONE;

    for(int i = 0; i < count; i++)
    {
      xAve += args [i * 2];
      yAve += args [i * 2 + 1];

      x = args [i * 2];
      y = args [i * 2 + 1];

      width  = (int)Scale(x, f.GetMinRangeX(), f.GetMaxRangeX(), 0, WscrFunc - 1, false);
      height = (int)Scale(y, f.GetMinRangeY(), f.GetMaxRangeY(), 0, HscrFunc - 1, true);

      clrF = DoubleToColor(i, 0, count - 1, 0, 270);
      Canvas.FillCircle(width + shiftX, height + shiftY, 1, COLOR2RGB(clrF));
    }

    xAve /=(double)count;
    yAve /=(double)count;

    width  = (int)Scale(xAve, f.GetMinRangeX(), f.GetMaxRangeX(), 0, WscrFunc - 1, false);
    height = (int)Scale(yAve, f.GetMinRangeY(), f.GetMaxRangeY(), 0, HscrFunc - 1, true);

    if(!main)
    {
      Canvas.FillCircle(width + shiftX, height + shiftY, 3, COLOR2RGB(clrBlack));
      Canvas.FillCircle(width + shiftX, height + shiftY, 2, COLOR2RGB(clrWhite));
    }
    else
    {
      Canvas.Circle (width + shiftX, height + shiftY, 5, COLOR2RGB (clrBlack));
      Canvas.Circle (width + shiftX, height + shiftY, 6, COLOR2RGB (clrBlack));
    }
  }

  //----------------------------------------------------------------------------
  public: void SendGraphToCanvas ()
  {
    for (int w = 0; w < HscrFunc; w++)
    {
      for (int h = 0; h < HscrFunc; h++)
      {
        Canvas.PixelSet (w + 1, h + 1, COLOR2RGB (FunctScrin [w].clr [h]));
      }
    }
  }

  //----------------------------------------------------------------------------
  public: void DrawFunctionGraph (C_Function & f)
  {
    double ar [2];
    double fV;

    for (int w = 0; w < HscrFunc; w++)
    {
      ar [0] = Scale (w, 0, H, f.GetMinRangeX (), f.GetMaxRangeX (), false);
      for (int h = 0; h < HscrFunc; h++)
      {
        ar [1] = Scale (h, 0, H, f.GetMinRangeY (), f.GetMaxRangeY (), true);
        fV = f.CalcFunc (ar, 1);
        FunctScrin [w].clr [h] = DoubleToColor (fV, f.GetMinFunValue (), f.GetMaxFunValue (), 0, 270);
      }
    }
  }

  //----------------------------------------------------------------------------
  public: void Update ()
  {
    Canvas.Update ();
  }

  //----------------------------------------------------------------------------
  //Scaling a number from a range to a specified range
  public: double Scale (double In, double InMIN, double InMAX, double OutMIN, double OutMAX, bool Revers = false)
  {
    if (OutMIN == OutMAX) return (OutMIN);
    if (InMIN == InMAX) return ((OutMIN + OutMAX) / 2.0);
    else
    {
      if (Revers)
      {
        if (In < InMIN) return (OutMAX);
        if (In > InMAX) return (OutMIN);
        return (((InMAX - In) * (OutMAX - OutMIN) / (InMAX - InMIN)) + OutMIN);
      }
      else
      {
        if (In < InMIN) return (OutMIN);
        if (In > InMAX) return (OutMAX);
        return (((In - InMIN) * (OutMAX - OutMIN) / (InMAX - InMIN)) + OutMIN);
      }
    }
  }

  //----------------------------------------------------------------------------
  private: color DoubleToColor (const double In,    //input value
                                const double inMin, //minimum of input values
                                const double inMax, //maximum of input values
                                const int    loH,   //lower bound of HSL range values
                                const int    upH)   //upper bound of HSL range values
  {
    int h = (int) Scale (In, inMin, inMax, loH, upH, true);
    return HSLtoRGB (h, 1.0, 0.5);
  }

  //----------------------------------------------------------------------------
  private: color HSLtoRGB (const int    h, //0   ... 360
                           const double s, //0.0 ... 1.0
                           const double l) //0.0 ... 1.0
  {
    int r;
    int g;
    int b;
    if (s == 0.0)
    {
      r = g = b = (unsigned char)(l * 255);
      return StringToColor ((string) r + "," + (string) g + "," + (string) b);
    }
    else
    {
      double v1, v2;
      double hue = (double) h / 360.0;
      v2 = (l < 0.5) ? (l * (1.0 + s)) : ((l + s) - (l * s));
      v1 = 2.0 * l - v2;
      r = (unsigned char)(255 * HueToRGB (v1, v2, hue + (1.0 / 3.0)));
      g = (unsigned char)(255 * HueToRGB (v1, v2, hue));
      b = (unsigned char)(255 * HueToRGB (v1, v2, hue - (1.0 / 3.0)));
      return StringToColor ((string) r + "," + (string) g + "," + (string) b);
    }
  }

  //----------------------------------------------------------------------------
  private: double HueToRGB (double v1, double v2, double vH)
  {
    if (vH < 0) vH += 1;
    if (vH > 1) vH -= 1;
    if ((6 * vH) < 1) return (v1 + (v2 - v1) * 6 * vH);
    if ((2 * vH) < 1) return v2;
    if ((3 * vH) < 2) return (v1 + (v2 - v1) * ((2.0f / 3) - vH) * 6);
    return v1;
  }

  //----------------------------------------------------------------------------
  public: int W; //monitor screen width
  public: int H; //monitor screen height

  private: int WscrFunc; //test function screen width
  private: int HscrFunc; //test function screen height

  public:  CCanvas Canvas;      //drawing table
  private: S_CLR FunctScrin []; //two-dimensional matrix of colors
};
//——————————————————————————————————————————————————————————————————————————————
では、テストスタンドのコードそのものを見てみましょう。スタンドの入力を調べると、最適化アルゴリズムとテスト関数を設定で選択できるようになったことがわかります。これにより、アルゴリズムをテストするためのユニークなテスト関数のセットを作成することができます。これで、平滑関数だけ、あるいは離散関数だけのテストができるようになりました。さらに、ユーザーのニーズに合わせて検査機能の組み合わせを自由に選択できるようになりました。
#include "PAO\#C_TestStandFunctions.mqh"
#include "PAO\#C_AO.mqh"

//——————————————————————————————————————————————————————————————————————————————
input string AOparam            = "----------------"; //AO parameters-----------
input E_AO   AOexactly_P        = AO_NONE;

input string TestStand_1        = "----------------"; //Test stand--------------
input double ArgumentStep_P     = 0.0;   //Argument Step

input string TestStand_2        = "----------------"; //------------------------
input int    Test1FuncRuns_P    = 5;     //Test #1: Number of functions in the test
input int    Test2FuncRuns_P    = 25;    //Test #2: Number of functions in the test
input int    Test3FuncRuns_P    = 500;   //Test #3: Number of functions in the test

input string TestStand_3        = "----------------"; //------------------------
input EFunc  Function1          = Hilly;
input EFunc  Function2          = Forest;
input EFunc  Function3          = Megacity;

input string TestStand_4        = "----------------"; //------------------------
input int    NumbTestFuncRuns_P = 10000; //Number of test function runs
input int    NumberRepetTest_P  = 10;    //Test repets number

input string TestStand_5        = "----------------"; //------------------------
input bool   Video_P            = true;  //Show video
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
void OnStart ()
{
  C_AO *AO = SelectAO (AOexactly_P);
  if (AO == NULL)
  {
    Print ("AO is not selected...");
    return;
  }

  Print (AO.GetName (), "|", AO.GetDesc (), "|", AO.GetParams ());

  //============================================================================
  C_TestStand ST; //stand
  ST.Init (750, 375);

  double allScore = 0.0;
  double allTests = 0.0;

  C_Function *F1 = SelectFunction (Function1);
  C_Function *F2 = SelectFunction (Function2);
  C_Function *F3 = SelectFunction (Function3);

  if (F1 != NULL)
  {
    Print ("=============================");
    ST.CanvasErase ();

    FuncTests (AO, ST, F1, Test1FuncRuns_P, clrLime,      allScore, allTests);
    FuncTests (AO, ST, F1, Test2FuncRuns_P, clrAqua,      allScore, allTests);
    FuncTests (AO, ST, F1, Test3FuncRuns_P, clrOrangeRed, allScore, allTests);
    delete F1;
  }

  if (F2 != NULL)
  {
    Print ("=============================");
    ST.CanvasErase ();
    FuncTests (AO, ST, F2, Test1FuncRuns_P, clrLime,      allScore, allTests);
    FuncTests (AO, ST, F2, Test2FuncRuns_P, clrAqua,      allScore, allTests);
    FuncTests (AO, ST, F2, Test3FuncRuns_P, clrOrangeRed, allScore, allTests);
    delete F2;
  }

  if (F3 != NULL)
  {
    Print ("=============================");
    ST.CanvasErase ();
    FuncTests (AO, ST, F3, Test1FuncRuns_P, clrLime,      allScore, allTests);
    FuncTests (AO, ST, F3, Test2FuncRuns_P, clrAqua,      allScore, allTests);
    FuncTests (AO, ST, F3, Test3FuncRuns_P, clrOrangeRed, allScore, allTests);
    delete F3;
  }

  Print ("=============================");
  if (allTests > 0.0) Print ("All score: ", DoubleToString (allScore, 5), " (", DoubleToString (allScore * 100 / allTests, 2), "%)");
  delete AO;
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
void FuncTests (C_AO          &ao,
                C_TestStand   &st,
                C_Function    &f,
                const  int     funcCount,
                const  color   clrConv,
                double        &allScore,
                double        &allTests)
{
  if (funcCount <= 0) return;

  allTests++;

  if (Video_P)
  {
    st.DrawFunctionGraph (f);
    st.SendGraphToCanvas ();
    st.MaxMinDr          (f);
    st.Update            ();
  }

  int    xConv      = 0.0;
  int    yConv      = 0.0;
  double aveResult  = 0.0;
  int    params     = funcCount * 2;
  int    epochCount = NumbTestFuncRuns_P / (int)ao.params [0].val;

  //----------------------------------------------------------------------------
  double rangeMin  [], rangeMax  [], rangeStep [];
  ArrayResize (rangeMin,  params);
  ArrayResize (rangeMax,  params);
  ArrayResize (rangeStep, params);

  for (int i = 0; i < funcCount; i++)
  {
    rangeMax  [i * 2] = f.GetMaxRangeX ();
    rangeMin  [i * 2] = f.GetMinRangeX ();
    rangeStep [i * 2] = ArgumentStep_P;

    rangeMax  [i * 2 + 1] = f.GetMaxRangeY ();
    rangeMin  [i * 2 + 1] = f.GetMinRangeY ();
    rangeStep [i * 2 + 1] = ArgumentStep_P;
  }

  for (int test = 0; test < NumberRepetTest_P; test++)
  {
    //--------------------------------------------------------------------------
    if (!ao.Init (rangeMin, rangeMax, rangeStep)) break;

    // Optimization-------------------------------------------------------------
    for (int epochCNT = 1; epochCNT <= epochCount && !IsStopped (); epochCNT++)
    {
      ao.Moving ();

      for (int set = 0; set < ArraySize (ao.a); set++)
      {
        ao.a [set].f = f.CalcFunc (ao.a [set].c, funcCount);
      }

      ao.Revision  ();

      if (Video_P)
      {
        //drawing a population--------------------------------------------------
        st.SendGraphToCanvas  ();

        for (int i = 0; i < ArraySize (ao.a); i++)
        {
          st.PointDr (ao.a [i].c, f, 1, 1, funcCount, false);
        }
        st.PointDr (ao.cB, f, 1, 1, funcCount, true);

        st.MaxMinDr (f);

        //drawing a convergence graph---------------------------------------------
        xConv = (int)st.Scale (epochCNT, 1, epochCount, st.H + 2, st.W - 3, false);
        yConv = (int)st.Scale (ao.fB, f.GetMinFunValue (), f.GetMaxFunValue (), 2, st.H - 2, true);
        st.Canvas.FillCircle (xConv, yConv, 1, COLOR2RGB (clrConv));

        st.Update ();
      }
    }

    aveResult += ao.fB;
  }

  aveResult /= (double)NumberRepetTest_P;

  double score = aveResult;

  Print (funcCount, " ", f.GetFuncName (), "'s; Func runs: ", NumbTestFuncRuns_P, "; result: ", aveResult);
  allScore += score;
}
//——————————————————————————————————————————————————————————————————————————————


5. 一般的テスト関数の追加

広く知られ、活発に使用されている最適化アルゴリズムを、なぜ研究開発用のテスト関数に含めなかったのかと聞かれることがあります。その理由は、私の分類によれば、これらはすべて「単純な」テスト関数に分類されるからです。そのサーフェスの半分以上が全域的極値に近い場所に集中しているため、適切なテストをおこなうには「予測可能」すぎるのです。しかし、それにもかかわらず、人々はそれらを信頼することに慣れており、そのような機能でアルゴリズムをテストする傾向があります。私は、Ackley、Goldstein-Price、Shaffer #2をセットに入れることにしました。これにより、ユーザーにとってテスト関数の選択のバランスが取りやすくなり、最適化アルゴリズムのより包括的で信頼性の高いテストが提供され、研究者に新たな地平が開かれ、その効率性のより深い理解に貢献します。

Ackley方程式:

f(x, y) = -(-20 * exp(-0.2 * sqrt(0.5 * (x^2 + y^2))) - exp(0.5 * (cos(2 * pi * x) + cos(2 * pi * y))) + e + 20)

ここで
x、y:関数の入力
e:オイラー数(約2.71828)
π:円周率(約3.14159)

以下が、関数コードです。

double Core (double x, double y)
{
  double res1 = -20.0 * MathExp (-0.2 * MathSqrt (0.5 * (x * x + y * y)));
  double res2 = -MathExp (0.5 * (MathCos (2.0 * M_PI * x) + MathCos (2.0 * M_PI * y)));
  double res3 = -(res1 + res2 + M_E + 20.0);

  return Scale (res3, -14.302667500265278, 0.0, 0.0, 1.0);
}

Goldstein-Price方程式:

f(x, y) = -([1 + (x + y + 1)^2 * (19 - 14x + 3x^2 - 14y + 6xy + 3y^2)] * [30 + (2x - 3y)^2 * (18 - 32x + 12x^2 + 48y - 36xy + 27y^2)])

以下が、関数コードです。

double Core (double x, double y)
{
  double part1 = 1 + MathPow ((x + y + 1), 2) * (19 - 14 * x + 3 * x * x - 14 * y + 6 * x * y + 3 * y * y);
  double part2 = 30 + MathPow ((2 * x - 3 * y), 2) * (18 - 32 * x + 12 * x * x + 48 * y - 36 * x * y + 27 * y * y);

  return Scale (-part1 * part2, -1015690.2717980597, -3.0, 0.0, 1.0);
}

Shaffer #2方程式

f(x, y) = -(0.5 + ((sin(x^2 - y^2)^2 - 0.5) / (1 + 0.001 * (x^2 + y^2))^2))

以下が、関数コードです。

double Core (double x, double y)
{
  double numerator   = MathPow (MathSin (x * x - y * y), 2) - 0.5;
  double denominator = MathPow (1 + 0.001 * (x * x + y * y), 2);
    
  return Scale (-(0.5 + numerator / denominator), -0.9984331449753265, 0, 0, 1.0);
}

注:テスト関数の値は[0.0, 1.0]に正規化されています。

6.3Dテスト関数の構築

時には、数値や方程式から抽象化するだけでなく、テスト関数とその救済をより深く理解するために、視覚的に見ることが必要な場合もあります。そこで、MetaTrader 5プラットフォームでDirectXを使用して3Dシーンを構築する機能を使用することにしました。標準配布されている開発者の「...\MQL5\Experts\Examples\Math 3D Morpher\Math 3D Morpher.mq5」ファイルに触発されて、先に開発した関数(テストスタンドの関数リストに追加予定)を可視化しました。そこで、テスト関数のビジュアライザーを作成することにしました。

そのためには、最適化アルゴリズムのテストに使われるテスト関数のクラスを保存しているFunctions.mqhファイルを拡張する必要がありました。私が追加した関数は、もしそのような必要が生じた場合、関数を修正する際の変化を視覚的に調べることができるだけでなく、その特性や特徴をより深く探求することもできます。

これは私の研究をより楽しく視覚的にするだけでなく、テスト関数の動作をより完全に理解するのに役立ちます。結局のところ、3Dビジュアライゼーションは、スクリーン上の数字を見るだけでなく、関数の形状や構造と直接対話するのに役立ちます。これは、それらを変更したりプロパティを分析するときに重要です。

以下は、3Dシーンのモデルを構築するための追加関数のコードです。

//——————————————————————————————————————————————————————————————————————————————
//GenerateFunctionDataFixedSize
bool GenerateFunctionDataFixedSize (int x_size, int y_size, double &data [], double x_min, double x_max, double y_min, double y_max, C_Function &function)
{
  if (x_size < 2 || y_size < 2)
  {
    PrintFormat ("Error in data sizes: x_size=%d,y_size=%d", x_size, y_size);
    return (false);
  }

  double dx = (x_max - x_min) / (x_size - 1);
  double dy = (y_max - y_min) / (y_size - 1);

  ArrayResize (data, x_size * y_size);

  //---
  for (int j = 0; j < y_size; j++)
  {
    for (int i = 0; i < x_size; i++)
    {
      double x = x_min + i * dx;
      double y = y_min + j * dy;

      data [j * x_size + i] = function.Core (x, y);
    }
  }
  return (true);
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
//GenerateDataFixedSize
bool GenerateDataFixedSize (int x_size, int y_size, C_Function &function, double &data [])
{
  if (x_size < 2 || y_size < 2)
  {
    PrintFormat ("Error in data sizes: x_size=%d,y_size=%d", x_size, y_size);
    return (false);
  }

  return GenerateFunctionDataFixedSize (x_size, y_size, data,
                                        function.GetMinRangeX (),
                                        function.GetMaxRangeX (),
                                        function.GetMinRangeY (),
                                        function.GetMaxRangeY (),
                                        function);
}
//——————————————————————————————————————————————————————————————————————————————

さらに、SkinDiscreteとHillyDiscreteという、すでにおなじみの関数の離散的なバリエーションを追加しました。

Hilly Discrete

HillyDiscrete関数

Skin Discrete

SkinDiscrete関数

Shaffer

Shaffer #2関数

7.まとめ

すでに最適化アルゴリズムの真のテストとして高い評価を得ている「Hilly」「Forest」「Megacity」のテスト関数を引き続き使用し、信頼性の高いランキング表の作成に貢献します。しかし、これらの関数だけに限定すべきではありません。結局のところ、テスト関数のリストはうまく拡張されています。つまり、読者には、実験し、探索し、新たな地平を発見するという選択の自由が与えられています。これが科学的研究の素晴らしさなのです。

基本クラスの継承によるハイブリッド最適化手法の料理に似た探求の結論として、このような出発点を確立することが、研究の無限の可能性への扉を開くことがわかります。さまざまな母集団アルゴリズムを1つのクラスにまとめることで、最適解を求める精度と速度を向上させるだけでなく、研究のための普遍的なツールを作成することができます。

離散、スムーズ、シャープ、非差別など、さまざまなテスト関数を組み合わせた単一のテストベンチを作成することで、さまざまな最適化手法をテストし、比較する新たな機会が生まれます。このアプローチにより、研究者は標準的な機能セットを使用できるだけでなく、特定のタスクや要件に適合した独自のテストベンチを作成することもできます。

このように、母集団アルゴリズムを1つのクラスに統合し、普遍的なテストベンチを作成することで、最適化手法の新しい「美食」の組み合わせを生み出すエキサイティングな道が開かれ、最適解探索の世界で新しい「味のメモ」を発見することができます。この「料理」の実験を続け、最適化の分野における卓越性と革新への道のりで新たな傑作を生み出しましょう。

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

添付されたファイル |
AOs.zip (36.92 KB)
ニューラルネットワークが簡単に(第78回):Transformerを用いたデコーダなしの物体検出器(DFFT) ニューラルネットワークが簡単に(第78回):Transformerを用いたデコーダなしの物体検出器(DFFT)
この記事では、取引戦略の構築という問題を別の角度から見てみようと思います。将来の値動きを予測するのではなく、過去のデータの分析に基づいた取引システムの構築を試みます。
角度ベースの取引 角度ベースの取引
この記事では、角度ベースの取引について取り上げます。角度の組み立て方と、それを取引に利用する方法について見ていきます。
母集団最適化アルゴリズム:極値から抜け出す力(第I部) 母集団最適化アルゴリズム:極値から抜け出す力(第I部)
本稿では、母集団最適化アルゴリズムの挙動を、集団の多様性が低い場合に効率的に極小値を脱出し、最大値に到達する能力という観点から検証することを目的としたユニークな実験を紹介します。この方向性で取り組むことで、ユーザーが設定した座標を出発点として、どの特定のアルゴリズムが検索を成功させることができるのか、またその成功にどのような要因が影響するのかについて、さらなる洞察が得られるでしょう。
MetaTraderのMultibot(第2回):動的テンプレートの改良 MetaTraderのMultibot(第2回):動的テンプレートの改良
前回の記事のテーマを発展させ、より柔軟で機能的なテンプレートを作成することにしました。このテンプレートは、より大きな機能を持ち、フリーランスとして、また外部ソリューションとの統合機能を備えた多通貨多期間EAを開発するためのベースとして効果的に使用することができます。