English Русский Español Português
preview
Community of Scientists Optimization (CoSO): Theorie

Community of Scientists Optimization (CoSO): Theorie

MetaTrader 5Handel |
18 0
Andrey Dik
Andrey Dik

Inhalt

  1. Einführung
  2. Implementierung des Algorithmus
  3. Schlussfolgerung


Einführung

Im Rahmen unserer weiteren Untersuchung von Optimierungsmethoden werden wir in diesem Artikel einen weiteren Ansatz zur Lösung von Optimierungsproblemen betrachten – den CoSO-Algorithmus (Community of Scientists Optimization), der auf der Simulation der Mechanismen der wissenschaftlichen Gemeinschaft basiert. Im Gegensatz zu klassischen bioinspirierten Algorithmen bildet CoSO die einzigartigen Merkmale wissenschaftlicher Tätigkeit nach: das Veröffentlichen von Ergebnissen in Fachzeitschriften, den Wettbewerb um Fördermittel, die Bildung von Forschungsgruppen und das Ausbalancieren zwischen der vertieften Untersuchung bekannter Bereiche und der Suche nach grundlegend neuen Lösungen. Der CoSO-Algorithmus wurde 2012 von zwei Wissenschaftlern, A. Milani und V. Santucci, entwickelt und veröffentlicht.

Ein interessantes Merkmal dieses Ansatzes ist die natürliche Selbstorganisation des Suchprozesses. Wie eine echte wissenschaftliche Gemeinschaft konzentriert der Algorithmus Ressourcen auf vielversprechende Bereiche, speichert und verbreitet die besten Lösungen durch den Mechanismus der Fachzeitschriften und erhält die notwendige Vielfalt durch die Finanzierung von „Außenseitern“. Die dynamisch sich ändernde Populationsgröße ermöglicht es dem Algorithmus, sich an die Besonderheiten eines bestimmten Problems anzupassen, ohne dass eine Feinabstimmung der Parameter erforderlich ist, was eine weitere Neuerung darstellt, da in der Regel mit einer konstanten Populationsgröße gearbeitet wird. In diesem Artikel werden wir uns eingehend mit den mathematischen Grundlagen des Algorithmus, seinen Schlüsselkomponenten und den Interaktionsmechanismen zwischen ihnen befassen. 


Implementierung des Algorithmus

Der CoSO-Populationsalgorithmus basiert auf der Idee, das Verhalten von Forschern zu simulieren, die in einem mehrdimensionalen Raum nach optimalen Lösungen suchen, Wissen durch Veröffentlichungen in Fachzeitschriften austauschen und um begrenzte Ressourcen (Fördermittel) konkurrieren. Jeder Forscher ist ein Punkt im Suchraum. Er verfügt über: eine Position (an der er sucht), eine Bewegungsrichtung und Geldmittel für die Forschung. Wenn die Mittel aufgebraucht sind, wird der Forscher deaktiviert.

Jeder Forscher wird durch eine Reihe von Parametern repräsentiert: eine Position im Suchraum (x), eine Bewegungsrichtung (v), das persönlich beste Ergebnis (b), die Höhe der Mittel (m), eine Strategie zur Mittelverwaltung (s) und die Wahrscheinlichkeiten für die Auswahl verschiedener Fachzeitschriften (ρ).

Die Bewegung der Forscher wird durch die Formel zur Aktualisierung der Richtung bestimmt, die drei Komponenten kombiniert: Trägheit (die Tendenz, sich in die gleiche Richtung weiterzubewegen), eine kognitive Komponente (das Streben nach persönlichen Bestergebnissen) und eine soziale Komponente (den Einfluss der besten Ergebnisse aus Fachzeitschriften): 

v {i, t} = ω · v {i, t-1} + φ₁ · β₁ · (b {i, t-1} - x {i, t-1}) + φ₂ · β₂ · Σⱼ [ρᵢⱼ · (J {j,c} - x {i, t-1})], 

wobei ω = 0.7298 – Trägheitskoeffizient, φ₁ = φ₂ = 1.49618 – Beschleunigungskoeffizienten, β₁ und β₂ – Zufallszahlen aus [0,1], ρᵢⱼ – Wahrscheinlichkeit der Auswahl der Fachzeitschrift j durch Forscher i, J {j,c} – zufälliger Artikel aus der Fachzeitschrift j.

Nach der Aktualisierung der Richtung ändert sich die Position des Forschers gemäß einer einfachen Formel: x {i, t} = x {i, t-1} + v {i, t}

Die Fachzeitschriften speichern die besten gefundenen Lösungen. Forscher lesen Fachzeitschriften und bewegen sich auf gute Lösungen zu, und nur die besten Ergebnisse schaffen es in die Fachzeitschrift. Die Fachzeitschriften im Algorithmus dienen als kollektives Gedächtnis und speichern die k besten Lösungen, die von allen Forschern gefunden wurden. Wenn ein Forscher ein neues Ergebnis erhält, reicht er es gemäß seiner Wahrscheinlichkeitsverteilung ρ in einer der Fachzeitschriften ein, das das Ergebnis nur akzeptiert, wenn es besser ist als das schlechteste der bereits veröffentlichten. Dies stellt eine kontinuierliche Verbesserung der Qualität der für die Gemeinschaft verfügbaren Informationen sicher.

Erfolgreiche Forscher erhalten mehr Finanzmittel. Ein Teil des Geldes geht an „Außenseiter“ – neue Forscher an zufälligen Orten. Auf diese Weise wird vermieden, dass man in lokalen Optima stecken bleibt. Der Mechanismus zur Verteilung von Mitteln imitiert die wettbewerbsorientierte Finanzierung in der Wissenschaft. Bei jeder Iteration geben die Forscher eine Einheit an Mitteln aus, und der gesamte ausgegebene Geldbetrag wird basierend auf der Roulette-Auswahl auf Rangbasis neu verteilt. Die Wahrscheinlichkeit, dass ein Forscher an Position i im globalen Ranking Mittel erhält, wird durch die Formel bestimmt:

P (i) = (N - i + 1) / (N · (N + 1) / 2),

wobei N die Anzahl der Forscher ist.

