ニューロボイド最適化アルゴリズム2 (NOA2)
内容
はじめに
長年にわたり最適化アルゴリズムを研究する中で、私は常に2つの並行するインスピレーションに惹かれてきました。1つは生物群の自己組織化、もう1つはニューラルネットワークの適応学習能力です。この2つのパラダイムを統合することで、Craig Reynoldsが提案した、空間的知能を持つボイド(人工生命)と、ニューラルネットワークの適応学習能力を組み合わせたハイブリッド型アルゴリズムを開発するに至りました。
私の研究の出発点は、従来の群アルゴリズムが複雑な探索空間の探索には適している一方、探索履歴から学習する能力に欠けることにあります。一方で、ニューラルネットワークは複雑なパターンの学習には優れていますが、最適化問題に直接適用した場合、空間的パターンの効果的な探索には苦戦しがちです。私の研究を動機付けた問いは一見単純に思えます。群の各エージェントが専用のニューラルネットワークを使って、自らの移動戦略を改善できるとしたらどうなるかということです。
その結果生まれたアルゴリズムでは、各エージェントが従来のボイドルールである凝集、分離、整列に従うことで、自己組織化し、探索空間を効率的に探索できるようになっています。しかし、従来のボイド実装とは異なり、各エージェントは多層ニューラルネットワークを備えており、その経験に基づいて継続的に学習し、探索空間の特性に応じて移動戦略を適応させていきます。このニューラルネットワークによる制御のレベルは、ボイドの行動に徐々に影響を与え、初期段階では探索重視の戦略を支配していたものが、有望な領域が特定されるにつれて活用重視の移動へとシフトしていきます。
開発中に私が最も魅力的だと感じたのは、ニューラルネットワークが探索空間内での位置に応じて異なる戦略を進化させる様子を観察したことです。有望領域の近くにあるエージェントは局所的な活用を強化するニューラルパターンを発達させ、一方で希薄な領域にあるエージェントは探索行動をサポートしました。このような自発的な専門化は、個々の学習プロセスから自然に生じ、明示的な大域的調整がなくても、文脈依存的で多様な行動を持つ群を形成します。
本記事では、NOA2アルゴリズムのアーキテクチャ、実装の詳細、性能分析を紹介するとともに、各種ベンチマーク関数に対するその能力を実証します。
アルゴリズムの実装
前述の通り、ニューロボイドアルゴリズムの主な考え方は、群アルゴリズムの集団知能とニューラルネットワークの適応学習の2つのパラダイムを組み合わせることにあります。
Craig Reynoldsによって提案された従来のボイドアルゴリズムでは、エージェントは、凝集(群れの中心に向かって移動する)、分離(衝突を避ける)、整列(近隣の方向と速度に合わせる)の3つの単純なルールに従います。これらのルールにより、鳥の群れのような現実的な集団行動が生まれます。ニューロボイドはこの概念を拡張し、各エージェントに探索空間での経験から学習する個別のニューラルネットワークを持たせています。このニューラルネットワークは2つの主要な機能を持ちます。
- 適応型運動制御:エージェントの現在の状態や移動履歴に基づいて速度を調整します。
- ボイドの標準ルールの修正:文脈に応じて、凝集、分離、整列ルールの影響を動的に調整します。
その結果、各エージェントは探索空間を効率的に探索するために必要な社会的行動を保持しつつ、学習を通じて適応度関数の特性に個別に適応するハイブリッドアルゴリズムが実現されます。これにより、探索と活用の間に自己調整されたバランスが生まれます。
このアプローチの主な利点は、各エージェントが最適な移動戦略を独立して学習できることです。その結果、アルゴリズムは異なるタイプの最適化ランドスケープに自動的に適応しつつ、集団行動によって探索は維持され、中央制御なしで空間探索を継続することができます。簡単な例えとして、空を飛ぶ鳥の群れを想像してください。彼らは協調して移動し、衝突せず、互いに離れず、同じ方向に飛びます。この行動は、近傍の個体の近くにいる(群れから離れない)、近傍の個体と衝突しない(距離を保つ)、同じ方向に飛ぶ(共通の進路を維持する)という、3つの単純なルールで表現できます。
これがいわゆるボイド(boid、「bird-oid」)アルゴリズムの基礎です。群れの中のそれぞれの鳥はこれらのルールに従うだけでなく、自己の経験からも学習します。どの行動が成功(たとえば、より多くの食物を見つけた)につながったか、どの行動が失敗だったかを記憶します。時間が経つにつれて、鳥はより賢くなり、どのように飛ぶかの意思決定が改善されます。これがニューロボイドアルゴリズムの本質です。つまり、集団移動の単純なルールと、各エージェントが自身の経験から学習する能力を組み合わせることです。名前の「ニューロ」は学習能力を意味します。このアプローチが特に興味深いのは、集団による探索の力(重要な領域を見逃さない)と個々の学習の利点(各自が自分の領域で最適化される)を同時に活用できる点にあります。

図1:NOA2アルゴリズムの動作
図には、以下の主要な要素が示されています。最適化ランドスケープでは、黄色から紫の領域が大域最適解を表し、オレンジ色および紫色の領域は局所最適解を示しています。等高線は、適応度関数の「高さ」を表しています。
エージェントの異なるグループとして、青色は大域最適解周辺を探索するエージェント、紫色は第一の局所最適解付近に集中するエージェント、緑色は第二の局所最適解を探索するエージェント、赤色は空間内をランダムに探索するエージェントです。矢印はボイドの移動方向を示しています。赤い点は、現在までに見つかった最良解を示しています。

