科学者コミュニティ最適化 (CoSO):理論編
内容
はじめに
最適化手法についての探究を続ける中で、この記事では、最適化問題を解決するためのもう1つのアプローチとして、CoSO (Community of Scientists Optimization)アルゴリズムを取り上げます。これは、科学者コミュニティの仕組みをシミュレーションすることを基礎としたアルゴリズムです。従来の生物を模倣したアルゴリズムとは異なり、CoSOは科学活動に特有の特徴を再現します。具体的には、研究成果を論文として発表すること、研究助成金をめぐって競争すること、研究グループを形成すること、そして既知の領域を深く研究することと、根本的に新しい解決策を探索することのバランスを取ることです。CoSOアルゴリズムは、A. MilaniとV. Santucciという2人の科学者によって2012年に開発・発表されました。
このアプローチの興味深い特徴は、探索プロセスが自然に自己組織化されることです。実際の科学者コミュニティと同じように、このアルゴリズムは有望な領域にリソースを集中させ、学術誌の仕組みを通じて最良の解を保存・共有し、さらに「アウトサイダー」に資金を提供することで、必要な多様性を維持します。集団サイズを動的に変化させることで、アルゴリズムは特定の問題の特性に適応できます。そのため、パラメータを細かく調整する必要がありません。これは、通常は一定の集団サイズを使用することを考えると、もう1つの革新的な特徴です。本記事では、アルゴリズムの数学的基礎、その主要な構成要素、そしてそれらがどのように相互作用するのかについて、詳しく見ていきます。
アルゴリズムの実装
CoSOの集団ベースアルゴリズムは、多次元空間の中で最適な解を探索する研究者の行動をシミュレーションするという考えに基づいています。研究者は学術誌への成果発表を通じて知識を交換し、限られた資源、すなわち研究助成金をめぐって競争します。各研究者は探索空間上の1つの点として表されます。研究者は、位置(どこを探索しているか)、移動方向、研究資金を持っています。資金がなくなると、その研究者は非アクティブになります。
各研究者は、探索空間における位置(x)、移動方向(v)、個人的な最良結果(b)、研究資金の額(m)、資金管理戦略(s)、そして異なる学術誌を選択する確率(ρ)という一連のパラメータによって表されます。
研究者の移動は、移動方向を更新する式によって決定されます。この式は、3つの要素、すなわち、慣性(同じ方向へ移動し続けようとする傾向)、認知的要素(個人的な最良結果を求める傾向)、そして社会的要素(学術誌に掲載された最良結果から受ける影響)を組み合わせたものです。
v {i, t} = ω · v {i, t-1} + φ₁ · β₁ · (b {i, t-1} - x {i, t-1}) + φ₂ · β₂ · Σⱼ [ρᵢⱼ · (J {j,c} - x {i, t-1})]
ここで、ω = 0.7298は慣性係数、φ₁ = φ₂ = 1.49618は加速度係数、β₁とβ₂は[0,1]の範囲から生成される乱数、ρᵢⱼは研究者iが学術誌jを選択する確率、J {j,c}は学術誌jからランダムに選択された論文です。
移動方向を更新した後、研究者の位置は単純な式「x {i, t} = x {i, t-1} + v {i, t}」に従って変化します。
学術誌には、発見された最良解が保存されます。研究者は学術誌を読み、良好な解へ向かって移動します。そして、最良の結果だけが学術誌に掲載されます。アルゴリズムにおける学術誌は、すべての研究者によって発見された上位k個の解を保存する集合的な記憶として機能します。研究者が新しい結果を得ると、その結果を自身の確率分布ρに従っていずれかの学術誌へ投稿します。学術誌は、その結果がすでに掲載されている結果の中で最も悪いものよりも優れている場合にのみ、その結果を受け入れます。これによって、コミュニティが利用できる情報の質が継続的に向上します。
成功した研究者は、より多くの資金を受け取ります。資金の一部は「アウトサイダー」、つまりランダムな場所に配置される新しい研究者に割り当てられます。これは、局所最適解に陥ることを避けるのに役立ちます。資金を分配する仕組みは、科学における競争的な研究資金の仕組みを模倣しています。各反復で、研究者は1単位の資金を消費し、消費された資金の総額は、順位に基づくルーレット選択によって再分配されます。全体順位においてi番目に位置する研究者が資金を受け取る確率は、次の式によって決定されます。
P (i) = (N - i + 1) / (N · (N + 1) / 2)
ここで、Nは研究者の人数です。
資金Ωの一部は、「アウトサイダー」、つまりランダムな位置に配置される新しい研究者を作成するために確保されます。Ωパラメータは、探索と活用のバランスを維持するために適応的に変更されます。集団における適応度値の標準偏差σが初期値σ₀を下回ると、これは収束を示すため、アウトサイダーの割合が増加します。
Ω {t} = Ω {t-1} + (Ω {max} - Ω {min}) / 2 · ε⁺(σ < σ₀の場合)Ω {t} = Ω {t-1} - (Ω {max} - Ω {min}) / 2 · ε⁻(それ以外の場合)
ここで、Ω {min} = 0.2、Ω {max} = 0.5、ε⁺ = 0.2、ε⁻ = 0.1です。
成功した科学者は、自分の近くで探索を開始するアシスタントを雇うことができます。これは、有望な領域を詳しく探索するのに役立ちます。資金m > 1を持つ成功した研究者は、新しい研究者を雇うことができます。この場合、研究者はs戦略に従って資金の一部を自分自身のために確保し、残りを雇用のために使用します。
新しい研究者は、わずかな摂動を加えた状態で指導者の特性を継承します。位置はx {new} = x {supervisor} + N (0, σv)として初期化され、戦略はs {new} = N (s {supervisor}, σs)として初期化され、学術誌を選択する確率はρ {new, j} = N (ρ {supervisor, j}, σρ)として初期化された後、正規化されます。
アルゴリズムにおける探索プロセスは、森でキノコを採っている人々のグループにたとえることができます。全員がそれぞれの場所を探し、誰かがキノコのある場所を見つけると、ほかの人たちに声をかけます。その後、グループの一部はその場所へ向かい、残りは新しい場所を探し続けます。長い間何も見つけられなかった人たちは帰宅しますが、最も成功したキノコ採りの人たちは、友人をキノコのある場所へ連れてきます。
アルゴリズムの性能は、複数のメカニズムの組み合わせによって生み出されます。学術誌は情報共有を促進し、探索を有望な領域へ導きます。競争的な資金配分は、非効率な研究者を排除することで選択圧を生み出します。アシスタントの雇用は、良好な解の周辺で局所探索をおこなうことを可能にし、アウトサイダーは早すぎる収束を防ぎます。
集団サイズを動的に変化させることで、アルゴリズムは問題の複雑さに適応できます。有望な領域では研究者の数を増やし、期待できない領域では減らします。これらすべてのメカニズムが連携することで、複雑な多次元空間における問題を解決できる自己組織化システムが形成されます。