Ein Teil der Ω-Mittel ist für die Schaffung von „Außenseitern“ reserviert – neuen Forschern an zufälligen Positionen. Der Ω-Parameter wird adaptiv geändert, um ein Gleichgewicht zwischen Exploration und Exploitation aufrechtzuerhalten. Wenn die Standardabweichung der Fitnesswerte der Population σ kleiner als die anfängliche σ₀ wird, signalisiert dies Konvergenz und der Anteil der Außenseiter steigt:

Ω {t} = Ω {t-1} + (Ω {max} - Ω {min}) / 2 · ε⁺ für σ < σ₀, ansonsten Ω {t} = Ω {t-1} - (Ω {max} - Ω {min}) / 2 · ε⁻,

wobei Ω {min} = 0.2, Ω {max} = 0.5, ε⁺ = 0.2, ε⁻ = 0.1.

Erfolgreiche Wissenschaftler können Assistenten einstellen, die ihre Suche in der Nähe ihres Vorgesetzten beginnen. Dies hilft dabei, vielversprechende Bereiche im Detail zu erforschen. Erfolgreiche Forscher mit Mitteln m > 1 können neue Forscher hinzufügen. In diesem Fall behält der Forscher einen Teil der Mittel für sich, entsprechend seiner Strategie, und gibt den Rest für Neuzugänge aus.

Neue Forscher übernehmen die Eigenschaften des Betreuers (supervisor) mit geringfügigen Abweichungen: Die Position wird initialisiert als x {new} = x {supervisor} + N (0, σv), die Strategie als s {new} = N (s {supervisor}, σs) und die Journalwahrscheinlichkeiten als ρ {new, j} = N (ρ {supervisor, j}, σρ) ρ {new, j} = N (ρ {supervisor, j},  σρ), gefolgt von einer Normalisierung.

Der Suchprozess im Algorithmus lässt sich mit einer Gruppe von Menschen vergleichen, die im Wald Pilze sammeln: Jeder sucht an seinem eigenen Platz, und wenn jemand eine Pilzstelle findet, ruft er den anderen zu, dann geht ein Teil der Gruppe zu dieser Stelle, und der Rest sucht weiter nach neuen Plätzen. Diejenigen, die lange Zeit nichts finden, scheiden aus, aber die erfolgreichsten Pilzsammler bringen Freunde zu den Pilzstellen.

Die Leistung des Algorithmus beruht auf einer Kombination von Mechanismen: Fachzeitschriften erleichtern den Informationsaustausch und lenken die Suche auf vielversprechende Bereiche, wettbewerbsorientierte Finanzierung erzeugt Selektionsdruck durch das Ausscheiden ineffektiver Forscher, die Einstellung von Assistenten stellt die lokale Suche um gute Lösungen herum sicher, und Außenseiter verhindern eine vorzeitige Konvergenz.

Die dynamisch sich ändernde Populationsgröße ermöglicht es dem Algorithmus, sich an die Komplexität des Problems anzupassen, indem die Anzahl der Forscher in vielversprechenden Bereichen erhöht und in weniger vielversprechenden Bereichen verringert wird. All diese Mechanismen arbeiten zusammen, um ein selbstorganisierendes System zu schaffen, das in der Lage ist, Probleme in komplexen mehrdimensionalen Räumen zu lösen. 

coso

Abb. 1. Der CoSO-Algorithmus in Aktion

Das Bild zeigt Folgendes:

  1. Der Suchraum, in dem sich die Forscher (grüne Kreise) in Richtung des Optimums bewegen; ihre Bewegungsvektoren sind dargestellt.
  2. Wissenschaftliche Fachzeitschriften speichern die besten gefundenen Lösungen, Forscher lesen diese und veröffentlichen ihre Ergebnisse.
  3. Mittelverteilung – ein Finanzierungsmechanismus in mehrdimensionalen Räumen mit dem adaptiven Parameter Ω für Außenseiter.
  4. Die Formel zur Aktualisierung der Richtung – die mathematische Grundlage für die Bewegung der Forscher.
  5. Die Schlüsselmechanismen – eine Legende mit Forschertypen: aktiv (mit Mitteln), inaktiv (ohne Mittel), Neuzugänge und Außenseiter mit zufälligen Positionen.

Die Pfeile zeigen die Informationsflüsse (grau), Bewegungen (blau) und Mittel (orange) an. Lassen Sie uns nun den Algorithmus in Pseudocode skizzieren.

1. INITIALISIERUNG:
– Erzeugung von 10 Forschern an zufälligen Positionen
– Jedem Forscher 15 Einheiten an Mitteln zuweisen (150 / 10)
– Jeder erhält:
     * Zufällige Sparstrategie (von 0 bis 1)
     * Zufällige Fachzeitschriftenpräferenzen
     * Anfängliche Bewegungsrichtung (leicht zufällig)
– 3 leere Fachzeitschriften erstellen
– Merke dir die anfängliche Populationsverteilung

2. HAUPTZYKLUS (wiederhole die angegebene Anzahl an Malen):
   
   2.1. BEWERTUNG DER ERGEBNISSE:
        Für jeden lebenden Forscher:
– Berechne die Qualität der aktuellen Position f(x)
– Wenn das Ergebnis besser ist als ein persönlicher Rekord:
* Aktualisiere den persönlichen Bestwert

   2.2. PUBLIKATION IN FACHZEITSCHRIFTEN:
Für jeden lebenden Forscher:
– Wähle eine Fachzeitschrift gemäß deinen persönlichen Präferenzen
– Versuche, das Ergebnis zu veröffentlichen:
* Die Fachzeitschrift akzeptiert das Ergebnis, wenn es in den Top 10 ist.
* Der schwächste Artikel wird entfernt

   2.3. KOSTENRECHNUNG UND BERICHTERSTATTUNG:
– Sammle alle ausgegebenen Mittel (1 von jedem)
– Erstelle ein Ranking der lebenden Forscher
– Markiere diejenigen als „tot“, denen das Geld ausgegangen ist

   2.4. MITTELVERTEILUNG:
        – Anteil für Außenseiter bestimmen (20-50%)
        – Den Rest unter den vorhandenen Forschern verteilen:
          * Je höher das Ranking, desto größer die Chancen