図2:NOA2アルゴリズムの動作
図には、初期化ブロック(赤色)、ボイドニューラルネットワーク(ピンク色)、アルゴリズム反復処理のサブブロック(青色)、適応メカニズム(緑色)が含まれています。また、ニューラルネットワークの構造と接続例の可視化も示されています。下部には、ニューラルネットワーク構造のミニ図、ボイド移動ルールの可視化、および探索と活用のバランスを示す図が配置されています。
基本概念を理解したところで、次にアルゴリズムの擬似コードの説明に進むことができます。
初期化
- 探索パラメータを設定し、エージェントの集団を生成します。
- 各エージェントに対してランダムな位置、低速、ニューラルネットワーク(入力層 → 隠れ層 → 出力層)、および空の経験メモリを初期化します。
- 大域最良適応度を負の無限大に設定し、停滞カウンタを0にリセットします。
メインループ
-
各反復処理において、以下を実行します。
適応度評価
- 全エージェントの適応度を計算します。
- 個体および大域の最良位置を更新します。
- 経験をメモリバッファに保存します。
停滞管理
- 改善が見られない場合は探索を強化し、弱いエージェントを時々再初期化します。
- 改善がある場合は探索レベルを徐々に低下させます。
ニューラル処理
- 各エージェントに対して
- 入力(位置、速度、最良位置までの距離、適応度)から隠れ層を経て出力へのフォワードパスをおこないます。
- 十分な経験が蓄積され、改善が検出された場合はニューラルネットワークの重みを更新します。
移動更新
- 各エージェントに対して
- 標準的なボイド力(凝集、分離、整列)を計算します。
- ニューラル修正力および直接的なニューラル補正項を適用します。
- 確率的にランダム試行要素を追加します。
- 速度制限と境界条件を適用します。
- 最終速度に基づき位置を更新します。
補助計算
- 適応度を正規化して質量を計算し、各エージェントに質量を割り当てます。
- 凝集:近傍エージェントの重心に向かって移動します。
- 分離:隣接エージェントとの混雑を回避します。
- 整列:近傍エージェントと速度を調整します。
- ニューラル更新:適応度の向上に基づいて簡易的なバックプロパゲーションをおこないます。
戻り値:大域最良位置および大域最良適応度
これでアルゴリズムコードを作成する準備が整いました。まず、ニューラルネットワーク構造に基づく最適化タスク用のニューロボイドエージェントを表すS_NeuroBoids_Agent構造体を定義します。実装では、エージェントは以下の主要なコンポーネントと機能を持ちます。
座標と速度
- x []:現在のエージェントの座標
- dx []:現在のエージェントの速度
- inputs []:ニューロンへの入力値(座標、速度、既知の最良位置までの距離など)
- outputs []:ニューラルネットワークの出力値(速度補正や適応パラメータ)
- weights []:ニューラルネットワーク内の接続の重み
- biases []:各ニューロンのバイアス
- memory []:以前の座標とその適応度を保存する配列
- memory_size:メモリの最大容量
- memory_index:現在のメモリインデックス
- best_local_fitness:エージェントの局所最良適応度
- m:エージェントの質量
- cB[]:エージェントが見つけた最良位置の座標
- fB:最良位置における適応度値
初期化(Init):すべての配列や変数をゼロまたはランダム値で初期化します。配列サイズを設定し、影響値(重みやバイアス)は小さなランダム値で初期化されます。
活性化関数:Tanh、ReLU、Sigmoidなど、ニューラルネットワークで使用される各種活性化関数です。
入力データの更新(UpdateInputs):入力配列を現在の座標、速度、既知の最良位置までの距離、および現在の適応度値で埋めます。
フォワードパス(ForwardPass):入力、重み、バイアスを用い、活性化関数を適用してニューラルネットワークの出力を計算します。
経験学習(Learn):現在の経験が過去よりも良好であれば、蓄積された経験に基づき重みとバイアスを更新します。
経験の記憶(MemorizeExperience):現在の座標とエージェントの適応度をメモリに保存します。
最良位置の更新(UpdateBestPosition):現在の適応度値が過去の最良値よりも良ければ、最良位置を更新します。
//—————————————————————————————————————————————————————————————————————————————— // Neuro-boid agent structure //—————————————————————————————————————————————————————————————————————————————— struct S_NeuroBoids_Agent { double x []; // current coordinates double dx []; // current speeds double inputs []; // neuron inputs double outputs []; // neuron outputs double weights []; // neuron weights double biases []; // neuron biases double memory []; // memory of previous positions and their fitnesses int memory_size; // memory size for accumulating experience int memory_index; // current index in memory double best_local_fitness; // best local agent fitness double m; // boid mass double cB []; // best position coordinates double fB; // fitness function value // Agent initialization void Init (int coords, int neuron_size) { ArrayResize (x, coords); ArrayResize (dx, coords); ArrayInitialize (x, 0.0); ArrayInitialize (dx, 0.0); // Initialize the best position structure ArrayResize (cB, coords); ArrayInitialize (cB, 0.0); fB = -DBL_MAX; // Inputs: coordinates, speeds, distance to best, etc. int input_size = coords * 2 + 2; // x, dx, dist_to_best, current_fitness ArrayResize (inputs, input_size); ArrayInitialize (inputs, 0.0); // Outputs: Speed correction and adaptive factors for flock rules int output_size = coords + 3; // dx_correction + 3 adaptive parameters ArrayResize (outputs, output_size); ArrayInitialize (outputs, 0.0); // Weights and biases for each output ArrayResize (weights, input_size * output_size); ArrayInitialize (weights, 0.0); ArrayResize (biases, output_size); ArrayInitialize (biases, 0.0); // Initialize weights and biases with small random values for (int i = 0; i < ArraySize (weights); i++) { weights [i] = 0.01 * (MathRand () / 32767.0 * 2.0 - 1.0); } for (int i = 0; i < ArraySize (biases); i++) { biases [i] = 0.01 * (MathRand () / 32767.0 * 2.0 - 1.0); } // Initialize memory for accumulating experience memory_size = 10; ArrayResize (memory, memory_size * (coords + 1)); // coordinates + fitness ArrayInitialize (memory, 0.0); memory_index = 0; best_local_fitness = -DBL_MAX; // Initialize mass m = 0.5; } // Activation function - hyperbolic tangent double Tanh (double input_val) { return MathTanh (input_val); } // ReLU activation function double ReLU (double input_val) { return (input_val > 0.0) ? input_val : 0.0; } // Sigmoid activation function double Sigmoid (double input_val) { return 1.0 / (1.0 + MathExp (-input_val)); } // Updating neural network inputs - Corrected version void UpdateInputs (double &global_best [], double current_fitness, int coords_count) { int input_index = 0; // Coordinates for (int c = 0; c < coords_count; c++) { inputs [input_index++] = x [c]; } // Speeds for (int c = 0; c < coords_count; c++) { inputs [input_index++] = dx [c]; } // Distance to the best global solution double dist_to_best = 0; for (int c = 0; c < coords_count; c++) { dist_to_best += MathPow (x [c] - global_best [c], 2); } dist_to_best = MathSqrt (dist_to_best); inputs [input_index++] = dist_to_best; // Current fitness function inputs [input_index++] = current_fitness; } // Direct distribution over the network void ForwardPass (int coords_count) { int input_size = ArraySize (inputs); int output_size = ArraySize (outputs); // For each output, calculate the weighted sum of the inputs + bias for (int o = 0; o < output_size; o++) { double sum = biases [o]; for (int i = 0; i < input_size; i++) { sum += inputs [i] * weights [o * input_size + i]; } // Apply different activation functions depending on the output if (o < coords_count) // Use the hyperbolic tangent to correct the speed { outputs [o] = Tanh (sum); } else // Use sigmoid for adaptive parameters { outputs [o] = Sigmoid (sum); } } } // Learning from accumulated experience void Learn (double learning_rate, int coords_count) { if (memory_index < memory_size) return; // Insufficient experience // Find the best experience in memory int best_index = 0; double best_fitness = memory [coords_count]; // The first fitness function in memory for (int i = 1; i < memory_size; i++) { double fitness = memory [i * (coords_count + 1) + coords_count]; if (fitness > best_fitness) { best_fitness = fitness; best_index = i; } } // If the current experience is not better than the previous one, do not update the weights if (best_fitness <= best_local_fitness) return; best_local_fitness = best_fitness; // Simple method for updating weights int input_size = ArraySize (inputs); int output_size = ArraySize (outputs); // Simple form of gradient update for (int o = 0; o < output_size; o++) { for (int i = 0; i < input_size; i++) { int weight_index = o * input_size + i; if (weight_index < ArraySize (weights)) { weights [weight_index] += learning_rate * outputs [o] * inputs [i]; } } // Update offsets biases [o] += learning_rate * outputs [o]; } } // Save current experience (coordinates and fitness) void MemorizeExperience (double fitness, int coords_count) { int offset = memory_index * (coords_count + 1); // Save the coordinates for (int c = 0; c < coords_count; c++) { memory [offset + c] = x [c]; } // Save the fitness function memory [offset + coords_count] = fitness; // Update the memory index memory_index = (memory_index + 1) % memory_size; } // Update the agent's best position void UpdateBestPosition (double current_fitness, int coords_count) { if (current_fitness > fB) { fB = current_fitness; ArrayCopy (cB, x, 0, 0, WHOLE_ARRAY); } } };
C_AO_NOA2クラスはNOA2アルゴリズムの実装であり、C_AO基底クラスを継承しています。ここではクラスの構造と主要な要素について詳しく見ていきます。
主なパラメータ- popSize:エージェント(ボイド)の集団サイズ
- cohesionWeight、cohesionDist:凝集ルールの重みと距離
- separationWeight、separationDist:分離ルールの重みと距離
- alignmentWeight、alignmentDist:整列ルールの重みと距離
- maxSpeed、minSpeed:エージェントの最大速度と最小速度
- learningRate:ニューラルネットワークの学習率
- neuralInfluence:ニューラルネットワークがエージェントの動きに与える影響
- exploreRate:解空間のランダム探索の確率
- m_stagnationCounter、m_prevBestFitness:停滞カウンタと直前の最良適応度値
- SetParams ():「params」配列に保存された値に基づきアルゴリズムパラメータを設定します
- Init ():初期化。エージェントの値の範囲を定義するパラメータを受け取り、アルゴリズムの初期条件を設定します。
- Moving ():各種相互作用ルールに基づくエージェントの移動を担当します。
- Revision ():アルゴリズム実行中にエージェントの状態を修正するために使用されます。
エージェント:S_NeuroBoids_Agent agent []:集団内のボイドを表すエージェントの配列
privateメソッド(クラス内で使用)
- CalculateMass ():エージェントの質量
- Cohesion ():凝集ルール
- Separation ():分離ルール
- Alignment ():整列ルール
- LimitSpeed ():エージェントの速度を制限します
- KeepWithinBounds ():エージェントを指定された境界内に保持します。
- Distance ():2つのエージェント間の距離を計算します。
- ApplyNeuralControl ():エージェントにニューラルネットワークによる制御を適用します。
- AdaptiveExploration():適応型探索を実装します。
//—————————————————————————————————————————————————————————————————————————————— // Class of neuron-like optimization algorithm inherited from C_AO //—————————————————————————————————————————————————————————————————————————————— class C_AO_NOA2 : public C_AO { public: //-------------------------------------------------------------------- ~C_AO_NOA2 () { } C_AO_NOA2 () { ao_name = "NOA2"; ao_desc = "Neuroboids Optimization Algorithm 2 (joo)"; ao_link = "https://www.mql5.com/ja/articles/17497"; popSize = 50; // population size cohesionWeight = 0.6; // cohesion weight cohesionDist = 0.001; // cohesion distance separationWeight = 0.005; // separation weight separationDist = 0.03; // separation distance alignmentWeight = 0.1; // alignment weight alignmentDist = 0.1; // alignment distance maxSpeed = 0.001; // maximum speed minSpeed = 0.0001; // minimum speed learningRate = 0.01; // neural network learning speed neuralInfluence = 0.3; // influence of the neural network on movement explorationRate = 0.1; // random exploration probability ArrayResize (params, 12); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "cohesionWeight"; params [1].val = cohesionWeight; params [2].name = "cohesionDist"; params [2].val = cohesionDist; params [3].name = "separationWeight"; params [3].val = separationWeight; params [4].name = "separationDist"; params [4].val = separationDist; params [5].name = "alignmentWeight"; params [5].val = alignmentWeight; params [6].name = "alignmentDist"; params [6].val = alignmentDist; params [7].name = "maxSpeed"; params [7].val = maxSpeed; params [8].name = "minSpeed"; params [8].val = minSpeed; params [9].name = "learningRate"; params [9].val = learningRate; params [10].name = "neuralInfluence"; params [10].val = neuralInfluence; params [11].name = "explorationRate"; params [11].val = explorationRate; // Initialize the stagnation counter and the previous best fitness value m_stagnationCounter = 0; m_prevBestFitness = -DBL_MAX; } void SetParams () { popSize = (int)params [0].val; cohesionWeight = params [1].val; cohesionDist = params [2].val; separationWeight = params [3].val; separationDist = params [4].val; alignmentWeight = params [5].val; alignmentDist = params [6].val; maxSpeed = params [7].val; minSpeed = params [8].val; learningRate = params [9].val; neuralInfluence = params [10].val; explorationRate = params [11].val; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0); void Moving (); void Revision (); //---------------------------------------------------------------------------- double cohesionWeight; // cohesion weight double cohesionDist; // cohesion distance double separationWeight; // separation weight double separationDist; // separation distance double alignmentWeight; // alignment weight double alignmentDist; // alignment distance double minSpeed; // minimum speed double maxSpeed; // maximum speed double learningRate; // neural network learning speed double neuralInfluence; // influence of the neural network on movement double explorationRate; // random exploration probability int m_stagnationCounter; // stagnation counter double m_prevBestFitness; // previous best fitness value S_NeuroBoids_Agent agent []; // agents (boids) private: //------------------------------------------------------------------- double distanceMax; // maximum distance double speedMax []; // maximum speeds by measurements int neuron_size; // neuron size (number of inputs) void CalculateMass (); // calculation of agent masses void Cohesion (S_NeuroBoids_Agent &boid, int pos); // cohesion rule void Separation (S_NeuroBoids_Agent &boid, int pos); // separation rule void Alignment (S_NeuroBoids_Agent &boid, int pos); // alignment rule void LimitSpeed (S_NeuroBoids_Agent &boid); // speed limit void KeepWithinBounds (S_NeuroBoids_Agent &boid); // keep within bounds double Distance (S_NeuroBoids_Agent &boid1, S_NeuroBoids_Agent &boid2); // calculate distance void ApplyNeuralControl (S_NeuroBoids_Agent &boid, int pos); // apply neural control void AdaptiveExploration (); // adaptive research }; //——————————————————————————————————————————————————————————————————————————————
Initメソッドは、アルゴリズムおよびエージェントのパラメータを初期化する役割を持ちます。処理はStandardInitを呼び出すことから始まり、範囲の最小値、最大値と探索ステップの配列を渡します。標準の初期化が失敗した場合、メソッドは直ちにfalseを返します。エージェントのニューラルネットワークで使用されるニューロン数もここで設定されます。
この場合、サイズは、座標数の2倍にdist_to_bestとcurrent_fitnessを加えた値として計算されます。エージェント配列のサイズは指定されたpopSizeに従って変更されます。その後、各エージェントに対してInitメソッドが呼ばれ、ニューラルネットワークを含む各種パラメータが初期化されます。
最大距離および速度の計算
- distanceMax変数は0に初期化されます。
- 各座標について、範囲の最大値と最小値の差に基づいて最大速度が計算されます。
- 最大距離は各座標の最大速度の二乗和の平方根として計算されます。
停滞カウンタ(m_stagnationCounter)は0にリセットされ、前回の最良適応度値を保持する変数(m_prevBestFitness)は取り得る最小値に設定されます。メソッドは、凝集、分離、整列の重み、最大および最小速度、学習率、ニューラルネットワークの影響度、探索確率など、さまざまなグローバル変数を設定します。すべての手順が正常に完了した場合、メソッドはtrueを返し、初期化が成功したことを示します。
//—————————————————————————————————————————————————————————————————————————————— bool C_AO_NOA2::Init (const double &rangeMinP [], // minimum search range const double &rangeMaxP [], // maximum search range const double &rangeStepP [], // search step const int epochsP) // number of epochs { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //---------------------------------------------------------------------------- // Determine the size of a neuron neuron_size = coords * 2 + 2; // x, dx, dist_to_best, current_fitness // Initialize the agents ArrayResize (agent, popSize); for (int i = 0; i < popSize; i++) { agent [i].Init (coords, neuron_size); } distanceMax = 0; ArrayResize (speedMax, coords); for (int c = 0; c < coords; c++) { speedMax [c] = rangeMax [c] - rangeMin [c]; distanceMax += MathPow (speedMax [c], 2); } distanceMax = MathSqrt (distanceMax); // Reset the stagnation counter and the previous best fitness value m_stagnationCounter = 0; m_prevBestFitness = -DBL_MAX; GlobalVariableSet ("#reset", 1.0); GlobalVariableSet ("1cohesionWeight", params [1].val); GlobalVariableSet ("2cohesionDist", params [2].val); GlobalVariableSet ("3separationWeight", params [3].val); GlobalVariableSet ("4separationDist", params [4].val); GlobalVariableSet ("5alignmentWeight", params [5].val); GlobalVariableSet ("6alignmentDist", params [6].val); GlobalVariableSet ("7maxSpeed", params [7].val); GlobalVariableSet ("8minSpeed", params [8].val); GlobalVariableSet ("9learningRate", params [9].val); GlobalVariableSet ("10neuralInfluence", params [10].val); GlobalVariableSet ("11explorationRate", params [11].val); return true; } //——————————————————————————————————————————————————————————————————————————————
Movingメソッドは、受け取ったデータとエージェント間の相互作用に基づき、環境内でのエージェントの移動を担当します。まず、#resetグローバル変数の値を確認します。値が1.0の場合はパラメータがリセットされ、revision変数がfalseに設定され、その後#resetは0.0に戻ります。凝集、分離、整列の重み、最小および最大速度、学習率、ニューラルネットワークの影響度、探索比率などの各種パラメータはグローバル変数から取得されます。これらのパラメータは、エージェントの挙動を変更するために設定可能です。
revision変数がfalseの場合、エージェントの座標xと速度dxが初期化されます。各座標には指定範囲内のランダム値が適用され、速度は小さなランダム値に設定されます。また、各座標には現在の位置に基づいたエージェントの状態が格納されます。その後、AdaptiveExploration()メソッドが呼び出され、停滞状態に応じてエージェントの探索行動を適応的に変更します。
エージェント移動のメインループ(エージェントごと)
- エージェントの現在の経験を保存します。
- エージェントが見つけた最良位置を更新します。
- エージェントのニューラルネットワークの入力データを更新します。
- ニューラルネットワークを通してデータを直接伝播させます。
- 蓄積された経験に基づきエージェントを学習させます。
ルール適用と移動:エージェントを順にループ処理し、ボイドアルゴリズムの標準ルールを各エージェントに適用します。
- 凝集:エージェント同士が互いに近づこうとします。
- 分離:衝突を避けるためにエージェント同士が近づきすぎないようにします
- 整列:エージェントは近隣のエージェントと同じ方向に移動しようとします。
//—————————————————————————————————————————————————————————————————————————————— void C_AO_NOA2::Moving () { double reset = GlobalVariableGet ("#reset"); if (reset == 1.0) { revision = false; GlobalVariableSet ("#reset", 0.0); } // Get parameters from global variables for interactive configuration cohesionWeight = GlobalVariableGet ("1cohesionWeight"); cohesionDist = GlobalVariableGet ("2cohesionDist"); separationWeight = GlobalVariableGet ("3separationWeight"); separationDist = GlobalVariableGet ("4separationDist"); alignmentWeight = GlobalVariableGet ("5alignmentWeight"); alignmentDist = GlobalVariableGet ("6alignmentDist"); maxSpeed = GlobalVariableGet ("7maxSpeed"); minSpeed = GlobalVariableGet ("8minSpeed"); learningRate = GlobalVariableGet ("9learningRate"); neuralInfluence = GlobalVariableGet ("10neuralInfluence"); explorationRate = GlobalVariableGet ("11explorationRate"); // Initialization of initial positions and speeds if (!revision) { for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { agent [i].x [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); agent [i].dx [c] = (rangeMax [c] - rangeMin [c]) * u.RNDfromCI (-1.0, 1.0) * 0.001; a [i].c [c] = u.SeInDiSp (agent [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; return; } // Adaptive research depending on stagnation AdaptiveExploration (); //---------------------------------------------------------------------------- // Main loop of boid movement for (int i = 0; i < popSize; i++) { // Save the current experience agent [i].MemorizeExperience (a [i].f, coords); // Update the agent's best position agent [i].UpdateBestPosition (a [i].f, coords); // Update the neural network inputs agent [i].UpdateInputs (cB, a [i].f, coords); // Forward propagation through the neural network agent [i].ForwardPass (coords); // Learning from accumulated experience agent [i].Learn (learningRate, coords); } // Calculate masses CalculateMass (); // Application of rules and movement for (int i = 0; i < popSize; i++) { // Standard rules of the boid algorithm Cohesion (agent [i], i); Separation (agent [i], i); Alignment (agent [i], i); // Apply neural control ApplyNeuralControl (agent [i], i); // Speed limit and keeping within bounds LimitSpeed (agent [i]); KeepWithinBounds (agent [i]); // Update positions for (int c = 0; c < coords; c++) { agent [i].x [c] += agent [i].dx [c]; a [i].c [c] = u.SeInDiSp (agent [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } } //——————————————————————————————————————————————————————————————————————————————
CalculateMassメソッドは、エージェントの適応度(生産性)に基づいて「質量」を計算し、これらの値を正規化します。maxMassとminMass変数はそれぞれ-DBL_MAXとDBL_MAXで初期化され、集団内のエージェントの中で最大および最小の適応度を見つけるために使用されます。メソッドはまず、少なくとも1つのエージェントが存在するかどうかを確認するため、popSize(集団サイズ)が0より大きいかをチェックします。エージェントが存在しない場合、メソッドは終了します。最初のループでは、すべてのエージェント(インデックス0からpopSizeまで)を順に処理し、それぞれの適応度(a[i].fの値)が現在のmaxMassより大きいか、またはminMassより小さいかを確認します。
このループの結果、全エージェントの中で最大および最小の適応度値が決定されます。次のループでは、再びすべてのエージェントを順に処理し、それぞれの「質量」(m変数)をu.Scale関数を用いて計算します。この関数により、適応度値が正規化されます。
//—————————————————————————————————————————————————————————————————————————————— void C_AO_NOA2::CalculateMass () { double maxMass = -DBL_MAX; double minMass = DBL_MAX; // Check for data presence before calculations if (popSize <= 0) return; for (int i = 0; i < popSize; i++) { if (a [i].f > maxMass) maxMass = a [i].f; if (a [i].f < minMass) minMass = a [i].f; } for (int i = 0; i < popSize; i++) { agent [i].m = u.Scale (a [i].f, minMass, maxMass, 0.1, 1.0); } } //——————————————————————————————————————————————————————————————————————————————
ApplyNeuralControlメソッドは、ニューラルネットワークの出力に基づいて「ボイド型」エージェントの制御を適用する役割を持ちます。メソッドは、エージェントのcoordsのすべての座標に対して順に処理をおこないます。各座標cについて、現在のインデックスがboid.outputs出力配列の範囲を超えていないかを確認します。インデックスが正しい場合、エージェントの速度dx[c]は、ニューラルネットワーク出力の対応する値にneuralInfluenceを掛けた値で補正されます。
後続の処理を簡略化するため、boid.outputs配列のサイズが取得されます。凝集係数(cohesion_factor)、分離係数(separation_factor)、整列係数(alignment_factor)について、対応するインデックスが出力配列の範囲内かをチェックします。インデックスが範囲外の場合は、デフォルト値として0.5が割り当てられます。これらの比率は、ニューラルネットワークの影響を反映させるために基準重みをスケーリングする際に使用され、凝集、分離、整列のエージェントの挙動に影響を与えます。
- local_cohesion:凝集重みを設定します。
- local_separation:分離重みを設定します。
- local_alignment:整列重みを設定します。
さらに、与えられたexplorationRate確率に基づき、ランダム探索をおこなうかどうかを判定します。条件が満たされる場合、摂動を加える座標cがランダムに選ばれます。摂動量perturbation_sizeは、エージェントの質量(適応度の指標)とその座標の可能な値の範囲に基づいて計算されます。これにより、エージェントの質量に依存せずランダム変動の大きさを制御できます。perturbation_sizeからperturbation_sizeの範囲から選ばれたランダム摂動がdx[c]に加算されます。ApplyNeuralControlメソッドは、ニューラルネットワークの出力結果をエージェントの移動管理に統合し、速度を調整すると同時に、ランダム探索の必要性も考慮します。
//—————————————————————————————————————————————————————————————————————————————— // Apply neural control to a boid void C_AO_NOA2::ApplyNeuralControl (S_NeuroBoids_Agent &boid, int pos) { // Use the neural network outputs to correct the speed for (int c = 0; c < coords; c++) { // Make sure the index is not outside the array bounds if (c < ArraySize (boid.outputs)) { // Apply neural speed correction with a given influence boid.dx [c] += boid.outputs [c] * neuralInfluence; } } // Use the neural network outputs to adapt the flock parameters // Check that the indices do not go beyond the array bounds int output_size = ArraySize (boid.outputs); double cohesion_factor = (coords < output_size) ? boid.outputs [coords] : 0.5; double separation_factor = (coords + 1 < output_size) ? boid.outputs [coords + 1] : 0.5; double alignment_factor = (coords + 2 < output_size) ? boid.outputs [coords + 2] : 0.5; // Scale the base weights considering neural adaptation // These variables are local and do not change global parameters double local_cohesion = cohesionWeight * (0.5 + cohesion_factor); double local_separation = separationWeight * (0.5 + separation_factor); double local_alignment = alignmentWeight * (0.5 + alignment_factor); // Random study with a given probability if (u.RNDprobab () < explorationRate) { // Select a random coordinate for perturbation int c = (int)u.RNDfromCI (0, coords - 1); // Adaptive perturbation size depending on mass (fitness) double perturbation_size = (1.0 - boid.m) * (rangeMax [c] - rangeMin [c]) * 0.01; // Add random perturbation boid.dx [c] += u.RNDfromCI (-perturbation_size, perturbation_size); } } //——————————————————————————————————————————————————————————————————————————————
C_AO_NOA2クラスのAdaptiveExplorationメソッドは、最適化プロセスにおける停滞段階に応じて適応型探索を実装します。まず、fB目的関数値が、以前の最良値であるm_prevBestFitnessと比較して変化しているかを確認します。両者の差が0.000001未満であれば進展がないとみなし、m_stagnationCounterを増加させます。そうでない場合は停滞が終了したとみなし、カウンタをリセットし、現在のfBの値を新しい最良値として保存します。
停滞の回数が20を超えた場合、ランダム探索の確率(explorationRate)が増加しますが、ランダム変化の確率が高くなりすぎないよう上限は0.5に制限されます。また、50回の停滞イテレーションごとに部分的な再初期化がおこなわれます。この際、集団の70%のエージェントは、指定されたrangeMinおよびrangeMaxの範囲内で新しいランダム値により再初期化されます。一方、残りの上位エージェントは現在の位置のまま保持されます。再初期化後、停滞カウンタはリセットされます。
進展があった場合、かつparams配列のパラメータ数が11より多ければ、探索確率explorationRateはパラメータ配列から設定されます。パラメータ数が少ない場合は、デフォルト値として0.1が設定されます。
//—————————————————————————————————————————————————————————————————————————————— // Adaptive research depending on stagnation void C_AO_NOA2::AdaptiveExploration () { // Determine if there is progress in the search if (MathAbs (fB - m_prevBestFitness) < 0.000001) { m_stagnationCounter++; } else { m_stagnationCounter = 0; m_prevBestFitness = fB; } // Increase research during stagnation if (m_stagnationCounter > 20) { // Increase the probability of random exploration explorationRate = MathMin (0.5, explorationRate * 1.5); // Perform a partial restart every 50 stagnation iterations if (m_stagnationCounter % 50 == 0) { // Restart 70% of the population leaving the best agents int restart_count = (int)(popSize * 0.7); for (int i = popSize - restart_count; i < popSize; i++) { for (int c = 0; c < coords; c++) { agent [i].x [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); agent [i].dx [c] = (rangeMax [c] - rangeMin [c]) * u.RNDfromCI (-1.0, 1.0) * 0.001; } } // Reset the stagnation counter m_stagnationCounter = 0; } } else { // If progress is good enough, use the normal research level if (11 < ArraySize (params)) { explorationRate = params [11].val; } else { explorationRate = 0.1; // default value } } } //——————————————————————————————————————————————————————————————————————————————
C_AO_NOA2クラスのCohesionメソッドは、ボイドのエージェントベースモデルにおける「凝集」の挙動を実装する役割を持ちます。以下が変数の宣言です。
- centerX:近傍エージェントの重心座標を格納する配列で、サイズはcoordsの数に対応します。
- numNeighbors:近傍エージェントの数をカウントする変数で、指定距離内にあるエージェントを追跡します(質量を考慮)。
- sumMass:近傍エージェントの質量の合計で、重心座標を正規化するために使用されます。
最初のループ(0からpopSize)では、現在のボイド(posインデックスのエージェント)を除く集団内のすべてのエージェントの質量を合計します。次のループでは、すべてのエージェントを順に処理し、現在のボイドからの距離がcohesionDist係数を掛けた最大距離以内かどうかを確認します。エージェントまでの距離が指定距離より近い場合、そのエージェントの座標(質量を考慮)をcenterXに加算し、numNeighborsを増加させます。
近傍エージェントの座標合計と質量が計算された後、近傍エージェントが存在するか(numNeighbors > 0)および質量の合計が正か(sumMass > 0.0)を確認します。条件を満たす場合、重心座標は質量の合計で割ることにより正規化されます。その後、現在のボイドの速度dxは、与えられたcohesionWeightに基づき重心の方向に加算されます。つまり、ボイドは近傍エージェントの重心に向かって移動するようになります。
Cohesionメソッドは、重心を計算し、ボイドの速度を調整することで「凝集」する挙動を実現します。この挙動は群れや集団のシミュレーションにおいて重要な要素です。cohesionDistパラメータやcohesionWeightパラメータにより、この挙動が影響を与える距離や、重心がボイドの移動に与える影響の度合いを調整することが可能です。
//—————————————————————————————————————————————————————————————————————————————— // Find the center of mass of other boids and adjust the speed towards the center of mass void C_AO_NOA2::Cohesion (S_NeuroBoids_Agent &boid, int pos) { double centerX []; ArrayResize (centerX, coords); ArrayInitialize (centerX, 0.0); int numNeighbors = 0; double sumMass = 0; for (int i = 0; i < popSize; i++) { if (pos != i) sumMass += agent [i].m; } for (int i = 0; i < popSize; i++) { if (pos != i) { if (Distance (boid, agent [i]) < distanceMax * cohesionDist) { for (int c = 0; c < coords; c++) { centerX [c] += agent [i].x [c] * agent [i].m; } numNeighbors++; } } } if (numNeighbors > 0 && sumMass > 0.0) { for (int c = 0; c < coords; c++) { centerX [c] /= sumMass; boid.dx [c] += (centerX [c] - boid.x [c]) * cohesionWeight; } } } //——————————————————————————————————————————————————————————————————————————————
C_AO_NOA2クラスのSeparationメソッドは、モデル内のエージェントに対して「分離」の挙動を実装します。この挙動は、ボイドと近傍エージェントとの衝突を防ぐことを目的としています。moveXパラメータ(cohesionDist配列と同様の構造)は、ボイドを他のエージェントから押し離すための変位ベクトルを格納するために使用されます。
moveX配列のサイズはcoordsの数に対応し、ランダム値の累積を避けるため最初に0で初期化されます。その後、集団内のすべてのエージェント(インデックス0からpopSize)を順に処理します。各エージェントについて、現在のボイドに近いかどうかをDistance関数を用いて確認します。ボイドとエージェントの距離が指定された最大距離(separationDist係数を掛けた値)より小さい場合、各座標についてエージェントの座標を現在のボイドの座標から減算します。これにより、現在のボイドの方向に向かうベクトル、すなわちエージェントから押し返すベクトルが生成されます。
すべてのエージェントに対するループが終了した後、ボイドの現在の速度dxは、計算されたmoveX変位ベクトルにseparationWeightを掛けて加算されます。これにより、反発力の強さを制御できます。separationWeightの値が大きいほど、ボイドは衝突をより強く回避します。
Separationメソッドは、ボイドが最も近い隣接エージェントから反発する挙動を実現し、衝突を防ぎます。この挙動は群れのシミュレーションにおいて重要であり、各エージェントのパーソナルスペースを維持し、より自然な相互作用を促進します。separationDistやseparationWeightのパラメータにより、反発の半径や強さを柔軟に調整することが可能です。
//—————————————————————————————————————————————————————————————————————————————— // Pushing away from other boids to avoid collisions void C_AO_NOA2::Separation (S_NeuroBoids_Agent &boid, int pos) { double moveX []; ArrayResize (moveX, coords); ArrayInitialize (moveX, 0.0); for (int i = 0; i < popSize; i++) { if (pos != i) { if (Distance (boid, agent [i]) < distanceMax * separationDist) { for (int c = 0; c < coords; c++) { moveX [c] += boid.x [c] - agent [i].x [c]; } } } } for (int c = 0; c < coords; c++) { boid.dx [c] += moveX [c] * separationWeight; } } //——————————————————————————————————————————————————————————————————————————————
C_AO_NOA2クラスのAlignmentメソッドは、モデル内のエージェントに対して「整列」の挙動を実装します。この挙動により、ボイドは近隣エージェントの速度と調和して動くことで、より統一された集団移動を実現します。avgDXは近隣エージェントの平均速度ベクトルを格納するための配列で、サイズはcoordsの数に対応します。numNeighborsは、指定距離内にいる近隣エージェントの数を追跡するカウンタです。
ループでは、集団内のすべてのエージェントを順に処理します。各ループ内で、対象のエージェントが現在のボイド自身でないかを確認します。他のエージェントまでの距離が最大距離にalignmentDist係数を掛けた値より小さい場合、そのエージェントの現在の速度dxをavgDXに加算し、numNeighborsを1増加させます。ループ処理が完了した後、近隣エージェントが存在する場合(numNeighbors > 0)、合計された速度ベクトルを近隣エージェントの数で割り、平均速度avgDXを計算します。その後、ボイドの現在の速度dxは、alignmentWeightに基づき近隣エージェントの平均速度方向に調整されます。
Alignmentメソッドにより、ボイドは自身の速度を近隣エージェントの速度に適応させることができます。この挙動により、ボイドの集団はより結束して移動し、衝突や急激な方向変更の発生を抑えることが可能です。alignmentDistおよびalignmentWeightのパラメータは、整列効果の半径や、近隣エージェントの速度の平均値が現在のボイドの速度に与える影響度を設定するために使用されます。//—————————————————————————————————————————————————————————————————————————————— // Align speed with other boids void C_AO_NOA2::Alignment (S_NeuroBoids_Agent &boid, int pos) { double avgDX []; ArrayResize (avgDX, coords); ArrayInitialize (avgDX, 0.0); int numNeighbors = 0; for (int i = 0; i < popSize; i++) { if (pos != i) { if (Distance (boid, agent [i]) < distanceMax * alignmentDist) { for (int c = 0; c < coords; c++) { avgDX [c] += agent [i].dx [c]; } numNeighbors++; } } } if (numNeighbors > 0) { for (int c = 0; c < coords; c++) { avgDX [c] /= numNeighbors; boid.dx [c] += (avgDX [c] - boid.dx [c]) * alignmentWeight; } } } //——————————————————————————————————————————————————————————————————————————————
C_AO_NOA2クラスのLimitSpeedメソッドは、モデル内のエージェント(ボイド)の速度を制御し、速度が指定範囲内に収まるようにするために設計されています。speedはボイドの現在の速度を格納する変数であり、速度ベクトルの長さとして計算されます。座標ループ(coords)では、各座標に対して速度の二乗(boid.dx[c] * boid.dx[c])を計算し、それらを合計します。これにより、速度ベクトルの長さの二乗が得られます。その後、MathSqrt関数を用いて平方根を計算し、長さ(実速度)を求めます。速度が十分に大きい場合(1e-10より大きい場合)は処理を続行します。現在の速度が許容される最小速度(speedMax[0] * minSpeed)より小さい場合、速度ベクトルを正規化(現在の長さで割る)し、最小速度に調整します。
逆に、現在の速度が最大許容値(speedMax[0] * maxSpeed)より大きい場合は、同様に速度ベクトルを正規化して最大の速度に設定します。もし速度がほぼゼロの場合(ゼロまたは非常に近い場合)、速度ベクトルの各座標はu.RNDfromCI(-1.0, 1.0)関数で生成された小さなランダム値に最大速度を掛けた値に置き換えられます。
LimitSpeedメソッドにより、ボイドの速度は適切な範囲内に保たれ、移動が遅すぎたり速すぎたりすることを防ぎます。この挙動により、速度の大きな変動による不自然な動きを避けることができ、より現実的なエージェントシミュレーションが可能になります。minSpeedおよびmaxSpeedのパラメータを設定することで、シミュレーションの目的に応じてエージェントの速度や挙動を調整できます。
//—————————————————————————————————————————————————————————————————————————————— // Speed limit void C_AO_NOA2::LimitSpeed (S_NeuroBoids_Agent &boid) { double speed = 0; for (int c = 0; c < coords; c++) { speed += boid.dx [c] * boid.dx [c]; } speed = MathSqrt (speed); // If the speed is not zero (prevent division by zero) if (speed > 1e-10) { // If the speed is too low, increase it if (speed < speedMax [0] * minSpeed) { for (int c = 0; c < coords; c++) { boid.dx [c] = boid.dx [c] / speed * speedMax [c] * minSpeed; } } // If the speed is too high, reduce it else if (speed > speedMax [0] * maxSpeed) { for (int c = 0; c < coords; c++) { boid.dx [c] = boid.dx [c] / speed * speedMax [c] * maxSpeed; } } } else { // If the speed is almost zero, set a small random speed for (int c = 0; c < coords; c++) { boid.dx [c] = u.RNDfromCI (-1.0, 1.0) * speedMax [c] * minSpeed; } } } //——————————————————————————————————————————————————————————————————————————————
C_AO_NOA2クラスのKeepWithinBoundsメソッドは、エージェント(ボイド)を指定された境界内に保持するために設計されています。ボイドが領域の端に近づきすぎた場合、このメソッドは進行方向を変更し、境界内に押し戻す処理をおこないます。メソッドはまず、すべての座標に対してループをおこない、多次元空間に対応できるようにします。
各座標について、ボイドの位置(boid.x[c])が最小境界(rangeMin[c])より小さいかどうかを確認します。小さい場合は、速度の方向(boid.dx[c])を反転させるために「boid.dx[c] *= -1.0」とします。これにより、ボイドは逆方向に移動します。その後、境界から少し押し戻す値として「(rangeMax[c] - rangeMin[c]) * 0.001」を加え、ボイドを領域内に戻す補正をおこないます。
最大境界(rangeMax[c])についても同様のチェックをおこないます。ボイドの位置が最大値を超えた場合、速度を反転させ、前述と同様の値を減算して調整します。
KeepWithinBoundsメソッドにより、ボイドの移動は指定された領域内に制限され、領域外に飛び出すことを防ぎ、常に境界内に留まるように制御されます。
//—————————————————————————————————————————————————————————————————————————————— // Keep the boid within the boundaries. If it gets too close to the edge, // push it back and change direction. void C_AO_NOA2::KeepWithinBounds (S_NeuroBoids_Agent &boid) { for (int c = 0; c < coords; c++) { if (boid.x [c] < rangeMin [c]) { boid.dx [c] *= -1.0; // Add a small push from the border boid.dx [c] += (rangeMax [c] - rangeMin [c]) * 0.001; } if (boid.x [c] > rangeMax [c]) { boid.dx [c] *= -1.0; // Add a small push from the border boid.dx [c] -= (rangeMax [c] - rangeMin [c]) * 0.001; } } } //——————————————————————————————————————————————————————————————————————————————
C_AO_NOA2クラスのDistanceメソッドは、2つのエージェント(ボイド)間の距離を多次元空間で計算するために設計されています。ユークリッド距離を計算する式を用いており、distは2つのボイドの座標差の二乗和を格納する変数です。
メソッドはすべての座標に対してループ処理をおこない、任意の次元空間で距離を計算できるようにしています。各座標cについて、対応するボイドの座標差(boid1.x[c]-boid2.x[c])の二乗を計算します。
計算結果((boid1.x[c]-boid2.x[c])^2)はdist変数に加算されます。ループ処理が終了すると、distには座標差の二乗和が格納されます。実際の距離を求めるため、メソッドはMathSqrtを用いて平方根を計算し、ユークリッド距離を求めます。
//—————————————————————————————————————————————————————————————————————————————— // Calculate the distance between two boids double C_AO_NOA2::Distance (S_NeuroBoids_Agent &boid1, S_NeuroBoids_Agent &boid2) { double dist = 0; for (int c = 0; c < coords; c++) { dist += MathPow (boid1.x [c] - boid2.x [c], 2); } return MathSqrt (dist); } //——————————————————————————————————————————————————————————————————————————————
C_AO_NOA2クラスのRevisionメソッドは、最適化中に見つかった最良解に関する情報を更新する役割を持ちます。この処理では、適応度関数の値とその値に対応する座標の更新がおこなわれます。さらに、進捗を監視し、顕著な改善がある場合にはアルゴリズムパラメータを適応させます。メソッドはa配列で表される集団全体を処理し、popSize(エージェントの数)を対象とします。
ループ内では、各エージェントの適応度値(a[i].f)が現在の最良値(fB)より大きいかどうかを確認します。現在のエージェントが最良の適応度を示す場合、適応度関数の大域最良値が更新され、fBにa[i].fの新しい値が割り当てられます。その後、最良解の座標も更新されます。各座標cについて、最良解の座標を格納するcB配列は、現在のエージェントの値で更新されます。解の改善が見つかったため、停滞カウンタ(m_stagnationCounter)は0にリセットされます。
メソッドはhasProgress変数を用いて進捗の有無を判定します。これは、適応度関数の現在の最良値と前回の最良値の絶対差(MathAbs(fB-m_prevBestFitness))を計算し、この差が0.000001より大きい場合に進捗ありと見なすものです。進捗があった場合、m_prevBestFitnessは現在の最良値fBで更新されます。
また、探索速度であるexplorationRateも適応されます。改善が見つかった場合は減少させ、params配列の値や現在のexplorationRateを考慮して調整されます。
//—————————————————————————————————————————————————————————————————————————————— // Update the best solution found void C_AO_NOA2::Revision () { // Update the best coordinates and fitness function value for (int i = 0; i < popSize; i++) { // Update the global best solution if (a [i].f > fB) { fB = a [i].f; for (int c = 0; c < coords; c++) { cB [c] = a [i].c [c]; } // Reset the stagnation counter when a better solution is found m_stagnationCounter = 0; } } // Check for progress to adapt the algorithm parameters bool hasProgress = MathAbs (fB - m_prevBestFitness) > 0.000001; if (hasProgress) { m_prevBestFitness = fB; explorationRate = MathMax (params [11].val * 0.5, explorationRate * 0.9); } } //——————————————————————————————————————————————————————————————————————————————
テスト結果
テスト結果はかなり弱いです。NOA2|Neuroboids Optimization Algorithm 2 (joo)|50.0|0.6|0.001|0.005|0.03|0.1|0.1|0.1|0.01|0.01|0.3|0.1|
=============================
5 Hilly's; Func runs:10000; result:0.47680799582735267
25 Hilly's; Func runs:10000; result:0.30763714006051013
500 Hilly's; Func runs:10000; result:0.2544737238936433
=============================
5 Forest's; Func runs:10000; result:0.3238017030688524
25 Forest's; Func runs:10000; result:0.20976876473929068
500 Forest's; Func runs:10000; result:0.15740101965732595
=============================
5 Megacity's; Func runs:10000; result:0.27076923076923076
25 Megacity's; Func runs:10000; result:0.14676923076923082
500 Megacity's; Func runs:10000; result:0.09729230769230844
=============================
All score:2.24472 (24.94%)
可視化では控えめな探索能力が示されています。グローバル変数を用いてアルゴリズムの外部パラメータを変更できるため、ボイドの挙動を実験的に観察でき、興味深い行動パターンを明らかにすることが可能です。以下に、数ある挙動パターンの一部の可視化例を示します。