図1:CoSOアルゴリズムの動作
図には以下の内容が示されています。
- 探索空間では、研究者(緑色の円)が最適解へ向かって移動しており、その移動ベクトルが示されています。
- 学術誌には発見された最良の解が保存され、研究者はそれらを読み取り、自身の結果を投稿します。
- 資金配分では、多次元空間における資金配分の仕組みと、アウトサイダーのための適応パラメータΩが示されています。
- 移動方向を更新する式は、研究者の移動を決定する数学的基礎です。
- 主要なメカニズムでは、研究者の種類を示す凡例があり、資金を持つアクティブな研究者、資金を持たない非アクティブな研究者、新規雇用研究者、そしてランダムな位置に配置されたアウトサイダーが示されています
矢印は、情報(灰色)、移動(青色)、資金(オレンジ色)の流れを示しています。それでは、アルゴリズムの擬似コードを見ていきましょう。
1. 初期化
- ランダムな位置に10人の研究者を作成する
- 全員に15単位の研究資金を与える(150 / 10)
- 全員に以下を与える:
* ランダムな資金管理戦略(0~1)
* ランダムな学術誌の選好
* 初期移動方向(小さなランダム性)
- 空の学術誌を3つ作成する
- 初期状態における集団の分布の広がりを記録する
2. メインループ(指定された回数繰り返す)
2.1. 結果の評価
活動中のすべての研究者について:
- 現在の位置f(x)の品質を計算する
- 結果が個人記録よりも良ければ:
* 個人記録を更新する
2.2. 学術誌への投稿
活動中のすべての研究者について:
- 個人的な選好に従って学術誌を選択する
- 結果を投稿する
* 結果が上位10件に入っていれば、学術誌はその結果を受け入れる
* 最も質の低い論文を削除する
2.3. コストの計上と報告
- 消費されたすべての資金を集計する(各研究者から1単位)
- 活動中の研究者の順位を作成する
- 資金を使い果たした研究者を「非稼働」としてマークする
2.4. 資金配分
- アウトサイダーに割り当てる割合を決定する(20~50%)
- 残りを既存の研究者の間で配分する:
順位が高いほど、資金を受け取る確率が高くなる
* 重み付きルーレット選択を使用する
2.5. アウトサイダーの作成
- 割り当てられた資金を1~5人の新規研究者に使用する
- ランダムな位置に配置する
- 割り当てられた予算を均等に配分する
2.6. アシスタントの雇用
資金が豊富な研究者(資金>1)ごとに:
- 戦略に従って資金の一部を自分自身のために確保する
- 残りの資金で1~3人のアシスタントを雇用する:
* 自分の近くに配置する
* わずかな変更を加えた上で、自分の経験を共有する
2.7. 移動方向の選択
活動中のすべての研究者について:
New_direction =
0.7 × Old_direction + // 慣性
1.5 × Randomness × (Personal_record - Position) + // 自身の最良結果へ
1.5 × Randomness × Sum_by_journals // 学術誌の最良結果へ
(Journal_weight × (Journal_article - Position))
2.8. 移動
活動中のすべての研究者について:
- 1ステップ進める:New_position = Old_position + Direction
- 境界の外に出る場合は、境界内へ戻す
2.9. 多様性の制御
- 現在の集団の分布の広がりを測定する
- 分布の広がりが小さくなっている場合(収束している場合):
* アウトサイダーの割合を10%増加させる
- それ以外の場合:
* アウトサイダーの割合を5%減少させる
2.10. 集団サイズの管理:
- 非活動の研究者が25%を超える、または総数が200を超える場合:
* 配列から非活動の研究者を削除する
- 残りが150人を超える場合:
* 上位150人だけを残す
3.結果
- 発見された最良の解を返す
それでは、CoSOアルゴリズムの実装に進みましょう。3つのデータ構造体を記述します。最初の構造体であるS_Journal_Entryは、以下を含む1つの学術誌のエントリを表します。
- fitness:特定のエントリの「品質」または「適応度」を表す数値
- decision[]:このエントリに関連付けられたパラメータを格納する動的配列
- entries []:S_Journal_Entryエントリの動的配列
- length:現在のエントリ数
- maxLength:学術誌が保存できるレコードの最大数
Initメソッドは、最大長を設定し、entries用のメモリを確保し、各エントリを最小のfitness値で初期化することで、学術誌を初期化します。Addメソッドは、新しいエントリを学術誌に追加するために使用されます。このメソッドの特徴は、fitness値に基づいて学術誌をソートされた状態に維持することです。学術誌がすでに満杯で、新しいエントリが現在の最も悪いエントリよりも悪い場合、そのエントリは追加されません。それ以外の場合は、二分探索を使用して新しいエントリを挿入する位置を探し、既存のエントリを移動させて空きを作り、新しいエントリを挿入します。学術誌がまだ満杯でない場合は、lengthを増加させます。
3つ目の構造体であるS_Researcherは、「研究者」または「エージェント」を表します。以下のパラメータを持ちます。
- x []:研究者の現在位置(座標の配列)
- v []:研究者の移動方向または速度ベクトル
- b []:当該研究者が個別に発見した最良の位置
- rho []:さまざまな学術誌における出版確率の配列。研究者は複数の「学術誌」または情報源と相互作用します。
- s:資金管理戦略
- m:資金の額(整数)
- f:現在の研究者の適合度
- fb:研究者が発見した最良位置の適合度
- alive:研究者が稼働中であることを示すフラグ
//———————————————————————————————————————————————————————————————————— // Structure for the journal struct S_Journal_Entry { double fitness; double decision []; void Init (int coords) { ArrayResize (decision, coords); fitness = -DBL_MAX; } }; // Journal structure struct S_Journal { S_Journal_Entry entries []; int length; int maxLength; void Init (int maxLen, int coords) { maxLength = maxLen; length = 0; ArrayResize (entries, maxLen); for (int i = 0; i < maxLen; i++) { entries [i].Init (coords); entries [i].fitness = -DBL_MAX; // Initialize with the minimum value } } void Add (double fit, const double &coord []) { // Quick check - if it is the worst and the journal is full, do not add if (length >= maxLength && fit <= entries [length - 1].fitness) return; int insertPos = length; // Find the insertion position (binary search) if (length > 0) { int left = 0; int right = length - 1; while (left <= right) { int mid = (left + right) / 2; if (entries [mid].fitness < fit) right = mid - 1; else left = mid + 1; } insertPos = left; } // If we insert at the end and the journal is full if (insertPos >= maxLength) return; // Shift the elements if (length < maxLength) length++; for (int i = length - 1; i > insertPos; i--) { entries [i].fitness = entries [i - 1].fitness; ArrayCopy (entries [i].decision, entries [i - 1].decision, 0, 0, WHOLE_ARRAY); } // Insert a new element entries [insertPos].fitness = fit; ArrayCopy (entries [insertPos].decision, coord, 0, 0, WHOLE_ARRAY); } }; // Extended researcher structure struct S_Researcher { double x []; // current position double v []; // movement direction double b []; // personal best result double rho []; // probabilities of publication in journals double s; // funds management strategy int m; // amount of funds double f; // fitness of the current position double fb; // fitness of the best position bool alive; // activity flag void Init (int coords, int journalsNum) { if (ArraySize (x) != coords) { ArrayResize (x, coords); ArrayResize (v, coords); ArrayResize (b, coords); } if (ArraySize (rho) != journalsNum) { ArrayResize (rho, 0); ArrayResize (rho, journalsNum); } f = -DBL_MAX; fb = -DBL_MAX; m = 0; s = 0.5; alive = true; } };
C_AO_CoSOクラスを記述しましょう。このクラスは、Community of Scientists Optimization (CoSO)最適化アルゴリズムを実装したものです。C_AO_CoSOクラスはC_AO基底クラスを継承しており、さまざまな最適化アルゴリズムに共通するプロパティとメソッドが存在することを前提としています。
CoSOを制御する各種パラメータには、以下のデフォルト値が設定されています。
- 「研究者」集団の初期サイズ
- 配分可能な「資金」(リソース)の総量(科学者のメタファーでは、これは「仕事」(解の探索)に使用する研究助成金に相当します)
- 研究者が自身の「発見」(最良解)を投稿できる「学術誌」の数
- 1つの「学術誌」に含めることができる論文/発見の最大数
- 以前の移動方向の影響を制御する「慣性」パラメータ
- phi1 = 1.5:認知パラメータ。研究者の移動に対する個人的な影響(自身の最良の発見を求める傾向)を表します。
- phi2 = 1.5:社会的パラメータ。研究者が学術誌の掲載内容をどの程度重視するかを決定します。
- omegaMin = 0.2:「アウトサイダー」の最小割合
- omegaMax = 0.5:「アウトサイダー」の最大割合
- epsilonPlus = 0.2:多様性を増加させるステップ
- epsilonMinus = 0.1:多様性を減少させるステップ
Init()は、アルゴリズムの初期化メソッドです。各パラメータの探索範囲(最小値、最大値)とステップ、およびepochsPの数を受け取ります。
Moving ()は、研究者の「速度」と「方向」に基づいて、探索空間における研究者の「移動」(位置の更新)をおこなうメソッドです。
Revision ()は、その時点までにすべての研究者によって発見された最良の大域解を更新するメソッドです。
privateフィールドには、S_Researcher構造体の動的配列があります。この構造体の各要素は1人の「研究者」を表し、その現在位置、速度、発見した最良解、alive、fitness(f)などの情報を保持します。さらに、S_Journal構造体の動的配列があります。各構造体には、特定の「学術誌」に研究者が投稿した最良の解の集合が格納されます。現在の「アウトサイダー」の割合、初期標準偏差、現在の実際の集団サイズも保持します。集団サイズはアルゴリズムの実行中に変化する可能性があります。また、許容される集団サイズの最大値も保持します。
struct S_GlobalReportは大域最良解に関する情報を格納するためのネストされた構造体で、fitness(目的関数の値)と、この解を発見した研究者のindex(インデックス)を保持します。
S_GlobalReport globalReport []は、全体レポートを格納するための配列です。
privateアルゴリズムメソッド
- UpdateDirection():idxインデックスで指定された研究者の移動方向(または「速度」)を更新します。
- SubmitToJournal ():指定されたインデックスの研究者が、自身の現在の最良解をいずれかの「学術誌」に「投稿」できるようにします。
- AssignFunds():研究者間で「資金」を配分するメソッドです。CoSOでは、研究助成金が研究者の行動や探索能力に影響を与える可能性があります。
- HireResearchers():新しい研究者を作成するメソッドです。
- CreateOutsiders ():「アウトサイダー」を作成します。これは、局所最適解に陥ることを避けるために、より積極的またはランダムな探索をおこなうことができる特殊な研究者です。
- ComputeStdDev():集団の標準偏差を計算します。これは、集団の「多様性」または「収束」の指標として機能します。
- UpdateOmega():現在のomegaCurrentパラメータを更新します。
- SelectJournal():指定された確率に基づいて、投稿先となる学術誌を選択します。
- NormalizeProbabilities ():確率の合計が1になるように、確率配列を正規化します。
- CompactPopulation():集団を「コンパクト化」するメソッドです。すなわち、最も効率の低い研究者を削除し、集団サイズを管理します。
//———————————————————————————————————————————————————————————————————— class C_AO_CoSO : public C_AO { public: //---------------------------------------------------------- ~C_AO_CoSO () { } C_AO_CoSO () { ao_name = "CoSO"; ao_desc = "Community of Scientist Optimization"; ao_link = "https://www.mql5.com/ja/articles/18886"; popSize = 30; // initial population size totalFunds = 150; // total amount of funds journalsNum = 3; // number of journals journalLen = 10; // journal length omega = 0.7; // inertia parameter ArrayResize (params, 5); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "totalFunds"; params [1].val = totalFunds; params [2].name = "journalsNum"; params [2].val = journalsNum; params [3].name = "journalLen"; params [3].val = journalLen; params [4].name = "omega"; params [4].val = omega; //---------------------------------------------------------------- phi1 = 1.5; // cognitive parameter phi2 = 1.5; // social parameter omegaMin = 0.2; // minimum percentage of outsiders omegaMax = 0.5; // maximum percentage of outsiders epsilonPlus = 0.2; // diversity increase step epsilonMinus = 0.1; // diversity reduction step } void SetParams () { popSize = (int)params [0].val; totalFunds = (int)params [1].val; journalsNum = (int)params [2].val; journalLen = (int)params [3].val; omega = params [4].val; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0); void Moving (); void Revision (); //------------------------------------------------------------------ int totalFunds; // total amount of funds int journalsNum; // number of journals int journalLen; // journal length double omega; // inertia parameter double phi1; // cognitive parameter double phi2; // social parameter double omegaMin; // minimum percentage of outsiders double omegaMax; // maximum percentage of outsiders double epsilonPlus; // diversity increase step double epsilonMinus; // diversity reduction step private: //--------------------------------------------------------- S_Researcher researchers []; // array of researchers S_Journal journals []; // array of journals double omegaCurrent; // current percentage of outsiders double sigma0; // initial standard deviation int actualPopSize; // current population size int maxPopSize; // maximum population size double socialComponent []; // cache for the social component struct S_GlobalReport { double fitness; int index; }; S_GlobalReport globalReport []; // Algorithm methods void UpdateDirection (int idx); void SubmitToJournal (int idx); void AssignFunds (int availableFunds); void HireResearchers (int idx); void CreateOutsiders (int outsiderFunds); double ComputeStdDev (); void UpdateOmega (); int SelectJournal (const double &probs []); void NormalizeProbabilities (double &probs []); void CompactPopulation (); }; //————————————————————————————————————————————————————————————————————
InitメソッドはC_AO_CoSOクラスの一部であり、アルゴリズムの実行を開始する前に、必要なすべての構造体とパラメータを準備する役割を担います。まず、探索パラメータの範囲(最小値、最大値、ステップ)に関する標準的な初期化がおこなわれ、epochsPの数が設定されます。この標準的な初期化に失敗した場合、関数はfalseを返します。その後、アルゴリズムの内部状態変数が初期化されます。
- actualPopSize:現在の研究者集団のサイズ
- maxPopSize:許容される研究者集団の最大サイズ(または研究者用に確保されたメモリ)
- sigma0:後で計算される標準偏差の初期値
- omegaCurrent:現在のomegaパラメータであり、omegaMinとomegaMaxの算術平均として初期化される
次に、入力パラメータの有効性を確認します。
- totalFundsは、研究者集団のサイズ以上でなければなりません。各研究者には、一定量の資金または研究助成金を割り当てる必要があるためです。
- journalsNumは、「学術誌」の数であり、少なくとも1でなければなりません。
- journalLenは、各「学術誌」の長さであり、これも少なくとも1でなければなりません。
- omega(グローバルパラメータ)は、0.0~1.0の範囲内でなければなりません。
パラメータの確認後、「以前の実行結果を完全にクリアする」処理がおこなわれます。これは、動的な「researchers」、「journals」、「globalReport」、「socialComponent」配列のサイズを0にリセットすることを意味します。アルゴリズムは常にクリーンな状態から開始します。すでに説明した状態変数(actualPopSize、maxPopSize、sigma0)も再度リセットされます。
次に、「学術誌」が初期化されます。「journals」配列はjournalsNumに従ってサイズ変更されます。この配列内の各学術誌は、指定されたjournalLenの長さと、指定された数のcoords座標で初期化されます。
研究者の初期化では、maxPopSizeが少なくとも3×popSize(集団サイズ)に設定されます。これによって、集団サイズが変化する可能性に備えた「予備領域」が確保されます。アルゴリズムでは新しい研究者が作成されるためです。次に、fundsPerResearcherが計算されます。これは各研究者に割り当てられる資金の量であり、総資金を集団サイズで割って求めます。その後、すべての研究者を対象にループが実行されます。
各研究者は、coords個の座標とjournalsNum個の学術誌を持つ状態で初期化されます。aliveフラグは、最初のpopSize人の研究者に対してのみtrueに設定され、それ以外の研究者はまだ非活動です。アクティブな研究者には、計算されたfundsPerResearcherの資金が割り当てられます。「s」で表される「資金管理戦略」はランダムに初期化されます。位置「x」、個人的な最良位置「b」、移動ベクトル「v」も初期化されます。「x」と「b」の位置は、指定された範囲(rangeMin~rangeMax、rangeStepを考慮)内でランダムに設定されます。「v」の移動ベクトルは、正規分布から生成された小さなランダム値で初期化されます。「rho」は、各学術誌に対する確率としてランダムな値で初期化されます。
NormalizeProbabilities関数が呼び出され、これらの確率が正規化されます。すべての研究者の初期化が完了した後、初期標準偏差sigma0が計算されます。これは、すべての座標における研究者の位置の広がりを表します。sigma0が0になった場合(たとえば、すべての研究者が同じ位置にいる場合)は、0による除算を防ぐために1.0に設定されます。omegaCurrentパラメータも再びomegaMinとomegaMaxの平均値に設定されます。
最後に、初期化されたすべての研究者(popSize人)の位置が、標準的なエージェント配列「a」にコピーされます。これにより、Init関数は最適化アルゴリズムを実行するための環境を完全に準備します。すべての「研究者」エージェントとそのパラメータ、「学術誌」による結果の保存領域、そしてアルゴリズムを制御するための内部パラメータが初期化されます。
//———————————————————————————————————————————————————————————————————— bool C_AO_CoSO::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ // Initialize state variables actualPopSize = 0; maxPopSize = 0; sigma0 = 0; omegaCurrent = omegaMin + (omegaMax - omegaMin) / 2.0; // Check the parameters validity if (totalFunds < popSize) totalFunds = popSize; if (journalsNum < 1) journalsNum = 1; if (journalLen < 1) journalLen = 1; if (omega < 0.0) omega = 0.0; if (omega > 1.0) omega = 1.0; // Complete reset of data from previous runs ArrayResize (researchers, 0); ArrayResize (journals, 0); ArrayResize (globalReport, 0); ArrayResize (socialComponent, 0); // Reset parameters actualPopSize = 0; maxPopSize = 0; sigma0 = 0; // Initialize the journals ArrayResize (journals, journalsNum); for (int i = 0; i < journalsNum; i++) { journals [i].Init (journalLen, coords); } // Initialize the array of researchers with a reserve maxPopSize = MathMin (popSize * 3, 300); // Limit the maximum size ArrayResize (researchers, maxPopSize); ArrayResize (socialComponent, coords); actualPopSize = popSize; int fundsPerResearcher = totalFunds / popSize; for (int i = 0; i < maxPopSize; i++) { researchers [i].Init (coords, journalsNum); researchers [i].alive = (i < popSize); if (i < popSize) { researchers [i].m = fundsPerResearcher; researchers [i].s = u.RNDprobab (); // Initialize a position for (int c = 0; c < coords; c++) { researchers [i].x [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); researchers [i].x [c] = u.SeInDiSp (researchers [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]); researchers [i].b [c] = researchers [i].x [c]; researchers [i].v [c] = u.GaussDistribution (0.0, -0.01, 0.01, 1); } // Initialize journal probabilities for (int j = 0; j < journalsNum; j++) { researchers [i].rho [j] = u.RNDprobab (); } NormalizeProbabilities (researchers [i].rho); } } // Calculate the initial standard deviation sigma0 = ComputeStdDev (); if (sigma0 == 0) sigma0 = 1.0; // Protection against zero divide omegaCurrent = omegaMin + (omegaMax - omegaMin) / 2.0; // Copy the researchers to the standard array of agents for (int i = 0; i < popSize; i++) { ArrayCopy (a [i].c, researchers [i].x, 0, 0, WHOLE_ARRAY); } return true; } //————————————————————————————————————————————————————————————————————
Movingメソッドは、アルゴリズムの反復処理における中心的な部分であり、「研究者」の行動とその相互作用をシミュレートしながら、最適解に向けて1回の探索ステップを実行します。関数の開始時に、初期化後の最初の呼び出しである場合(revision = false)、単純に「revision」を「true」に設定して終了します。以下は、各反復処理で実行されるアルゴリズムの主な処理です。
研究者の適合度の更新。 すべての稼働中の研究者(現在の集団に含まれる研究者)について、すべてのエージェントのfitness評価結果を格納している一般的な「a」配列から、そのfitness値を取得します。次に、研究者の現在のfitnessが、それまでに記録されていた個人的な最良fitnessよりも優れているかどうかを確認します。優れている場合は、fbを更新し、研究者の最良位置bにも現在の位置xを保存します。
結果の学術誌への投稿 。すべての稼働中の研究者は、現在の結果(現在のfitnessと位置)をいずれかの「学術誌」に「投稿」します。この処理の詳細はSubmitToJournal関数にまとめられており、研究者の「rho」確率に基づいて学術誌を選択し、その結果が十分に良ければ学術誌に追加します。
全体レポートの収集と利用可能な資金の計算 。この段階では、研究者の状態を再評価し、資金配分のためのリソースを準備します。活動中のすべての研究者について、その「資金」(m)を1減らします。これは、現在の反復にかかるコストを表しています。同時に、利用可能な資金の総額availableFundsが増加します。資金を消費した後も研究者に資金が残っている場合(m > 0)、その研究者は活動を継続できるとみなされ、「全体レポート」に含められます。資金が枯渇した場合、その研究者は非活動になります(alive = false)。globalReportが生成されます。これは、アクティブな状態を維持し、資金を持っている研究者だけについて、fitnessとindexを格納した配列です。
全体レポートのソート 。globalReportの要素は、単純なバブルソートアルゴリズムを使用して、fitnessの降順(最良から最悪)に並べ替えられます。これによって、最も成功した研究者をすばやく特定できます。
資金の分配 。AssignFunds関数が呼び出され、利用可能な資金availableFundsがアクティブな研究者の間で再配分されます。globalReportによって決定された最も優れた研究者にはより多くの資金が与えられ、活動を継続するよう促されます。一方、成功度の低い研究者には資金が少なくなるか、まったく与えられない場合があり、その結果、将来の反復で非活動になる可能性があります。
既存の研究者による新しい研究者の雇用 。現在、十分な資金を残している研究者(m > 1)は、新しい研究者を「雇用」できます。これは、集団を増加させたり、既存の成功した研究者を基に新しいエージェントを作成したりするための仕組みです。詳細はHireResearchersメソッドで処理されます。
方向と位置の更新。 各アクティブな研究者について、UpdateDirection関数が呼び出されます。この関数は、研究者の個人的な最良結果b、学術誌から得られた最良結果、社会的要素、その他のアルゴリズム上の要素に基づいて、研究者の新しい移動ベクトルvを計算します。研究者の位置xは、移動ベクトルvを加えることで更新されます。位置が範囲外になっていないか確認し、必要に応じて調整します。また、許容されるrangeStepに最も近い値へ「離散化」、つまり丸め処理もおこないます。
多様性パラメータの更新 。UpdateOmega関数が呼び出され、アルゴリズムのomegaCurrentパラメータが調整されます。このパラメータは、探索(新しい領域を見つけること)と活用(発見した解をさらに改善すること)のバランスを制御するために使用され、アルゴリズムの実行中に動的に変化することで、集団の多様性を維持します。
集団のコンパクト化 。CompactPopulation関数が呼び出され、researchers配列から非活動の研究者(alive = false)を削除します。これによって、配列のサイズが実質的に縮小され、メモリが解放されます。
エージェント配列への位置のコピー 。最後に、アクティブなすべての研究者の現在位置が「a」配列にコピーされます。「a」のサイズはactualPopSizeに応じて更新されます。また、popSizeグローバル変数も新しいactualPopSizeに更新されます。
このように、Moving関数はCoSOアルゴリズムにおける1回の反復のライフサイクル全体をカプセル化しています。これには、評価、社会的相互作用(「学術誌」)、資源管理、集団の動的な変化、そして探索空間におけるエージェントの移動が含まれます。
//———————————————————————————————————————————————————————————————————— void C_AO_CoSO::Moving () { if (!revision) { revision = true; return; } //--- CoSO basic steps: // 1. Updating 'fitness' of researchers from the agents array int aSize = ArraySize (a); for (int i = 0, j = 0; i < actualPopSize && j < aSize; i++) { if (!researchers [i].alive) continue; researchers [i].f = a [j].f; // Update the personal best if (researchers [i].f > researchers [i].fb) { researchers [i].fb = researchers [i].f; ArrayCopy (researchers [i].b, researchers [i].x, 0, 0, WHOLE_ARRAY); } j++; } // 2. Submit results to journals for (int i = 0; i < actualPopSize; i++) { if (researchers [i].alive) SubmitToJournal (i); } // 3. Collect a global report and calculate available funds int availableFunds = 0; int reportSize = 0; // Preliminary calculation of the report size for (int i = 0; i < actualPopSize; i++) { if (!researchers [i].alive) continue; researchers [i].m--; // Spend 1 unit of funds per iteration availableFunds++; if (researchers [i].m > 0) reportSize++; else researchers [i].alive = false; } // Fill out the global report ArrayResize (globalReport, reportSize); int idx = 0; for (int i = 0; i < actualPopSize && idx < reportSize; i++) { if (researchers [i].alive && researchers [i].m > 0) { globalReport [idx].fitness = researchers [i].f; globalReport [idx].index = i; idx++; } } // 4. Quick sorting of a global report for (int i = 0; i < reportSize - 1; i++) { for (int j = i + 1; j < reportSize; j++) { if (globalReport [i].fitness < globalReport [j].fitness) { S_GlobalReport temp = globalReport [i]; globalReport [i] = globalReport [j]; globalReport [j] = temp; } } } // 5. Distribution of funds AssignFunds (availableFunds); // 6. Hire new researchers by existing ones for (int i = 0; i < actualPopSize; i++) { if (researchers [i].alive && researchers [i].m > 1) HireResearchers (i); } // 7. Update the direction and position for each researcher for (int i = 0; i < actualPopSize; i++) { if (!researchers [i].alive) continue; UpdateDirection (i); // Update position for (int c = 0; c < coords; c++) { researchers [i].x [c] += researchers [i].v [c]; // Boundary control if (researchers [i].x [c] < rangeMin [c]) researchers [i].x [c] = rangeMin [c]; if (researchers [i].x [c] > rangeMax [c]) researchers [i].x [c] = rangeMax [c]; researchers [i].x [c] = u.SeInDiSp (researchers [i].x [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } // 8. Update the diversity parameter UpdateOmega (); // 9. Population compactification CompactPopulation (); // 10. Copy positions to the array of agents to calculate 'fitness' ArrayResize (a, actualPopSize); idx = 0; for (int i = 0; i < maxPopSize && idx < actualPopSize; i++) { if (researchers [i].alive) { a [idx].Init (coords); ArrayCopy (a [idx].c, researchers [i].x, 0, 0, WHOLE_ARRAY); idx++; } } popSize = actualPopSize; // Update the population size } //————————————————————————————————————————————————————————————————————
UpdateDirection関数は、CoSOアルゴリズムにおいて、個々の研究者の移動ベクトルvを更新する役割を担います。この処理は、エージェント、つまり研究者が探索空間内を移動するための中心的な処理であり、自身の経験と他のエージェントから得られた情報の両方を利用しながら、新しい領域を探索できるようにします。この関数はidxパラメータを受け取ります。これは、どの研究者の移動方向を更新するのかを指定するインデックスです。
関数の冒頭では、0から1の範囲の2つの乱数が生成されます。beta1は個人的要素の係数として使用され、beta2は社会的要素の係数として使用されます。次に、社会的要素を計算します。このとき、各回の計算を開始する前にsocialComponent配列をリセットし、以前の計算結果が残らないようにします。
続いて、アルゴリズムで利用可能なすべての「学術誌(journals)」をループ処理します。そして、それぞれの学術誌について、エントリが存在するかどうか(length > 0)を確認します。空の学術誌からは社会的要素に利用できる情報を取得できないためです。学術誌が空でなければ、その中から1つのエントリ(entryIdx)をランダムに選択します。
各座標、つまり探索空間の各次元について、社会的要素を更新します。選択された学術誌のエントリが与える影響は、その学術誌を研究者が選択する確率(rho[j])に、学術誌内の位置と研究者の現在位置との差を掛けることで計算されます。これらの影響をすべての学術誌について合計することで、研究者の移動に対する社会的情報の全体的な影響が形成されます。社会的要素の計算が完了すると、研究者の移動ベクトルが更新されます。新しい移動ベクトル**v[c]**は、慣性要素、個人的要素、そして社会的要素の合計として計算されます。
それぞれの要素について見てみましょう。
慣性要素:omega * researchers[idx].v[c]。ここで、omegaは、Init関数で設定され、Moving関数で更新されるomegaCurrentパラメータであり、慣性係数を表します。この値によって、研究者が以前の移動方向をどの程度維持するかが決まります。値が大きい場合、研究者は同じ方向に移動し続ける傾向が強くなります。一方、値が小さい場合は、他の要素の影響を受けて、より早く方向を変えるようになります。
個人的要素:phi1 * beta1 * (researchers [idx]. b [c] — researchers [idx]. x [c])。ここで、phi1は個人的な経験に関連する加速係数であり、beta1は確率的な変動を導入する乱数係数です。
researchers[idx].b[c] - researchers[idx].x[c]は、研究者の現在位置から、その研究者自身がこれまでに見つけた最良の位置(fb)へ向かうベクトルを表します。したがって、この要素は、研究者が過去に良い結果を得た場所へ研究者を引き戻す働きをします。
- ある程度以前の移動を維持する
- 自分自身が見つけた最良の結果を「記憶」してその場所へ戻る
- 他の研究者が見つけた成功例を学習して追従する
この結果、UpdateDirection関数は研究者の新しい移動ベクトルを計算します。この移動ベクトルは、次のステップで研究者の探索空間内の位置を更新するために使用されます。
//———————————————————————————————————————————————————————————————————— void C_AO_CoSO::UpdateDirection (int idx) { double beta1 = u.RNDprobab (); double beta2 = u.RNDprobab (); // Social component ArrayInitialize (socialComponent, 0); for (int j = 0; j < journalsNum; j++) { if (journals [j].length > 0) { int entryIdx = u.RNDminusOne (journals [j].length); for (int c = 0; c < coords; c++) { socialComponent [c] += researchers [idx].rho [j] * (journals [j].entries [entryIdx].decision [c] - researchers [idx].x [c]); } } } // Update direction for (int c = 0; c < coords; c++) { researchers [idx].v [c] = omega * researchers [idx].v [c] + phi1 * beta1 * (researchers [idx].b [c] - researchers [idx].x [c]) + phi2 * beta2 * socialComponent [c]; } } //————————————————————————————————————————————————————————————————————
SubmitToJournal関数は、個々の研究者が現在の結果を利用可能な「学術誌」の1つに「発表」する過程を表しています。これは、CoSOアルゴリズムにおいて情報を広め、社会的な学習をおこなうための重要な仕組みです。この関数はidxパラメータを受け取ります。これは、自身の結果を学術誌に投稿しようとしている研究者のインデックスです。この関数の処理は、大きく2つの段階から構成されています。
学術誌の選択 。まず、補助関数であるSelectJournal関数が呼び出されます。この関数には、研究者のrho配列が入力として渡されます。rho配列は確率、あるいは選好を表しており、ある研究者が特定の学術誌に結果を投稿する可能性を決定します。SelectJournal関数は、これらの確率に基づいて利用可能な学術誌の1つをランダムに選択し、そのインデックス(journalIdx)を返します。
学術誌へのエントリの追加。学術誌が選択されると、その学術誌に対してAddメソッドが呼び出されます。このメソッドには2つのパラメータが渡されます。
- researchers[idx].f:研究者の現在の適応度値。その研究者が見つけた解の「品質」または「成功度」を表します。
- researchers [idx]. x:研究者の現在位置、つまり探索空間内の座標。これは、その適応度値を生み出した解そのものです。
選択された学術誌、つまりjournalIdxのインデックスを持つ学術誌にあるAddメソッドが、実際にこの情報を追加する役割を担います。このようにして、学術誌には、さまざまな研究者によって発見された集合的な知識や最良の成果が保存されます。
全体として、SubmitToJournalは科学的な研究成果の発表をシミュレートしています。研究者は自分の成果をどの学術誌に投稿するかを選択し、その成果が他の研究者から利用できるようになります。これによって、アルゴリズムは成功した解をエージェント集団の中に蓄積し、共有していくことができます。
//———————————————————————————————————————————————————————————————————— void C_AO_CoSO::SubmitToJournal (int idx) { int journalIdx = SelectJournal (researchers [idx].rho); journals [journalIdx].Add (researchers [idx].f, researchers [idx].x); } //————————————————————————————————————————————————————————————————————
SelectJournal関数は、指定された確率に基づいて、利用可能な複数の学術誌の中から1つを選択するために使用されます。これは、ルーレットホイール選択、あるいは比例選択と呼ばれる方法の実装であり、それぞれの学術誌が一定の「重み」または「選好」を持っています。この関数は1つのパラメータを受け取ります。probs[]は、それぞれの学術誌を選択するための確率、または相対的な選好を表す浮動小数点数の配列です。
この関数は次のように動作します。まず、0以上1未満の範囲で一様に分布する乱数rndを生成します。この値を使用して、どの学術誌を選択するかを決定します。cumSum変数は0で初期化され、probs配列を順番に処理する際に、確率の累積値を保持します。
極端なケースへの対応 :ループが終了しても学術誌が選択されなかった場合、最後の学術誌のインデックス(probsSize - 1)が返されます。このような状況は、計算時の極めて小さな精度誤差によって理論上発生する可能性があります。たとえば、rndが1になり、すべてのprobsの合計が正確に1になっている場合や、probsの合計がわずかに1未満になっている場合です。この処理によって、境界的なケースや特殊なケースであっても、必ず1つの学術誌が選択されることが保証されます。
本質的には、この関数は0から1までの数直線上に一連の「区間」を作成しており、それぞれの区間の長さは対応する学術誌の確率に比例しています。そして、そこに「ダーツ」を投げるように、乱数rndを生成し、それがどの区間に入るかを確認します。これにより、確率の高い学術誌ほど、より高い頻度で選択されるようになります。
//———————————————————————————————————————————————————————————————————— int C_AO_CoSO::SelectJournal (const double &probs []) { double rnd = u.RNDprobab (); double cumSum = 0; int probsSize = ArraySize (probs); for (int i = 0; i < probsSize; i++) { cumSum += probs [i]; if (rnd <= cumSum) return i; } return probsSize - 1; } //————————————————————————————————————————————————————————————————————
結論
CoSOアルゴリズムの理論について詳しく見てきたほか、アルゴリズムのメソッドの一部がコード内でどのように実装されているのかについても見てきました。当初の想定より内容量が大きくなったため、本記事は2部構成としました。アルゴリズムそのものが非常に興味深く、多面的であり、深く多層的なロジックを持っているため、注意深く検討する価値があります。
第2部では、具体的なメソッドについて引き続き説明するとともに、アルゴリズムをテストし、その性能についての結論を示します。
記事で使用されているプログラム
| # | 名前 | 種類 | 説明 |
|---|---|---|---|
| 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_CoSO.mq5 | スクリプト | CoSOテストスタンド |
MetaQuotes Ltdによってロシア語から翻訳されました。
元の記事: https://www.mql5.com/ru/articles/18886
警告: これらの資料についてのすべての権利はMetaQuotes Ltd.が保有しています。これらの資料の全部または一部の複製や再プリントは禁じられています。
この記事はサイトのユーザーによって執筆されたものであり、著者の個人的な見解を反映しています。MetaQuotes Ltdは、提示された情報の正確性や、記載されているソリューション、戦略、または推奨事項の使用によって生じたいかなる結果についても責任を負いません。
エラー 146 (「トレードコンテキスト ビジー」) と、その対処方法
取引におけるニューラルネットワーク:高精度分類のための効果的な特徴量抽出(最終章)
- 無料取引アプリ
- 8千を超えるシグナルをコピー
- 金融ニュースで金融マーケットを探索