* Gewichtete Roulette-Auswahl verwenden

   2.5. AUSSENSEITER ERSTELLEN:
– Die zugewiesenen Mittel für 1-5 Neulinge ausgeben
– An zufälligen Positionen platzieren
– Gleiche Anteile des zugewiesenen Budgets geben

   2.6. ASSISTENTEN EINSTELLEN:
        Für jeden wohlhabenden Forscher (Mittel > 1):
– Einen Teil gemäß der Strategie für sich behalten
– 1-3 Assistenten für den Rest einstellen:
* Neben sich platzieren
* Eigene Erfahrung mit geringfügigen Änderungen teilen

   2.7. AUSWAHL DER BEWEGUNGSRICHTUNG:
Für jeden lebenden Forscher berechnen:
        
        New_direction = 
        0.7 × Old_direction +                                    // Trägheit
        1.5 × Randomness × (Personal_record - Position) +  // Zum eigenen Bestwert
        1.5 × Randomness × Sum_by_journals               // Zu den besten Lösungen der Fachzeitschriften
        (Journal_weight × (Journal_article - Position))

   2.8. BEWEGUNG:
        Für jeden lebenden Forscher:
– Einen Schritt machen: New_position = Old_position + Direction
– Wenn die Grenzen überschritten werden, zurückbewegen

   2.9. DIVERSITÄTSKONTROLLE:
– Messen der aktuellen Populationsstreuung
– Wenn die Streuung abgenommen hat (konvergierend):
          * Erhöhen des Anteils der Außenseiter um 10%
        – SONST:
* Verringern des Anteils der Außenseiter um 5%

   2.10. VERWALTUNG DER POPULATIONSGRÖSSE:
– Wenn mehr als 25 % tot sind oder insgesamt mehr als 200 vorhanden sind:
           * Entfernen der Toten aus dem Array
         – Wenn mehr als 150 übrig sind:
           * Nur die besten 150 behalten

3. ERGEBNIS:
   – Rückgabe der besten gefundenen Lösung

Kommen wir nun zur praktischen Umsetzung des CoSO-Algorithmus. Schreiben wir drei Datenstrukturen. Die erste Struktur, S_Journal_Entry, repräsentiert einen einzelnen Fachzeitschrifteneintrag, der Folgendes enthält:

  • fitness – ein numerischer Wert, der die „Qualität“ oder „Fitness“ eines gegebenen Eintrags widerspiegelt. 
  • decision[] - dynamisches Array, das die mit diesem Eintrag verbundenen Parameter speichert.
Die Init-Methode wird zur Initialisierung verwendet, indem die Größe des decision-Arrays festgelegt und der Fitness ein anfänglicher Minimalwert zugewiesen wird. Die zweite Struktur, S_Journal, repräsentiert die Fachzeitschrift selbst – eine geordnete Sammlung von Einträgen, die durch Folgendes gekennzeichnet ist:
  • entries [] – dynamisches Array von S_Journal_Entry-Einträgen.
  • length – aktuelle Anzahl der Einträge in der Fachzeitschrift.
  • maxLength – maximale Anzahl an Datensätzen, die die Fachzeitschrift speichern kann.

Die Init-Methode initialisiert die Fachzeitschrift, indem sie die maximale Länge festlegt, Speicher für Einträge zuweist und jeden Eintrag mit einem minimalen Fitnesswert initialisiert. Die Add-Methode wird verwendet, um einen neuen Eintrag zur Fachzeitschrift hinzuzufügen. Die Besonderheit dieser Methode besteht darin, dass sie die Fachzeitschrift nach Fitnesswerten sortiert hält. Wenn ein neuer Eintrag schlechter ist als der schlechteste Eintrag in einer bereits gefüllten Fachzeitschrift, wird er nicht hinzugefügt. Andernfalls wird mittels binärer Suche Platz für den neuen Eintrag gefunden, dann werden bestehende Einträge verschoben, um Platz zu schaffen, und der neue Eintrag wird eingefügt; wenn die Fachzeitschrift noch nicht voll ist, wird seine „length“ erhöht.

Die dritte Struktur, S_Researcher, beschreibt den „Forscher“ oder „Agenten“. Sie enthält eine Reihe von Parametern:

  • x [] – aktuelle Position des Forschers (Array von Koordinaten)
  • v [] – Richtungs- oder Geschwindigkeitsvektor der Bewegung des Forschers
  • b [] – die beste Position, die vom jeweiligen Forscher individuell gefunden wurde
  • rho [] – Array von Publikationswahrscheinlichkeiten in verschiedenen Fachzeitschriften. Der Forscher interagiert mit mehreren „Fachzeitschriften“ oder Informationsquellen
  • s – Strategie zur Mittelverwaltung
  • m – Höhe der Mittel (Ganzzahl)
  • f – Fitness der aktuellen Forscherposition
  • fb – Fitness der besten vom Forscher gefundenen Position
  • alive – Flag, das die Aktivität des Forschers anzeigt
Die Methode Init initialisiert den Forscher, indem sie die Größen der Arrays für Positionen, Geschwindigkeiten, beste Positionen und Publikationswahrscheinlichkeiten festlegt und den verbleibenden Parametern Anfangswerte zuweist. Die vorgestellten Datenstrukturen sind Komponenten zur Implementierung einer Methode, bei der „Forscher“ nach optimalen Lösungen suchen und „Fachzeitschriften“ verwenden, um die gefundenen besten Lösungen zu speichern und zu teilen. 
//————————————————————————————————————————————————————————————————————
// 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;
    }
};

Lassen Sie uns die Klasse C_AO_CoSO schreiben, die eine Implementierung des Optimierungsalgorithmus „Community of Scientists Optimization“ (CoSO) ist. Die Klasse C_AO_CoSO erbt von der Basisklasse C_AO und setzt das Vorhandensein gemeinsamer Eigenschaften und Methoden für verschiedene Optimierungsalgorithmen voraus.