Hillyテスト関数のNOA2

Forestテスト関数のNOA2

Megacityテスト機能のNOA2
テスト結果に基づき、基本バージョンのNOA2アルゴリズムは、参考情報として我々の集団最適化アルゴリズムのランキング表に含まれています。
| # | AO | 詳細 | Hilly | Hilly最終 | Forest | Forest最終 | Megacity(離散) | Megacity最終 | 最終結果 | MAXの% | ||||||
| 10p(5F) | 50p(25F) | 1000p(500F) | 10p(5F) | 50p(25F) | 1000p(500F) | 10p(5F) | 50p(25F) | 1000p(500F) | ||||||||
| 1 | ANS | across neighbourhood search | 0.94948 | 0.84776 | 0.43857 | 2.23581 | 1.00000 | 0.92334 | 0.39988 | 2.32323 | 0.70923 | 0.63477 | 0.23091 | 1.57491 | 6.134 | 68.15 |
| 2 | CLA | コードロックアルゴリズム(joo) | 0.95345 | 0.87107 | 0.37590 | 2.20042 | 0.98942 | 0.91709 | 0.31642 | 2.22294 | 0.79692 | 0.69385 | 0.19303 | 1.68380 | 6.107 | 67.86 |
| 3 | AMOm | 動物移動最適化m | 0.90358 | 0.84317 | 0.46284 | 2.20959 | 0.99001 | 0.92436 | 0.46598 | 2.38034 | 0.56769 | 0.59132 | 0.23773 | 1.39675 | 5.987 | 66.52 |
| 4 | (P+O)ES | (P+O)進化戦略 | 0.92256 | 0.88101 | 0.40021 | 2.20379 | 0.97750 | 0.87490 | 0.31945 | 2.17185 | 0.67385 | 0.62985 | 0.18634 | 1.49003 | 5.866 | 65.17 |
| 5 | CTA | 彗星の尾アルゴリズム(joo) | 0.95346 | 0.86319 | 0.27770 | 2.09435 | 0.99794 | 0.85740 | 0.33949 | 2.19484 | 0.88769 | 0.56431 | 0.10512 | 1.55712 | 5.846 | 64.96 |
| 6 | TETA | 時間進化移動アルゴリズム(joo) | 0.91362 | 0.82349 | 0.31990 | 2.05701 | 0.97096 | 0.89532 | 0.29324 | 2.15952 | 0.73462 | 0.68569 | 0.16021 | 1.58052 | 5.797 | 64.41 |
| 7 | SDSm | 確率的拡散探索M | 0.93066 | 0.85445 | 0.39476 | 2.17988 | 0.99983 | 0.89244 | 0.19619 | 2.08846 | 0.72333 | 0.61100 | 0.10670 | 1.44103 | 5.709 | 63.44 |
| 8 | BOAm | ビリヤード最適化アルゴリズムM | 0.95757 | 0.82599 | 0.25235 | 2.03590 | 1.00000 | 0.90036 | 0.30502 | 2.20538 | 0.73538 | 0.52523 | 0.09563 | 1.35625 | 5.598 | 62.19 |
| 9 | AAm | アーチェリーアルゴリズムM | 0.91744 | 0.70876 | 0.42160 | 2.04780 | 0.92527 | 0.75802 | 0.35328 | 2.03657 | 0.67385 | 0.55200 | 0.23738 | 1.46323 | 5.548 | 61.64 |
| 10 | ESG | 社会集団の進化(joo) | 0.99906 | 0.79654 | 0.35056 | 2.14616 | 1.00000 | 0.82863 | 0.13102 | 1.95965 | 0.82333 | 0.55300 | 0.04725 | 1.42358 | 5.529 | 61.44 |
| 11 | SIA | 等方的焼きなまし(joo) | 0.95784 | 0.84264 | 0.41465 | 2.21513 | 0.98239 | 0.79586 | 0.20507 | 1.98332 | 0.68667 | 0.49300 | 0.09053 | 1.27020 | 5.469 | 60.76 |
| 12 | ACS | 人工協調探索 | 0.75547 | 0.74744 | 0.30407 | 1.80698 | 1.00000 | 0.88861 | 0.22413 | 2.11274 | 0.69077 | 0.48185 | 0.13322 | 1.30583 | 5.226 | 58.06 |
| 13 | DA | 弁証法的アルゴリズム | 0.86183 | 0.70033 | 0.33724 | 1.89940 | 0.98163 | 0.72772 | 0.28718 | 1.99653 | 0.70308 | 0.45292 | 0.16367 | 1.31967 | 5.216 | 57.95 |
| 14 | BHAm | ブラックホールアルゴリズムM | 0.75236 | 0.76675 | 0.34583 | 1.86493 | 0.93593 | 0.80152 | 0.27177 | 2.00923 | 0.65077 | 0.51646 | 0.15472 | 1.32195 | 5.196 | 57.73 |
| 15 | ASO | 無政府社会最適化 | 0.84872 | 0.74646 | 0.31465 | 1.90983 | 0.96148 | 0.79150 | 0.23803 | 1.99101 | 0.57077 | 0.54062 | 0.16614 | 1.27752 | 5.178 | 57.54 |
| 16 | RFO | ロイヤルフラッシュ最適化(joo) | 0.83361 | 0.73742 | 0.34629 | 1.91733 | 0.89424 | 0.73824 | 0.24098 | 1.87346 | 0.63154 | 0.50292 | 0.16421 | 1.29867 | 5.089 | 56.55 |
| 17 | AOSm | 原子軌道探索M | 0.80232 | 0.70449 | 0.31021 | 1.81702 | 0.85660 | 0.69451 | 0.21996 | 1.77107 | 0.74615 | 0.52862 | 0.14358 | 1.41835 | 5.006 | 55.63 |
| 18 | TSEA | 亀甲進化アルゴリズム(joo) | 0.96798 | 0.64480 | 0.29672 | 1.90949 | 0.99449 | 0.61981 | 0.22708 | 1.84139 | 0.69077 | 0.42646 | 0.13598 | 1.25322 | 5.004 | 55.60 |
| 19 | DE | 差分進化 | 0.95044 | 0.61674 | 0.30308 | 1.87026 | 0.95317 | 0.78896 | 0.16652 | 1.90865 | 0.78667 | 0.36033 | 0.02953 | 1.17653 | 4.955 | 55.06 |
| 20 | SRA | レストラン経営達人アルゴリズム(joo) | 0.96883 | 0.63455 | 0.29217 | 1.89555 | 0.94637 | 0.55506 | 0.19124 | 1.69267 | 0.74923 | 0.44031 | 0.12526 | 1.31480 | 4.903 | 54.48 |
| 21 | CRO | 化学反応の最適化 | 0.94629 | 0.66112 | 0.29853 | 1.90593 | 0.87906 | 0.58422 | 0.21146 | 1.67473 | 0.75846 | 0.42646 | 0.12686 | 1.31178 | 4.892 | 54.36 |
| 22 | BIO | 血液型遺伝最適化(joo) | 0.81568 | 0.65336 | 0.30877 | 1.77781 | 0.89937 | 0.65319 | 0.21760 | 1.77016 | 0.67846 | 0.47631 | 0.13902 | 1.29378 | 4.842 | 53.80 |
| 23 | BSA | 鳥群アルゴリズム | 0.89306 | 0.64900 | 0.26250 | 1.80455 | 0.92420 | 0.71121 | 0.24939 | 1.88479 | 0.69385 | 0.32615 | 0.10012 | 1.12012 | 4.809 | 53.44 |
| 24 | HS | ハーモニー検索 | 0.86509 | 0.68782 | 0.32527 | 1.87818 | 0.99999 | 0.68002 | 0.09590 | 1.77592 | 0.62000 | 0.42267 | 0.05458 | 1.09725 | 4.751 | 52.79 |
| 25 | SSG | 苗木の播種と育成 | 0.77839 | 0.64925 | 0.39543 | 1.82308 | 0.85973 | 0.62467 | 0.17429 | 1.65869 | 0.64667 | 0.44133 | 0.10598 | 1.19398 | 4.676 | 51.95 |
| 26 | BCOm | 細菌走化性最適化M | 0.75953 | 0.62268 | 0.31483 | 1.69704 | 0.89378 | 0.61339 | 0.22542 | 1.73259 | 0.65385 | 0.42092 | 0.14435 | 1.21912 | 4.649 | 51.65 |
| 27 | ABO | アフリカ水牛の最適化 | 0.83337 | 0.62247 | 0.29964 | 1.75548 | 0.92170 | 0.58618 | 0.19723 | 1.70511 | 0.61000 | 0.43154 | 0.13225 | 1.17378 | 4.634 | 51.49 |
| 28 | (PO)ES | (PO)進化戦略 | 0.79025 | 0.62647 | 0.42935 | 1.84606 | 0.87616 | 0.60943 | 0.19591 | 1.68151 | 0.59000 | 0.37933 | 0.11322 | 1.08255 | 4.610 | 51.22 |
| 29 | TSm | タブーサーチM | 0.87795 | 0.61431 | 0.29104 | 1.78330 | 0.92885 | 0.51844 | 0.19054 | 1.63783 | 0.61077 | 0.38215 | 0.12157 | 1.11449 | 4.536 | 50.40 |
| 30 | BSO | ブレインストーム最適化 | 0.93736 | 0.57616 | 0.29688 | 1.81041 | 0.93131 | 0.55866 | 0.23537 | 1.72534 | 0.55231 | 0.29077 | 0.11914 | 0.96222 | 4.498 | 49.98 |
| 31 | WOAm | 鯨最適化アルゴリズムM | 0.84521 | 0.56298 | 0.26263 | 1.67081 | 0.93100 | 0.52278 | 0.16365 | 1.61743 | 0.66308 | 0.41138 | 0.11357 | 1.18803 | 4.476 | 49.74 |
| 32 | AEFA | 人工電界アルゴリズム | 0.87700 | 0.61753 | 0.25235 | 1.74688 | 0.92729 | 0.72698 | 0.18064 | 1.83490 | 0.66615 | 0.11631 | 0.09508 | 0.87754 | 4.459 | 49.55 |
| 33 | AEO | 人工生態系ベースの最適化アルゴリズム | 0.91380 | 0.46713 | 0.26470 | 1.64563 | 0.90223 | 0.43705 | 0.21400 | 1.55327 | 0.66154 | 0.30800 | 0.28563 | 1.25517 | 4.454 | 49.49 |
| 34 | ACOm | 蟻コロニー最適化M | 0.88190 | 0.66127 | 0.30377 | 1.84693 | 0.85873 | 0.58680 | 0.15051 | 1.59604 | 0.59667 | 0.37333 | 0.02472 | 0.99472 | 4.438 | 49.31 |
| 35 | BFO-GA | 細菌採食の最適化:Ga | 0.89150 | 0.55111 | 0.31529 | 1.75790 | 0.96982 | 0.39612 | 0.06305 | 1.42899 | 0.72667 | 0.27500 | 0.03525 | 1.03692 | 4.224 | 46.93 |
| 36 | SOA | シンプル最適化アルゴリズム | 0.91520 | 0.46976 | 0.27089 | 1.65585 | 0.89675 | 0.37401 | 0.16984 | 1.44060 | 0.69538 | 0.28031 | 0.10852 | 1.08422 | 4.181 | 46.45 |
| 37 | ABHA | 人工蜂の巣アルゴリズム | 0.84131 | 0.54227 | 0.26304 | 1.64663 | 0.87858 | 0.47779 | 0.17181 | 1.52818 | 0.50923 | 0.33877 | 0.10397 | 0.95197 | 4.127 | 45.85 |
| 38 | ACMO | 大気雲モデルの最適化 | 0.90321 | 0.48546 | 0.30403 | 1.69270 | 0.80268 | 0.37857 | 0.19178 | 1.37303 | 0.62308 | 0.24400 | 0.10795 | 0.97503 | 4.041 | 44.90 |
| 39 | ADAMm | 適応モーメント推定M | 0.88635 | 0.44766 | 0.26613 | 1.60014 | 0.84497 | 0.38493 | 0.16889 | 1.39880 | 0.66154 | 0.27046 | 0.10594 | 1.03794 | 4.037 | 44.85 |
| 40 | CGO | カオスゲーム最適化 | 0.57256 | 0.37158 | 0.32018 | 1.26432 | 0.61176 | 0.61931 | 0.62161 | 1.85267 | 0.37538 | 0.21923 | 0.19028 | 0.78490 | 3.902 | 43.35 |
| 41 | ATAm | 人工部族アルゴリズムM | 0.71771 | 0.55304 | 0.25235 | 1.52310 | 0.82491 | 0.55904 | 0.20473 | 1.58867 | 0.44000 | 0.18615 | 0.09411 | 0.72026 | 3.832 | 42.58 |
| 42 | CFO | 中心力最適化 | 0.60961 | 0.54958 | 0.27831 | 1.43750 | 0.63418 | 0.46833 | 0.22541 | 1.32792 | 0.57231 | 0.23477 | 0.09586 | 0.90294 | 3.668 | 40.76 |
| 43 | ASHA | 人工シャワーアルゴリズム | 0.89686 | 0.40433 | 0.25617 | 1.55737 | 0.80360 | 0.35526 | 0.19160 | 1.35046 | 0.47692 | 0.18123 | 0.09774 | 0.75589 | 3.664 | 40.71 |
| 44 | ASBO | 適応型社会行動最適化(ASBO) | 0.76331 | 0.49253 | 0.32619 | 1.58202 | 0.79546 | 0.40035 | 0.26097 | 1.45677 | 0.26462 | 0.17169 | 0.18200 | 0.61831 | 3.657 | 40.63 |
| 45 | MEC | mind evolutionary computation | 0.69533 | 0.53376 | 0.32661 | 1.55569 | 0.72464 | 0.33036 | 0.07198 | 1.12698 | 0.52500 | 0.22000 | 0.04198 | 0.78698 | 3.470 | 38.55 |
| NOA2 | ニューロボイド最適化アルゴリズム2(joo ) | 0.47681 | 0.30764 | 0.25447 | 1.03892 | 0.32380 | 0.20977 | 0.15740 | 0.69097 | 0.27077 | 0.14677 | 0.09729 | 0.51483 | 2.245 | 24.94 | |
まとめ
私が開発したニューラルボイド最適化アルゴリズム(NOA2)は、群知能(ボイドアルゴリズム)の原理とニューラルネットワークによる制御を組み合わせたハイブリッドアプローチです。主なアイデアは、ニューラルネットワークを用いてボイドエージェントの行動パラメータを適応的に制御することにあります。現在の実装では、隠れ層を持たない単純な単層ニューラルネットワークを使用しています。入力層は、現在の座標、速度、最良解までの距離、適応度関数の値を受け取り、出力層はボイド群の行動ルールに対する速度補正および適応パラメータを定義します。
このバージョンには中間層は存在せず、入力数は(coords*2 + 2)で定義されます。ここでcoordsは探索空間の次元数です。出力には各座標の補正値に加え、群れルールを適応させるための3つの追加パラメータが含まれます。アルゴリズムには設定可能なパラメータが非常に多いため、最適な組み合わせを見つけることは困難です。様々な組み合わせを試みましたが、テスト関数上で最良の結果を示す理想的な構成はまだ見つかっていません。
現状の形では、このアルゴリズムは明確に概念実証としての位置付けであり、ハイブリッド最適化手法の実験として全体的に興味深いものです。しかし、十分な性能を示すには至っておらず、実施したテストでは最小限のスコアしか得られません。あくまでアルゴリズムのハイブリッド化アプローチの例として理解されるべきです。興味のある研究者や開発者にとって、NOA2のコードは異なる構成やパラメータを試す出発点となり得ます。また、集団ベース手法と機械学習手法の利点を活かした、より高度なハイブリッド最適化アルゴリズムの開発にも応用可能です。