Standardwerte für verschiedene CoSO-Steuerungsparameter werden festgelegt:

  1. Anfangsgröße der Population der „Forscher“;
  2. Gesamtmenge an „Geldmitteln“ (Ressourcen), die zugewiesen werden können (in der Metapher der Wissenschaftler könnten dies Zuschüsse sein, die sie für ihre „Arbeit“ (Suche nach einer Lösung) verwenden);
  3. Anzahl der „Fachzeitschriften“, in denen Forscher ihre „Entdeckungen“ (beste Lösungen) veröffentlichen können;
  4. maximale Anzahl von Artikeln/Entdeckungen, die eine „Fachzeitschrift“ enthalten kann;
  5. „Trägheits“-Parameter, der den Einfluss der vorherigen Bewegungsrichtung steuert.

  • phi1 = 1,5 – kognitiver Parameter spiegelt den individuellen Einfluss auf die Bewegung des Forschers wider (das Streben nach eigenen besten Entdeckungen);
  • phi2 = 1,5 – sozialer Parameter bestimmt, wie stark sich der Forscher auf Veröffentlichungen in Fachzeitschriften konzentriert;
  • omegaMin = 0,2 – Mindestprozentsatz an „Außenseitern“;
  • omegaMax = 0,5 – Maximalprozentsatz an „Außenseitern“;
  • epsilonPlus = 0,2 – Schritt zur Erhöhung der Diversität;
  • epsilonMinus = 0,1 – Schritt zur Verringerung der Diversität.
SetParams() – Methode zum Festlegen interner Klassenparameter basierend auf dem externen „params“-Array. 
Init() – Methode zur Initialisierung des Algorithmus. Sie akzeptiert Suchbereiche (Min-, Max-Werte) und Schritte für jeden Parameter sowie die Anzahl der EpochenP.
Moving() – Methode dient dem „Bewegen“ (Aktualisieren der Positionen) der Forscher im Suchraum, basierend auf ihren „Geschwindigkeiten“ und „Richtungen“.
Revision() – Methode veranlasst die Aktualisierung der besten globalen Lösung, die bis zum aktuellen Zeitpunkt von allen Forschern gefunden wurde.

Private Felder: dynamisches Array von S_Researcher-Strukturen. Jedes Element dieser Struktur repräsentiert einen „Forscher“ und enthält Informationen über dessen aktuelle Position, Geschwindigkeit, gefundene beste Lösung, „alive“, „fitness“ (f) usw. Das dynamische Array von S_Journal-Strukturen, wobei jede Struktur eine Sammlung der besten Lösungen speichert, die von Forschern in einer bestimmten „Fachzeitschrift“ veröffentlicht wurden. Aktueller Prozentsatz an „Außenseitern“. Anfängliche Standardabweichung. Aktuelle tatsächliche Populationsgröße. Sie kann sich während der Ausführung des Algorithmus ändern. Maximal zulässige Populationsgröße. 

struct S_GlobalReport – eine verschachtelte Struktur zum Speichern von Informationen über die beste globale Lösung: „fitness“ (der Wert der Zielfunktion) und „index“ (der Index des Forschers, der diese Lösung gefunden hat).

S_GlobalReport globalReport [] – Array zum Speichern globaler Berichte. 

Private Algorithmusmethoden:

  • UpdateDirection() – Aktualisiert die Bewegungsrichtung (oder „Geschwindigkeit“) für einen bestimmten Forscher mit dem Index idx.
  • SubmitToJournal() – Ermöglicht es einem Forscher mit dem Index, seine aktuell beste Lösung in einem der „Fachzeitschriften“ zu „veröffentlichen“.
  • AssignFunds() – Methode zur Verteilung von „Mitteln“ unter Forschern. In CoSO können Zuschüsse das Verhalten oder die Fähigkeit von Forschern zur Suche beeinflussen.
  • HireResearchers() – Methode erstellt neue Forscher.
  • CreateOutsiders() – erstellt „Außenseiter“, spezielle Forscher, die aggressivere oder zufälligere Suchen durchführen können, um zu vermeiden, in lokalen Optima stecken zu bleiben.
  • ComputeStdDev() – Berechnet die Standardabweichung einer Population und dient als Indikator für die „Vielfalt“ oder „Konvergenz“ einer Population.
  • UpdateOmega() – Aktualisiert den aktuellen Parameter omegaCurrent.
  • SelectJournal() – Wählt eine Fachzeitschrift für die Veröffentlichung basierend auf den angegebenen Wahrscheinlichkeiten aus.
  • NormalizeProbabilities() – Normalisiert das Array der Wahrscheinlichkeiten, sodass deren Summe eins ergibt.
  • CompactPopulation() – Methode zum „Kompaktieren“ einer Population, bei der die am wenigsten effektiven Forscher entfernt werden, d. h. Verwaltung der Populationsgröße.
//————————————————————————————————————————————————————————————————————
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/en/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 ();
};
//————————————————————————————————————————————————————————————————————

Die Init-Methode ist Teil der Klasse C_AO_CoSO und ist dafür verantwortlich, alle notwendigen Strukturen und Parameter vorzubereiten, bevor der Algorithmus ausgeführt wird. Zuerst erfolgt eine Standardinitialisierung in Bezug auf die Bereiche der Suchparameter (Minimal-, Maximalwerte und Schrittweite), wobei die Anzahl der Epochen (epochsP) festgelegt wird. Wenn diese Standardinitialisierung fehlschlägt, gibt die Funktion „false“ zurück. Anschließend werden die internen Zustandsvariablen des Algorithmus initialisiert:

  • actualPopSize – aktuelle Größe der Forschungspopulation,
  • maxPopSize – maximal zulässige Populationsgröße (oder der für Forscher zugewiesene Speicher),
  • sigma0 – Anfangswert der Standardabweichung, der später berechnet wird,
  • omegaCurrent – aktueller „omega“-Parameter, initialisiert als arithmetisches Mittel zwischen omegaMin und omegaMax.

Als Nächstes folgt die Überprüfung der Gültigkeit der Eingabeparameter:

  • totalFunds – der Gesamtbetrag der „Mittel“ sollte nicht geringer sein als die Populationsgröße; jedem Forscher sollte ein bestimmter Betrag an Mitteln oder Zuschüssen zugewiesen werden;
  • journalsNum – die Anzahl der „Fachzeitschriften“ sollte mindestens eins betragen;
  • journalLen – die Länge jeder „Fachzeitschrift“ sollte ebenfalls mindestens 1 betragen;
  • omega (globaler Parameter) – sollte im Bereich von 0,0 bis 1,0 liegen.

Nach der Überprüfung der Parameter erfolgt ein „vollständiges Löschen vorheriger Durchläufe“, was bedeutet, dass die Größen der dynamischen Arrays „researchers“, „journals“, „globalReport“ und „socialComponent“ auf null zurückgesetzt werden; der Algorithmus startet immer von einem sauberen Zustand aus. Die bereits erwähnten Zustandsvariablen (actualPopSize, maxPopSize, sigma0) werden erneut zurückgesetzt.

Dann werden die „Fachzeitschriften“ initialisiert: Das Array journals wird entsprechend journalsNum in der Größe angepasst. Jede Fachzeitschrift in diesem Array wird mit einer vorgegebenen Länge journalLen und einer vorgegebenen Anzahl an coords-Koordinaten initialisiert.

Initialisierung der Forscher. maxPopSize wird auf mindestens das Dreifache von popSize (der Populationsgröße) gesetzt. Dies schafft eine „Reserve“ für mögliche Änderungen der Populationsgröße (der Algorithmus erstellt neue Forscher). fundsPerResearcher wird berechnet – der Geldbetrag, der jedem Forscher zugewiesen wird (der Gesamtbetrag der Mittel wird durch die Populationsgröße geteilt). Als Nächstes folgt ein Durchlauf durch alle Forscher.

Jeder Forscher wird mit einer Anzahl von Koordinaten coords und der Anzahl journalsNum an Fachzeitschriften initialisiert. Das alive-Flag wird nur für die ersten popSize-Forscher auf true gesetzt, die übrigen sind noch inaktiv. Aktiven Forschern wird der berechnete Geldbetrag fundsPerResearcher zugewiesen. Ihre Fondsmanagement-Strategie s wird zufällig initialisiert. Ihre Positionen x, besten persönlichen Positionen b und Bewegungsvektoren v werden initialisiert. Die Positionen von x und b werden zufällig im vorgegebenen Bereich gesetzt (rangeMin - rangeMax, unter Berücksichtigung von rangeStep). Die Bewegungsvektoren v werden auf einen kleinen Zufallswert aus einer Normalverteilung initialisiert. Die rho-Wahrscheinlichkeiten für jede Fachzeitschrift werden auf Zufallswerte initialisiert.

Die Funktion NormalizeProbabilities wird aufgerufen, welche diese Wahrscheinlichkeiten normalisiert. Nach der Initialisierung aller Forscher wird die anfängliche Standardabweichung sigma0 berechnet, welche die Streuung der Forscherpositionen über alle Koordinaten hinweg charakterisiert. Falls sigma0 gleich null ist (zum Beispiel, wenn sich alle Forscher am selben Punkt befinden), wird es auf 1.0 gesetzt, um eine Division durch 0 zu verhindern. Der Parameter omegaCurrent wird erneut auf den Durchschnitt zwischen omegaMin und omegaMax gesetzt.

Am Ende werden die Positionen aller initialisierten Forscher (popSize) in das „Standard-Agenten-Array“ von a kopiert. Somit bereitet die Init-Funktion die Umgebung vollständig für den Betrieb des Optimierungsalgorithmus vor, indem sie alle „Forscher“-Agenten, deren Parameter, „Fachzeitschriften“ zum Speichern der Ergebnisse sowie interne Parameter zur Steuerung des Algorithmus initialisiert.

//————————————————————————————————————————————————————————————————————
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;
}
//————————————————————————————————————————————————————————————————————

Die Moving-Methode ist der zentrale Teil des iterativen Prozesses des Algorithmus; sie führt einen Suchschritt in Richtung einer optimalen Lösung aus und simuliert das Verhalten der „Forscher“ sowie deren Interaktion. Zu Beginn der Funktion wird, falls dies der erste Aufruf nach der Initialisierung ist (revision = false), einfach „revision“ auf true gesetzt und die Funktion beendet. Im Folgenden sind die Hauptschritte des Algorithmus aufgeführt, die bei jeder Iteration ausgeführt werden.

Aktualisierung der Fitness der Forscher. Für jeden aktiven Forscher (aus der aktuellen Population) wird dessen Fitnesswert aus dem allgemeinen a-Array entnommen, das die Ergebnisse der Fitnessbewertung aller Agenten speichert. Anschließend wird geprüft, ob die aktuelle Fitness des Forschers besser ist als seine zuvor aufgezeichnete beste persönliche Fitness; falls dies der Fall ist, wird fb aktualisiert und die beste Position b des Forschers wird ebenfalls als dessen aktuelle Position x gespeichert.

Einreichen der Ergebnisse bei Fachzeitschriften. Jeder aktive Forscher „reicht“ seine aktuellen Ergebnisse (aktuelle Fitness und Position) bei einer der „Fachzeitschriften“ ein. Die Details dieses Prozesses sind in der Funktion SubmitToJournal gekapselt, die basierend auf den rho-Wahrscheinlichkeiten des Forschers eine Fachzeitschrift auswählt und das Ergebnis dort hinzufügt, sofern es gut genug ist.

Erstellen eines globalen Berichts und Berechnung der verfügbaren Mittel. In diesem Schritt wird der Status der Forscher neu bewertet und die Ressourcen für die Verteilung werden vorbereitet. Für jeden lebenden Forscher verringert sich die Anzahl seiner „Mittel“ (m) um eins, was die Kosten der aktuellen Iteration symbolisiert. Der Gesamtbetrag der availableFunds nimmt zu. Wenn der Forscher nach der Verwendung der Mittel noch über Restmittel verfügt (m > 0), gilt er als arbeitsfähig und wird in den „globalen Bericht“ aufgenommen. Wenn die Mittel aufgebraucht sind, „stirbt“ der Forscher (alive = false). Es wird ein globalReport erstellt – ein Array, das die Fitness und den Index nur derjenigen Forscher enthält, die aktiv bleiben und über Mittel verfügen.