図3:対応するテストに応じたアルゴリズムのカラーグラデーション

図4:アルゴリズムテスト結果のヒストグラム(0から100のスケール、高いほど良い)100は理論上の最大値であり、アーカイブには評価表を計算するためのスクリプトがあります。
NOA2の長所と短所
長所
- 興味深いアイディア
短所
- 結果は弱い
この記事には、最新版のアルゴリズムコードを含むアーカイブが添付されています。記事の著者は、正規アルゴリズムの説明の絶対的な正確さについて責任を負いません。検索機能を向上させるために、それらの多くに変更が加えられています。記事に示された結論と判断は、実験結果に基づいています。
記事で使用されているプログラム
| # | 名前 | 種類 | 詳細 |
|---|---|---|---|
| 1 | #C_AO.mqh | インクルード | 集団最適化アルゴリズムの親クラス |
| 2 | #C_AO_enum.mqh | インクルード | 集団最適化アルゴリズムの列挙 |
| 3 | TestFunctions.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_NOA2.mq5 | スクリプト | NOA2テストスタンド |
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/17497
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
初級から中級まで:インジケーター(III)
FXにおけるスワップ差裁定:合成ポートフォリオの構築と一貫したスワップフローの生成
市場シミュレーション(第14回):ソケット(VIII)
MQL5 MVCパラダイムにおけるテーブルのビューおよびコントローラーコンポーネント:コンテナ
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索