Sortieren des globalen Berichts. Die Elemente im globalReport werden mithilfe eines einfachen Bubble-Sort-Algorithmus nach absteigendem „Fitness“-Wert (von am besten bis am schlechtesten) sortiert. Dies ermöglicht es, die erfolgreichsten Forscher schnell zu identifizieren.

Mittelverteilung. Die Funktion AssignFunds wird aufgerufen, um die availableFunds unter den aktiven Forschern neu zu verteilen, wobei die besten Forscher (wie durch globalReport bestimmt) mehr Mittel erhalten, was ihre weitere Aktivität fördert, während weniger erfolgreiche Forscher möglicherweise weniger oder gar nichts erhalten, was in zukünftigen Iterationen potenziell zu ihrem „Tod“ führen kann.

Einstellung neuer Forscher durch bestehende. Aktuelle Forscher, die noch über ausreichende Mittel verfügen (m > 1), können neue Forscher „einstellen“. Dies ist ein Mechanismus zur Vergrößerung der Population oder zur Schaffung neuer Agenten auf der Grundlage erfolgreicher bestehender Agenten. Details finden Sie in der Methode HireResearchers.

Aktualisierung von Richtung und Position. Für jeden aktiven Forscher wird die Funktion UpdateDirection aufgerufen, die den neuen Bewegungsvektor v des Forschers auf der Grundlage seines persönlichen besten Ergebnisses b, des besten Ergebnisses aus den Fachzeitschriften, der sozialen Komponente oder anderer Algorithmusfaktoren berechnet. Die Position x des Forschers wird durch Addition des Bewegungsvektors v aktualisiert. Die Position wird auf Bereichsüberschreitungsfehler geprüft und gegebenenfalls angepasst sowie auf den nächsten akzeptablen rangeStep „diskretisiert“ oder „gerundet“.

Aktualisierung des Diversitätsparameters. Die Funktion UpdateOmega wird aufgerufen, die den Parameter omegaCurrent des Algorithmus anpasst. Dieser Parameter wird verwendet, um das Gleichgewicht zwischen Exploration (Finden neuer Bereiche) und Exploitation (Verfeinerung der gefundenen Lösungen) zu steuern, wobei er während des Laufs des Algorithmus dynamisch geändert wird, um die Populationsdiversität aufrechtzuerhalten.

Populationsverdichtung. Die Funktion CompactPopulation wird aufgerufen, um inaktive (alive = false) Forscher aus dem „researchers“-Array zu entfernen, wodurch dessen Größe effektiv reduziert und Speicher freigegeben wird.

Kopieren der Positionen in das Agents-Array. Schließlich werden die aktuellen Positionen aller aktiven Forscher in das a-Array kopiert. Die Größe von a wird entsprechend actualPopSize aktualisiert. Die globale Variable popSize wird ebenfalls auf die neue actualPopSize aktualisiert.

    Somit kapselt die Moving-Funktion den vollständigen Lebenszyklus einer Iteration des CoSO-Algorithmus, einschließlich Bewertung, sozialer Interaktion („Fachzeitschriften“), Ressourcenmanagement, dynamischer Populationsänderung und Agentenbewegung im Suchraum.

    //————————————————————————————————————————————————————————————————————
    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
    }
    //————————————————————————————————————————————————————————————————————
    

    Die Funktion UpdateDirection ist für die Aktualisierung des Bewegungsvektors von v für einen einzelnen Forscher im CoSO-Algorithmus verantwortlich. Dieser Prozess ist entscheidend für die Bewegung von Agenten (Forschern) im Suchraum und ermöglicht es ihnen, neue Bereiche zu erkunden und Informationen zu nutzen, die sowohl persönlich als auch von anderen Agenten gewonnen wurden. Die Funktion erhält den Parameter idx, also den Index des Forschers, dessen Richtung aktualisiert werden soll.

    Zu Beginn der Funktion werden zwei Zufallsvariablen zwischen 0 und 1 generiert: beta1, das als Koeffizient für die persönliche Komponente verwendet wird, und beta2, das als Koeffizient für die soziale Komponente verwendet wird. Als Nächstes berechnen wir eine soziale Komponente, während das socialComponent-Array zurückgesetzt wird, um sicherzustellen, dass es vor jeder neuen Berechnung sauber ist.

    Die Schleife durchläuft dann alle dem Algorithmus zur Verfügung stehenden „Fachzeitschriften“ und prüft für jede Fachzeitschrift, ob sie Einträge enthält (Länge > 0), da eine leere Fachzeitschrift keine Informationen für die soziale Komponente liefern kann. Wenn die Fachzeitschrift nicht leer ist, wird ein Eintrag (entryIdx) zufällig daraus ausgewählt. 

    Für jede Koordinate (Dimension des Suchraums) wird die soziale Komponente aktualisiert. Der Beitrag eines ausgewählten Fachzeitschriftenintrags wird berechnet als die Wahrscheinlichkeit, dass ein Forscher die Fachzeitschrift auswählt (rho[j]) * (Position des ausgewählten Artikels in der Fachzeitschrift – aktuelle Position des Forschers). Diese Beiträge werden über alle Fachzeitschriften hinweg summiert, um den Gesamteinfluss der sozialen Informationen auf die Bewegung der Forscher zu bilden. Nach der Berechnung der sozialen Komponente wird der Bewegungsvektor des Forschers, (v [c]) = Trägheitskomponente + persönliche Komponente + soziale Komponente, aktualisiert.

    Analysieren wir jeden Teil:

    Trägheitskomponente: omega * researchers [idx]. v [c]. Hier ist omega der Trägheitskoeffizient. Er bestimmt, wie stark der Forscher seine bisherige Bewegungsrichtung beibehält. Ein großer Wert bedeutet, dass der Forscher sich weiterhin in dieselbe Richtung bewegt; ein kleiner Wert bedeutet, dass er unter dem Einfluss anderer Faktoren schneller die Richtung ändert.

    Persönliche Komponente: phi1 * beta1 * (researchers [idx]. b [c] – researchers [idx]. x [c]), wobei phi1 ein Beschleunigungsfaktor ist, der mit der persönlichen Erfahrung verbunden ist, und beta1 ein Zufallsfaktor, der Stochastik hinzufügt.
    (researchers [idx]. b [c] – researchers [idx]. x [c]) ist ein Vektor, der von der aktuellen Position des Forschers auf dessen beste bisher gefundene persönliche Position (fb) zeigt. Diese Komponente zieht den Forscher zurück zu seinen eigenen erfolgreichen Orten.

    Soziale Komponente: phi2 * beta2 * socialComponent [c], wobei phi2 der Beschleunigungsfaktor ist, der mit der sozialen Erfahrung (Informationen von anderen) verbunden ist, beta2 ein Zufallsfaktor ist, der Stochastik hinzufügt, und socialComponent[c] der zuvor berechnete Vektor ist, der den Einfluss zufällig ausgewählter Entscheidungen aus allen Fachzeitschriften berücksichtigt. Diese Komponente zieht den Forscher in Richtung der vom Team gefundenen erfolgreichen Positionen. Die Kombination dieser drei Komponenten ermöglicht es dem Forscher, gleichzeitig:
    1. eine gewisse Trägheitsbewegung beibehalten;
    2. sich an die eigenen besten Funde „erinnern“ und zu diesen zurückkehren;
    3. die erfolgreichen Ergebnisse anderer Forscher untersuchen und ihnen folgen.

    Infolgedessen berechnet „UpdateDirection“ einen neuen Bewegungsvektor für den Forscher, der im nächsten Schritt verwendet wird, um dessen Position im Suchraum tatsächlich zu aktualisieren.

    //————————————————————————————————————————————————————————————————————
    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];
      }
    }
    //————————————————————————————————————————————————————————————————————
    

    Die Funktion „SubmitToJournal“ beschreibt den Prozess, bei dem ein einzelner Forscher seine aktuellen Ergebnisse in einer der verfügbaren „Fachzeitschriften“ „veröffentlicht“. Dies ist ein Schlüsselmechanismus für die Informationsverbreitung und das soziale Lernen innerhalb des CoSO-Algorithmus. Die Funktion verwendet den Parameter idx – den Index des Forschers, der seine Ergebnisse bei der Fachzeitschrift einreichen möchte. Der Funktionsablauf besteht aus zwei Hauptschritten:

    Auswahl einer Fachzeitschrift. Zunächst wird die Hilfsfunktion „SelectJournal“ aufgerufen. Diese Funktion verwendet das Array rho des Forschers als Eingabe. Das rho-Array (das Wahrscheinlichkeiten oder Präferenzen darstellt) bestimmt, wie wahrscheinlich es ist, dass ein bestimmter Forscher seine Ergebnisse bei einer bestimmten Fachzeitschrift einreicht. Die Funktion SelectJournal wählt basierend auf diesen Wahrscheinlichkeiten zufällig eine der verfügbaren Fachzeitschriften aus und gibt deren Index (journalIdx) zurück.

    Hinzufügen eines Eintrags zu einer Fachzeitschrift. Sobald eine Fachzeitschrift ausgewählt ist, wird die Add-Methode für diese aufgerufen. Der Methode werden zwei Parameter übergeben:

    • researchers [idx]. f – aktueller Fitnesswert des Forschers. Dies ist die „Qualität“ oder der „Erfolg“ der gefundenen Lösung.
    • researchers [idx]. x – aktuelle Position (Koordinaten im Suchraum) des Forschers. Dies ist die Lösung, die diesen Fitnesswert erzeugt hat.

      Die Add-Methode in der ausgewählten Fachzeitschrift (der Fachzeitschrift mit dem Index journalIdx) ist dafür verantwortlich, diese Informationen tatsächlich hinzuzufügen. Somit bewahrt die Fachzeitschrift kollektives Wissen oder bewährte Verfahren, die von verschiedenen Forschern entdeckt wurden.

      Im Allgemeinen simuliert SubmitToJournal die Veröffentlichung wissenschaftlicher Ergebnisse, wobei Forscher wählen, wo sie ihre Arbeit einreichen, und diese Arbeit dann für andere sichtbar wird. Dies ermöglicht es dem Algorithmus, erfolgreiche Lösungen innerhalb der Agentenpopulation anzusammeln und zu verteilen.

      //————————————————————————————————————————————————————————————————————
      void C_AO_CoSO::SubmitToJournal (int idx)
      {
        int journalIdx = SelectJournal (researchers [idx].rho);
        journals [journalIdx].Add (researchers [idx].f, researchers [idx].x);
      }
      //————————————————————————————————————————————————————————————————————
      

      Die Funktion SelectJournal ist darauf ausgelegt, basierend auf festgelegten Wahrscheinlichkeiten eine Fachzeitschrift aus einer Menge verfügbarer Fachzeitschriften auszuwählen. Dies ist eine Implementierung der Roulette-Selektion oder proportionalen Selektion, bei der jede Fachzeitschrift ein bestimmtes „Gewicht“ oder eine bestimmte „Präferenz“ hat. Die Funktion nimmt einen Parameter entgegen: probs[] – ein Array von Gleitkommazahlen, die die Wahrscheinlichkeiten oder relativen Präferenzen für die Auswahl der jeweiligen Fachzeitschrift darstellen.

      Die Funktion arbeitet wie folgt: Zuerst wird eine Zufallszahl rnd generiert, die gleichmäßig zwischen 0 (einschließlich) und 1 (ausschließlich) verteilt ist; diese wird verwendet, um zu bestimmen, welche Fachzeitschrift ausgewählt wird. Die Variable cumSum wird auf null initialisiert und akkumuliert die Summen der Wahrscheinlichkeiten, während sie das probs-Array durchläuft.

        Iteration über Wahrscheinlichkeiten. Die Funktion beginnt, die Elemente des probs-Arrays nacheinander vom ersten bis zum letzten zu durchlaufen. In jeder Iteration wird die aktuelle Wahrscheinlichkeit probs zu cumSum addiert. Somit ist cumSum die Summe der Wahrscheinlichkeiten vom Anfang des Arrays bis zur aktuellen Position von i. Die generierte Zufallszahl rnd wird mit dem aktuellen cumSum verglichen. Wenn rnd kleiner oder gleich cumSum ist, bedeutet dies, dass die Zufallszahl in das Intervall „gefallen“ ist, das der aktuellen Fachzeitschrift zugeordnet ist (d. h. der Fachzeitschrift mit dem Index i). In diesem Fall gibt die Funktion sofort den Index i zurück.

        Behandlung eines Extremfalls. Wenn die Schleife endet und keine Fachzeitschrift ausgewählt wurde (was theoretisch aufgrund extrem geringer Genauigkeit der Berechnungen passieren könnte, wenn rnd 1 ist und die Summe aller probs genau 1 ergibt, oder wenn die Summe der probs geringfügig kleiner als 1 ist), gibt die Funktion den Index der letzten Fachzeitschrift (probsSize - 1) zurück. Dies stellt sicher, dass die Fachzeitschrift immer ausgewählt wird, selbst in Grenz- oder pathologischen Fällen.

          Im Wesentlichen erstellt diese Funktion eine Reihe von „Slots“ auf dem Zahlenstrahl von 0 bis 1, wobei die Länge jedes Slots proportional zur Wahrscheinlichkeit der entsprechenden Fachzeitschrift ist. Dann wirft sie einen „Pfeil“ (rnd-Zufallszahl) und beobachtet, in welchem Bereich er landet. Dies stellt sicher, dass Fachzeitschriften mit höheren Wahrscheinlichkeiten häufiger ausgewählt werden.

          //————————————————————————————————————————————————————————————————————
          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;
          }
          //————————————————————————————————————————————————————————————————————
          


          Schlussfolgerung

          Wir haben uns die Theorie des CoSO-Algorithmus sowie einen Teil der Implementierung der Algorithmus-Methoden im Code im Detail angesehen. Der Umfang des Materials erwies sich als deutlich größer als zu Beginn der Arbeit erwartet, und ich habe beschlossen, den Artikel in zwei Teile aufzuteilen. Der Algorithmus selbst ist äußerst interessant und vielschichtig, mit einer tiefen, mehrschichtigen Logik, die eine sorgfältige Betrachtung verdient.

          Im zweiten Teil werde ich die spezifischen Methoden weiter beschreiben sowie den Algorithmus testen und Schlussfolgerungen zu seiner Leistung präsentieren.

          Im Artikel verwendete Programme

          # Name Typ Beschreibung
          1 #C_AO.mqh
          Include
          Übergeordnete Klasse von Populationsoptimierungsalgorithmen
          2 #C_AO_enum.mqh
          Include
          Enumeration der Algorithmen zur Populationsoptimierung
          3 TestFunctions.mqh
          Include
          Bibliothek mit Testfunktionen
          4
          TestStandFunctions.mqh
          Include
          Bibliothek mit Funktionen für die Testumgebung
          5
          Utilities.mqh
          Include
          Bibliothek mit Hilfsfunktionen
          6
          CalculationTestResults.mqh
          Include
          Skript zur Berechnung der Ergebnisse in der Vergleichstabelle
          7
          Testing AOs.mq5
          Skript Die einheitliche Testumgebung für alle Algorithmen zur Populationsoptimierung
          8
          Simple use of population optimization algorithms.mq5
          Skript
          Ein einfaches Beispiel für die Verwendung von Algorithmen zur Populationsoptimierung ohne Visualisierung
          9
          Test_AO_CoSO.mq5
          Skript CoSO-Testumgebung

          Übersetzt aus dem Russischen von MetaQuotes Ltd.
          Originalartikel: https://www.mql5.com/ru/articles/18886

          Beigefügte Dateien |
          CoSO.zip (257.36 KB)
          Die Übertragung der Trading-Signale in einem universalen Expert Advisor. Die Übertragung der Trading-Signale in einem universalen Expert Advisor.
          In diesem Artikel wurden die verschiedenen Möglichkeiten beschrieben, um die Trading-Signale von einem Signalmodul des universalen EAs zum Steuermodul der Positionen und Orders zu übertragen. Es wurden die seriellen und parallelen Interfaces betrachtet.
          Von der Grundstufe zur Mittelstufe: FileSave und FileLoad Von der Grundstufe zur Mittelstufe: FileSave und FileLoad
          Im heutigen Artikel werden wir uns verschiedene Möglichkeiten ansehen, mit den Bibliotheksfunktionen FileSave und FileLoad zu arbeiten. Obwohl viele Leute sie aufgrund bestimmter Einschränkungen oder Schwierigkeiten, die sie in spezifischen Szenarien verursachen, für nur begrenzt nutzbar halten, kann uns ein korrektes Verständnis der Funktionsweise dieser beiden Funktionen an manchen Stellen viel Aufwand ersparen. Sie sind auch eine hervorragende Möglichkeit, mit Protokolldateien zu arbeiten.
          Eine alternative Log-datei mit der Verwendung der HTML und CSS Eine alternative Log-datei mit der Verwendung der HTML und CSS
          In diesem Artikel werden wir eine sehr einfache, aber leistungsfähige Bibliothek zur Erstellung der HTML-Dateien schreiben, dabei lernen wir auch, wie man eine ihre Darstellung einstellen kann (nach seinem Geschmack) und sehen wir, wie man es leicht in seinem Expert Advisor oder Skript hinzufügen oder verwenden kann.
          Quantenneuronales Netzwerk in MQL5 (Teil II): Training eines neuronalen Netzwerkes mit Backpropagation unter Verwendung von ALGLIB-Markov-Matrizen Quantenneuronales Netzwerk in MQL5 (Teil II): Training eines neuronalen Netzwerkes mit Backpropagation unter Verwendung von ALGLIB-Markov-Matrizen
          Der Artikel präsentiert eine innovative Architektur für ein quantenneuronales Netz für den algorithmischen Handel, die die Prinzipien der Quantenmechanik mit modernen Methoden des maschinellen Lernens kombiniert. Das System umfasst Quanteneffekte (Resonanz, Interferenz, Dekohärenz), mehrstufiges Gedächtnis verschiedener Zeitskalen, Markov-Ketten mit der ALGLIB-Bibliothek und adaptive Parametersteuerung. Die vollständige Implementierung erfolgt in MQL5 unter Verwendung der integrierten Matrix-/Vektortypen, was Implementierungshürden in MetaTrader 5 beseitigt.