English Русский Português
preview
Eagle-Strategie (ES)

Eagle-Strategie (ES)

MetaTrader 5Handel |
20 0
Andrey Dik
Andrey Dik

Inhalt

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


Einführung

In der Welt der Programmierung, insbesondere bei der Entwicklung von Trading-EAs, spielt eine effektive Optimierung eine entscheidende Rolle. Hochspezialisierte Probleme, bei denen es darum geht, aus einer Vielzahl möglicher Optionen eine optimale Lösung zu finden, erfordern den Einsatz fortschrittlicher Algorithmen. Herkömmliche Optimierungsmethoden erweisen sich oft als unwirksam, da sie in lokalen Optima stecken bleiben und keine global bessere Lösung finden.

Dementsprechend rücken sowohl von der Natur inspirierte metaheuristische Algorithmen als auch Methoden, die sich im Zuge natürlicher Evolution herausgebildet haben, zunehmend in den Fokus. Diese Algorithmen sind in der Lage, gute und oft nahezu optimale Lösungen zu finden, selbst bei Problemen, bei denen die Rechengeschwindigkeit von entscheidender Bedeutung ist.

Vor dem Hintergrund immer komplexerer und ressourcenintensiverer Aufgaben werden wir heute einen weiteren vielversprechenden Ansatz untersuchen: Eagle Strategy (ES). Dieser Algorithmus, der vom Jagdverhalten des Adlers inspiriert ist, ist eine neue Metaheuristik, die darauf ausgelegt ist, Optimierungsprobleme durch die Simulation einer Strategie der visuellen Suche und der dynamischen Verfolgung der Beute zu lösen.


Implementierung des Algorithmus

Stell dir einen Steinadler vor, der hoch am Himmel schwebt und nach Beute Ausschau hält. Seine Jagdstrategie ist bemerkenswert effizient und besteht aus zwei klar voneinander getrennten Phasen: Zunächst fliegt er in großer Höhe und überfliegt mit chaotischen Zickzackbewegungen ein weitläufiges Gebiet; hat er dann ein Ziel ausgemacht, stürzt er sich rasch hinab und konzentriert all seine Kräfte auf eine bestimmte Beute. Es ist diese natürliche Weisheit, die die Grundlage für den Algorithmus der „Adlerstrategie“ bildete, der 2010 zur Lösung komplexer Optimierungsprobleme entwickelt wurde.

In der ersten Phase nutzt der Algorithmus sogenannte Levy-Flüge – ein mathematisches Modell, das Bewegungen im Raum beschreibt. Im Gegensatz zu einem typischen Zufallsweg, bei dem die Schritte in etwa gleichmäßig verteilt sind, besteht ein Levy-Flug aus vielen kleinen Schritten, die von seltenen, aber sehr langen Sprüngen unterbrochen werden. 

Wenn der Algorithmus einen vielversprechenden Bereich findet (wie ein Adler, der Beute erspäht), wird die zweite Stufe aktiviert – eine intensive lokale Suche unter Verwendung des Firefly-Algorithmus. Einen solchen bekannten Algorithmus haben wir bereits in einem früheren Artikel behandelt. Mehrere Suchagenten werden – wie Glühwürmchen in der Nacht – von helleren (erfolgreichen) Nachbarn angezogen. Die Helligkeit des Glühwürmchens hängt von der Qualität der Lösung ab, die es findet, und die Anziehungskraft nimmt mit zunehmender Entfernung exponentiell ab. Dadurch entsteht ein Gleichgewicht zwischen der Erkundung der Umgebung und der Bewegung in Richtung der bisher besten bekannten Lösung.

Die zentrale Neuerung von ES ist der zyklische Wechsel zwischen diesen beiden Modi. Nach einer intensiven lokalen Suche wechselt der Algorithmus wieder zur globalen Suche und macht einen großen Sprung in einen neuen, noch unerforschten Bereich des Suchraums. Dadurch wird eine vorzeitige Konvergenz verhindert und es kann selbst in sehr komplexen Landschaften mit vielen lokalen Optima ein globales Optimum gefunden werden.

Die Algorithmusparameter sind intuitiv und einfach zu konfigurieren. Die Populationsgröße bestimmt die Anzahl der gleichzeitig suchenden Agenten, der Levy-Parameter steuert das Verhältnis zwischen kurzen und langen Sprüngen, der Radius der Hyperkugel definiert den Bereich der intensiven lokalen Suche, und der Randomisierungskoeffizient fügt ein Zufallselement hinzu, um lokale Fallen zu überwinden. Dank dieser Flexibilität lässt sich der Algorithmus an ein bestimmtes Problem anpassen, ohne dass dafür tiefgreifende Kenntnisse der mathematischen Theorie erforderlich sind.

Die Philosophie des ES-Algorithmus ist einfach und elegant: globaler Überblick kombiniert mit lokaler Präzision. So wie ein Adler den Erkundungsflug mit einem gezielten Angriff verbindet, hält der Algorithmus das Gleichgewicht zwischen der Erkundung unbekannter Gebiete und der Nutzung vielversprechender Lösungen, die er findet.

Die Hauptmerkmale des Algorithmus sind: automatischer Wechsel zwischen den Phasen, sobald sich die Lösung verbessert, adaptive Parameter (λ nimmt bei Stagnation ab, die Schrittweite verringert sich mit der Zeit) sowie ein Gleichgewicht zwischen der Erkundung neuer Bereiche und der Nutzung der gefundenen Lösungen.

eagle_strategy

Abb. 1. Der ES-Algorithmus im Einsatz

Die Abbildung zeigt die beiden Betriebsphasen des Algorithmus, die Levy-Flugbahnen (rote gepunktete Linien), die lokale Suchhypersphäre (blauer Kreis), die Bewegung der Glühwürmchen innerhalb der Kugel (grüne Punkte mit Lichtkränzen) sowie die Konturlinien der Optimierungsfunktion.

Bereiten wir den Pseudocode vor.

INITIALISIERUNG:
1. Erstelle eine Population von Agenten mit zufälligen Positionen
2. Setze das Flag für die globale Suche (phase = global).
3. Initialisiere die Stagnations- und Fortschrittszähler

HAUPTSCHLEIFE (bis das Stoppkriterium erfüllt ist):
  
  WENN (phase = global):
    FÜR jeden Agenten:
      – Erzeuge einen Levy-Schritt mithilfe des Mantegna-Algorithmus
      – Berechne die adaptive Schrittweite (am Anfang größer, am Ende kleiner)
      – Aktualisiere die Position: neue_Position = aktuelle_Position + Levy-Schritt × Skalierung
      – Wende die Randbedingungen an
    
    WENN (eine Verbesserung gegenüber der global besten Lösung festgestellt wird):
      – Wechsel in die lokale Phase
      – Bestimme den besten Agenten als Zentrum der lokalen Suche
      – Setze den Stagnationszähler zurück
    SONST:
      – Erhöhe den Stagnationszähler
      – WENN (Stagnation > 5 Iterationen):
        – Verringere den λ-Parameter für aggressivere Flüge
  
  SONST (Phase = lokal):
    Mit einer Wahrscheinlichkeit von 80 %:
      – Ermittle Agenten in einer Hypersphäre mit einem Radius von 0,1 um den besten Agenten
      – WENN (Agenten < 5):
        – Nimm die 5 nächsten Nachbarn oder 30 % der Population
      
      Für jeden Agenten in der lokalen Gruppe:
        Für jeden anderen Agenten in der Gruppe:
          FALLS (ein anderer Agent besser ist):
            – Berechne die Anziehungskraft β = β₀ × exp(-γ × Entfernung²)
            – Verschiebe den Agenten: Position += β × (beste Position – aktuelle Position) + Zufallsrauschen
    
    Mit einer Wahrscheinlichkeit von 20 %:
      – Übertrage teilweise Koordinaten des global besten Agenten auf zufällig gewählte Koordinatenwerte.
    
    – Erhöhe den lokalen Iterationszähler
    – WENN (20 lokale Iterationen abgeschlossen):
       – Kehre zur globalen Phase zurück
       – Stelle den ursprünglichen λ-Parameter wieder her

  UPDATE:
    FÜR jeden Agenten:
      – Berechne die Fitness
      – Aktualisiere den persönlichen Bestwert
      – Aktualisiere den globalen Bestwert

In dieser Implementierung des ES-Algorithmus wird ein numerisches Verfahren angewendet, um Zahlen mit der Levy-Verteilung zu erzeugen — Mantegna-Algorithmus. Das Verfahren wurde 1994 von R. N. Mantegna vorgeschlagen und wurde zur Standardmethode für die Simulation von Levy-Flügen in Optimierungsalgorithmen. Mantegna hat mathematisch nachgewiesen, dass das Verhältnis zweier speziell skalierter Gaußscher Größen eine Verteilung ergibt, die im für praktische Anwendungen relevanten Wertebereich der Levy-Verteilung sehr nahekommt. Der Kernpunkt lautet wie folgt:

// Calculating sigma for Mantegna algorithm
double numerator   = Gamma(1.0 + lambda) * MathSin(M_PI * lambda / 2.0);
double denominator = Gamma((1.0 + lambda) / 2.0) * lambda * MathPow(2.0, (lambda - 1.0) / 2.0);
double sigma = MathPow(numerator / denominator, 1.0 / lambda);

// Generate u and v from normal distributions
double u_val = GenerateGaussian() * sigma;
double v_val = MathAbs(GenerateGaussian());

// Calculate Levy step
levyStep[c] = u_val / MathPow(v_val, 1.0 / lambda);

Nehmen wir zwei Zufallsvariablen:

  • u – aus der Normalverteilung N(0, σ²)
  • v – aus der Normalverteilung N(0, 1)
Spezielle Gleichung für σ:
σ = [Γ(1+λ) × sin(πλ/2) / (Γ((1+λ)/2) × λ × 2^((λ-1)/2))]^(1/λ)

wobei Γ die Gammafunktion ist und λ der Parameter der Levy-Verteilung (1 < λ ≤ 3).

Es ist wichtig zu beachten, dass bei der Bestimmung der Gammafunktion (zur Berechnung des σ-Parameters in der Mantegna-Methode) die Lanczos-Näherung verwendet wird, bei der es sich um eine hochgenaue numerische Methode zur Berechnung der Gammafunktion handelt. Dies ist eine der effizientesten Methoden zur Berechnung von Γ(z) und ist im Code als separate Funktion implementiert, auf die wir zum Schluss eingehen werden.

Die Grundgleichung lautet wie folgt:

Γ(z+1) = √(2π) × ((z+g+0.5)^(z+0.5)) × e^(-(z+g+0.5)) × Ag(z)

wobei: g ein Parameter ist (in der Regel 7); Ag(z) eine Reihe mit vorab berechneten Koeffizienten ist; bei g = 7 und 9 Koeffizienten ergibt sich eine Genauigkeit von etwa 15 signifikanten Stellen.

Für z < 0,5 verwendet die Reflexionsformel das Verhältnis, wodurch die Gammafunktion für alle reellen Zahlen berechnet werden kann:

Γ(z) × Γ(1-z) = π / sin(πz)

Ohne eine effiziente Berechnung der Gammafunktion wäre die Erzeugung von Levy-Flügen rechenintensiv, was den gesamten Optimierungsalgorithmus erheblich verlangsamen würde.

Endgültiger Levy-Schritt:

step = u / |v|^(1/λ)

Der Algorithmus erzeugt eine Schrittfolge, deren Verhalten typisch für Levy-Flüge ist: viele kleine Schritte (lokale Erkundung), seltene, aber sehr große Sprünge (globale Erkundung) sowie eine Potenzgesetzverteilung der Schrittlängen.

Die Vorteile dieser Methode liegen in der Einfachheit der Lösung – da lediglich ein Normalverteilungsgenerator benötigt wird – sowie in der Stabilität und numerischen Robustheit des Algorithmus. Gerade diese Eigenschaft macht Levy-Flüge für die Optimierung so effektiv – sie bieten ein optimales Gleichgewicht zwischen der detaillierten Erkundung lokaler Bereiche und dem schnellen Übergang zu neuen Regionen des Suchraums.

Nach einer eingehenden Untersuchung der im ES-Algorithmus verwendeten Methoden können wir nun mit der Erstellung der Klasse C_AO_ES fortfahren, die die Implementierung einer auf der Eagle-Hunting-Strategie basierenden Optimierungsmethode darstellt und von der Basisklasse C_AO abgeleitet ist. Das Verfahren erfolgt in zwei Schritten: Zunächst wird eine globale Suche durchgeführt, um potenziell vielversprechende Bereiche zu identifizieren, anschließend wird eine lokale Suche innerhalb des ausgewählten Suchbereichs durchgeführt, um das Ergebnis zu verfeinern.

Die Populationsgröße popSize gibt die Anzahl der „Kandidaten“-Lösungen an, die an der Suche teilnehmen. Der Lambda-Levy-Parameter steuert die Verteilung der Zufallssprünge. Der Radius der Hyperkugel „sphereRadius“ legt den Bereich für die lokale Suche fest. Die Anzahl der lokalen Suchiterationen „localIterations“ gibt an, wie oft die Lösung innerhalb der Hyperkugel verfeinert wird. Die Randomisierungs- und Attraktivitätsparameter „alpha“ und „beta0“ steuern Komponenten der Suchmodelle, wie beispielsweise zufällige Bewegungen und den Einfluss von „Licht“ (im übertragenen Sinne). 

Die globale Suchphase (GlobalExploration) konzentriert sich darauf, mithilfe von zufälligen Schritten, die nach der Levy-Verteilung generiert werden, „vielversprechende“ Regionen im gesamten Suchraum zu finden. Dieser Ansatz ermöglicht eine umfassende Erkundung und lässt sich gut auf große Suchräume skalieren.

Die lokale Intensivsuche (LocalExploitation) führt eine gründlichere Suche innerhalb der Hypersphäre um den ausgewählten Punkt herum durch. In diesem Fall werden kleinere und präzisere Schritte verwendet, was einer lokalen Optimierung entspricht.

Die Erzeugung von Levy-Schritten (GenerateLevyStep) generiert zufällige Bewegungen unter Verwendung der Levy-Verteilung; dies sorgt sowohl für kleine als auch für große Sprünge im Suchraum, um ein Gleichgewicht zwischen Erkundung und Ausnutzung herzustellen.

Die Klasse enthält Mechanismen zur Verfolgung des Suchfortschritts, wie beispielsweise das Speichern der besten gefundenen Lösung, Stagnationszähler (falls sich die Lösung nicht verbessert) sowie Epochen-Schleifen zur Begrenzung der Algorithmusausführung nach Zeit oder Iterationen, Berechnungen von Verteilungsparametern für Zufallsschritte, insbesondere die Erzeugung von Gauß- und Levy-Verteilungen, sowie Methoden zur Steuerung der Suchphasen, die den Wechsel zwischen der globalen und der lokalen Phase gewährleisten und die Kontrolle über deren Ablauf ermöglichen.

Insgesamt bietet die Methode eine Suchstrategie, die eine umfassende Erkundung des Suchraums zur Identifizierung potenzieller Bereiche mit einer anschließenden sorgfältigen lokalen Optimierung kombiniert, um präzise Lösungen zu erzielen. Dabei kommen Parameter und Mechanismen zum Einsatz, die es ermöglichen, das Verhalten der Methode an spezifische Probleme und Bedingungen anzupassen.

//————————————————————————————————————————————————————————————————————
class C_AO_ES : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_ES () { }
  C_AO_ES ()
  {
    ao_name = "ES";
    ao_desc = "Eagle Strategy";
    ao_link = "https://www.mql5.com/en/articles/18460";

    popSize         = 100;   // population size
    lambda          = 1.0;  // Levy distribution parameter (1 < λ ≤ 3)
    sphereRadius    = 0.1;  // hypersphere radius for local search
    localIterations = 20;   // number of local search iterations
    alpha           = 0.1;  // randomization parameter for Firefly
    beta0           = 1.2;  // initial attractiveness

    ArrayResize (params, 6);

    params [0].name = "popSize";         params [0].val = popSize;
    params [1].name = "lambda";          params [1].val = lambda;
    params [2].name = "sphereRadius";    params [2].val = sphereRadius;
    params [3].name = "localIterations"; params [3].val = localIterations;
    params [4].name = "alpha";           params [4].val = alpha;
    params [5].name = "beta0";           params [5].val = beta0;
  }

  void SetParams ()
  {
    popSize         = (int)params [0].val;
    lambda          = params      [1].val;
    sphereRadius    = params      [2].val;
    localIterations = (int)params [3].val;
    alpha           = params      [4].val;
    beta0           = params      [5].val;
  }

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

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  double lambda;          // Levy distribution parameter (1 < λ ≤ 3)
  double sphereRadius;    // hypersphere radius for local search
  int    localIterations; // number of local search iterations
  double alpha;           // randomization parameter
  double beta0;           // initial attractiveness

  private: //---------------------------------------------------------
  double gamma_es;           // light absorption coefficient
  double levyStep [];        // array for Levy steps

  // Phase tracking
  bool   inLocalSearchPhase; // local search flag
  int    localSearchCenter;  // local search center
  int    localSearchCounter; // local search iteration counter

  // Monitoring convergence
  double prevBestFitness;    // previous best value
  int    stagnationCounter;  // stagnation counter

  // Tracking epochs
  int    epochCurrent;       // current epoch
  int    epochMax;           // maximum number of epochs

  // Auxiliary methods
  void   GlobalExploration  ();
  void   LocalExploitation  ();
  void   GenerateLevyStep   ();
  double GenerateGaussian   ();
  double Gamma              (double z);
};
//————————————————————————————————————————————————————————————————————

Die Methode „Init“ der Klasse C_AO_ES initialisiert den Optimierungsalgorithmus vor Beginn der Suche. Zunächst wird die Methode „StandardInit“ aufgerufen, die für die Standardinitialisierung des Algorithmus zuständig ist. Es konfiguriert die allgemeinen Parameter und Datenstrukturen. Wenn StandardInit fehlschlägt, gibt die gesamte Methode „false“ zurück, was darauf hinweist, dass die Initialisierung nicht erfolgreich war.

Anschließend wird Speicher für das Array „levyStep“ zugewiesen, dessen Größe der Anzahl der Koordinaten „coords“ entspricht. Dieses Array dient zur Speicherung der gemäß der Levy-Verteilung generierten Schritte. Ist das Flag „inLocalSearchPhase“ auf „false“ gesetzt, befindet sich der Algorithmus zunächst in der globalen Suchphase. Die Variablen „localSearchCenter“ und „localSearchCounter“ werden auf „0“ gesetzt, um die Zähler für die lokale Suche vorzubereiten.

Initialisierung der Konvergenzparameter:

  • prevBestFitness wird auf den kleinstmöglichen Wert gesetzt, damit die erste gefundene Lösung garantiert als besser als die vorherige angesehen wird.
  • stagnationCounter wird auf „0“ gesetzt, um die Anzahl der Iterationen zu erfassen, bei denen die bisher beste Lösung nicht verbessert wurde.

Initialisierung der Epochenparameter:

  • epochMax erhält den Wert der Eingabe epochsP, wodurch die maximale Anzahl von Epochen (Iterationen) des Algorithmus festgelegt wird.
  • epochCurrent wird auf „0“ gesetzt, um die aktuelle Epoche zu verfolgen.

Es wird ein fester Parameter gesetzt: Der Variablen „gamma_es“ wird der Wert „1,0“ zugewiesen. Dieser Parameter wird im Firefly-Algorithmus verwendet, der Teil der Gesamtstrategie ist.

Die Ausgangspopulation von „a“ Lösungen wird initialisiert. Für jede Lösung in der Population:

  • Jede Koordinate des Lösungsvektors (a[i].c[c]) wird mit einem Zufallswert aus dem Bereich [rangeMin[c], rangeMax[c]] initialisiert.
  • Der Wert jeder Koordinate wird unter Berücksichtigung des Schritts (rangeStep[c]) mithilfe der Methode u.SeInDiSp auf den nächstgelegenen zulässigen Wert „gerundet“.
  • Der Wert der Zielfunktion (a[i].f und a[i].fB) wird für jede Lösung auf „-DBL_MAX“ gesetzt.
//————————————————————————————————————————————————————————————————————
bool C_AO_ES::Init (const double &rangeMinP  [],
                    const double &rangeMaxP  [],
                    const double &rangeStepP [],
                    const int     epochsP = 0)
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //------------------------------------------------------------------
  // Initialize arrays
  ArrayResize (levyStep, coords);

  // Initialize phase tracking
  inLocalSearchPhase = false;
  localSearchCenter  = 0;
  localSearchCounter = 0;

  // Initialize convergence tracking
  prevBestFitness   = -DBL_MAX;
  stagnationCounter = 0;

  // Initialize epoch tracking
  epochMax     = epochsP;
  epochCurrent = 0;

  // Fixed Firefly parameter
  gamma_es = 1.0;

  // Initialize the population randomly
  for (int i = 0; i < popSize; i++)
  {
    for (int c = 0; c < coords; c++)
    {
      a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
      a [i].c [c] = u.SeInDiSp  (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }

    a [i].f  = -DBL_MAX;
    a [i].fB = -DBL_MAX;
  }

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

Die Moving-Methode setzt die grundlegende Logik der iterativen Optimierung um, bei der zwischen globaler Suche und lokaler Ausnutzung gewechselt wird. Bei jedem Methodenaufruf wird der Epochenzähler erhöht, um den Fortschritt des Algorithmus zu verfolgen.

Abwicklung der Suchphasen:
  • Wenn die aktuelle Phase eine globale Suche ist, wird der Raum anhand von Schritten erkundet, die der Levy-Verteilung nachempfunden sind, wodurch neue Lösungen im gesamten Raum generiert werden, um nach neuen Bereichen mit Potenzial zu suchen;

  • Wenn nach einer globalen Suche eine Verbesserung gefunden wird, wechselt der Algorithmus zur lokalen Phase. In diesem Fall wird die vielversprechendste Lösung (Agent) ausgewählt, um die herum eine Suche durchgeführt wird, um sie weiter zu verfeinern;

  • Wenn über mehrere Iterationen hinweg keine Verbesserungen eintreten (Stagnation setzt ein), wird die Suche nach neuen Lösungen durch aggressivere Flugsprünge intensiviert, wobei der Lambda-Parameter verringert wird, um den Raum umfassender zu erkunden;

  • Befindet sich der Algorithmus in der lokalen Suchphase, besteht eine Wahrscheinlichkeit von 80 %, dass die lokale Optimierung mithilfe einer Methode durchgeführt wird, die den Firefly-Algorithmus simuliert. In diesem Fall wird die gewählte Lösung verfeinert und der lokale Iterationszähler erhöht. Sobald die festgelegte Anzahl von Iterationen der lokalen Suche erreicht ist, kehrt der Algorithmus zur globalen Suche zurück. 

Zusätzliche zufällige Störungen, die mit einer bestimmten Wahrscheinlichkeit die Entscheidungen der Population beeinflussen, sollen die Vielfalt erhalten und verhindern, dass man in lokalen „Fallen“ stecken bleibt.

    Somit verfolgt das Verfahren einen schrittweisen und flexiblen Suchansatz, bei dem sich globale Erkundung und gezielte lokale Optimierung abwechseln. Dies ermöglicht ein Gleichgewicht zwischen der Erforschung neuer Bereiche und der Verbesserung bestehender Lösungen.

    //————————————————————————————————————————————————————————————————————
    void C_AO_ES::Moving ()
    {
      epochCurrent++;
    
      // PHASE DECISION: Alternating between global and local search
      if (!inLocalSearchPhase)
      {
        // PHASE 1: GLOBAL EXPLORATION using Levy flights
        GlobalExploration ();
    
        // Check whether it is necessary to switch to local search
        // Switch if we find a promising area (improving the best fitness)
        if (fB > prevBestFitness)
        {
          inLocalSearchPhase = true;
          localSearchCounter = 0;
          prevBestFitness    = fB;
          stagnationCounter  = 0;
    
          // Find the best agent to center local search
          localSearchCenter = 0;
          double bestFit = -DBL_MAX;
    
          for (int i = 0; i < popSize; i++)
          {
            if (a [i].f > bestFit)
            {
              bestFit = a [i].f;
              localSearchCenter = i;
            }
          }
        }
        else
        {
          stagnationCounter++;
    
          // In case of stagnation, increase exploration
          if (stagnationCounter > 5)
          {
            lambda = MathMax (1.0, lambda - 0.1); // Make Levy flights more aggressive
          }
        }
      }
      else
      {
        if (u.RNDprobab () < 0.8)
        {
          // PHASE 2: LOCAL EXPLOITATION using the Firefly algorithm
          LocalExploitation ();
    
          localSearchCounter++;
    
          // Return to global search after local iterations are complete
          if (localSearchCounter >= localIterations)
          {
            inLocalSearchPhase = false;
            lambda = params [1].val; // Reset lambda to its original value
          }
        }
        else
        {
          for (int i = 0; i < popSize; i++)
          {
            for (int c = 0; c < coords; c++)
            {
              if (u.RNDprobab () < 0.5)
              {
                a [i].c [c] = cB [c];
              }
            }
          }
        }
      }
    }
    //————————————————————————————————————————————————————————————————————
    

    Die Revisionsmethode dient dazu, Informationen über die besten Lösungen während der Ausführung des Algorithmus zu aktualisieren. Die Methode durchläuft alle Elemente der aktuellen Lösungsmenge und vergleicht bei jeder Lösung deren aktuelle Fitness mit der in ihrem persönlichen Bestwert gespeicherten Fitness. Ist der aktuelle Wert besser, werden das persönliche Bestergebnis und die entsprechende Lösung aktualisiert (die beste Lösung der aktuellen Iteration wird gespeichert).

    Anschließend vergleicht das Verfahren die Fitness jeder Lösung mit dem aktuell besten Wert, der in der gesamten Population gefunden wurde. Ist das aktuelle Ergebnis das beste, wird das globale Ergebnis aktualisiert und die entsprechende Lösung gespeichert. Auf diese Weise hält das Verfahren im aktuellen Zustand des Algorithmus aktuelle Informationen über lokale und globale optimale Lösungen bereit und stellt so sicher, dass die besten gefundenen Lösungen bei jedem Schritt erhalten bleiben.

    //————————————————————————————————————————————————————————————————————
    void C_AO_ES::Revision ()
    {
      for (int i = 0; i < popSize; i++)
      {
        // Updating the personal best one
        if (a [i].f > a [i].fB)
        {
          a [i].fB = a [i].f;
          ArrayCopy (a [i].cB, a [i].c, 0, 0, WHOLE_ARRAY);
        }
    
        // Update the global best one
        if (a [i].f > fB)
        {
          fB = a [i].f;
          ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);
        }
      }
    }
    //————————————————————————————————————————————————————————————————————
    

    Die GlobalExploration-Methode implementiert eine globale Suchphase, bei der der Lösungsraum mithilfe von Levy-Flügen durchsucht wird. Für jede Lösung in der Schleife wird unter Verwendung der Levy-Verteilung ein Zufallssprung über die gesamte Population generiert, der die Richtung und die Länge der Bewegung im Lösungsraum bestimmt.

    Die Levy-Verteilung zeichnet sich durch „schwere Verteilungsschwänze“ aus, was sowohl kleine Verfeinerungsschritte als auch große, seltene Sprünge ermöglicht, um entfernte Regionen zu erkunden. In der Schleife wird für jede Koordinate der Lösung Folgendes berechnet:

    • Suchbereich (die Differenz zwischen dem maximalen und dem minimalen Koordinatenwert),
    • stepScale – adaptiver Skalierungsfaktor. Er nimmt im Verlauf der Suche ab (je näher man dem Ende kommt, desto kleiner werden die Schritte), was dazu beiträgt, den Suchbereich um vielversprechende Lösungen einzugrenzen, während die Schritte zu Beginn der Suche größer sind, um eine umfassendere Erkundung zu ermöglichen.
    • Der Levy-Schritt wird angewendet: Die Koordinate der aktuellen Lösung wird um einen Wert verschoben, der proportional zum Levy-Schritt, zur Suchspanne und zum Skalierungsfaktor ist.
    • Grenzkorrektur: Die neue Koordinate wird daraufhin überprüft, ob sie außerhalb des zulässigen Bereichs liegt; ist dies der Fall, wird der Wert so angepasst, dass er innerhalb des festgelegten Bereichs bleibt.
    Letztendlich aktualisiert die Methode die Position jeder Lösung in der Population anhand von Schritten, die aus der Levy-Verteilung abgeleitet werden, und ermöglicht so eine globale Erkundung des Lösungsraums mit einer adaptiven Steuerung der Schrittweite in Abhängigkeit vom Optimierungsfortschritt. So können wir den Raum zu Beginn erkunden und unsere Ergebnisse schrittweise verfeinern, während wir uns der optimalen Lösung nähern.
    //————————————————————————————————————————————————————————————————————
    // PHASE 1: GLOBAL EXPLORATION using Levy flights
    void C_AO_ES::GlobalExploration ()
    {
      for (int i = 0; i < popSize; i++)
      {
        // Generate Levy step
        GenerateLevyStep ();
    
        // Update position using Levy flight
        for (int c = 0; c < coords; c++)
        {
          double range = rangeMax [c] - rangeMin [c];
    
          // Adaptive step size depending on search progress
          double progress = (epochMax > 0) ? (double)epochCurrent / (double)epochMax : 0.5;
          double stepScale = 0.01 + 0.2 * (1.0 - progress); // Start with big steps
    
          // Apply the Levy step
          a [i].c [c] += levyStep [c] * range * stepScale;
    
          // Boundary restrictions
          a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //————————————————————————————————————————————————————————————————————
    

    Die Methode „LocalExploitation“ implementiert eine lokale Optimierungsphase für Lösungen unter Verwendung des Firefly-Algorithmus. Zunächst werden die Agenten ermittelt, die sich innerhalb einer bestimmten Hyperkugel um die beste Lösung befinden. Dazu wird der Abstand jedes Agenten zum Mittelpunkt der Hyperkugel berechnet; ist dieser kleiner als der Radius oder befindet sich der Agent im Mittelpunkt der Suche, wird er in die ausgewählte Gruppe aufgenommen.

    Befinden sich zu wenige Agenten innerhalb der Hyperkugel, wird der Suchbereich um die nächsten Nachbarn erweitert. Dazu wird für alle Agenten die Entfernung zum Zentrum berechnet, und die nächstgelegenen werden ausgewählt – entweder nach ihrer Anzahl (zum Beispiel 5) oder nach ihrem Anteil an der Gesamtzahl (zum Beispiel 30 %). Diese Agenten werden der Gruppe hinzugefügt.

    Anwendungsbereich des Firefly-Algorithmus: Die ausgewählten Agenten interagieren gemäß den Regeln des Algorithmus:
    • Für jeden Agenten wird geprüft, ob es in der Gruppe einen anderen Agenten mit besserer Fitness gibt.
    • Gibt es einen solchen, bewegt sich der aktuelle Agent auf einen attraktiveren (besseren) Agenten zu, und der Abstand zwischen ihnen wird berechnet.
    • Je attraktiver ein anderer Agent ist, desto größer ist seine „Leuchtkraft“ (abhängig von der Entfernung und den Algorithmusparametern).
    • Die Bewegung verbindet die Anziehungskraft zu einem besseren Agenten mit einer zufälligen Störung, um die Vielfalt zu erhalten.
    • Nach den Verschiebungen werden die Koordinatengrenzen überprüft, um sicherzustellen, dass die Lösungen innerhalb des zulässigen Bereichs bleiben.

      Dadurch ermöglicht die Methode eine lokale Verbesserung der Lösungen in der Nähe der gefundenen besten Punkte, indem sie die Interaktion der Agenten gemäß dem Strategie-Modell des Firefly-Algorithmus nutzt: Die besten Lösungen ziehen die schlechtesten an, was die punktuelle Optimierung des Lösungsraums erleichtert.

      //————————————————————————————————————————————————————————————————————
      // PHASE 2: Local exploitation using the Firefly algorithm
      void C_AO_ES::LocalExploitation ()
      {
        // Identification of agents inside the hypersphere around the best solution
        double agents_in_sphere [];
        ArrayResize (agents_in_sphere, 0);
      
        for (int i = 0; i < popSize; i++)
        {
          double normalized_dist = 0.0;
      
          for (int c = 0; c < coords; c++)
          {
            double diff = (a [i].c [c] - a [localSearchCenter].c [c]) / (rangeMax [c] - rangeMin [c]);
            normalized_dist += diff * diff;
          }
          normalized_dist = MathSqrt (normalized_dist);
      
          // Include agents inside the sphere or the center itself
          if (normalized_dist <= sphereRadius || i == localSearchCenter)
          {
            int size = ArraySize (agents_in_sphere);
            ArrayResize (agents_in_sphere, size + 1);
            agents_in_sphere [size] = i;
          }
        }
      
        // If there are too few agents, expand to the nearest neighbors
        if (ArraySize (agents_in_sphere) < 5)
        {
          ArrayResize (agents_in_sphere, 0);
      
          // Calculate distances for all agents
          double distances [];
          ArrayResize (distances, popSize);
      
          for (int i = 0; i < popSize; i++)
          {
            if (i == localSearchCenter)
            {
              distances [i] = 0.0;
            }
            else
            {
              double dist = 0.0;
              for (int c = 0; c < coords; c++)
              {
                double diff = (a [i].c [c] - a [localSearchCenter].c [c]) / (rangeMax [c] - rangeMin [c]);
                dist += diff * diff;
              }
              distances [i] = MathSqrt (dist);
            }
          }
      
          // Take the closest 5 agents or 30% of the population
          int numAgents = MathMin (popSize, MathMax (5, popSize / 3));
          ArrayResize (agents_in_sphere, numAgents);
      
          // Simple selection of nearby agents
          for (int k = 0; k < numAgents; k++)
          {
            double minDist = DBL_MAX;
            int minIdx = -1;
      
            for (int i = 0; i < popSize; i++)
            {
              bool already_selected = false;
      
              for (int j = 0; j < k; j++)
              {
                if (agents_in_sphere [j] == i)
                {
                  already_selected = true;
                  break;
                }
              }
      
              if (!already_selected && distances [i] < minDist)
              {
                minDist = distances [i];
                minIdx = i;
              }
            }
      
            agents_in_sphere [k] = minIdx;
          }
        }
      
        // Execute the Firefly algorithm on the selected agents
        int numLocalAgents = ArraySize (agents_in_sphere);
      
        for (int i = 0; i < numLocalAgents; i++)
        {
          int idx_i = (int)agents_in_sphere [i];
      
          for (int j = 0; j < numLocalAgents; j++)
          {
            if (i == j) continue;
      
            int idx_j = (int)agents_in_sphere [j];
      
            // If agent j is better than agent i, move i to j
            if (a [idx_j].f > a [idx_i].f)
            {
              // Calculate the distance
              double r_squared = 0.0;
      
              for (int c = 0; c < coords; c++)
              {
                double diff = (a [idx_j].c [c] - a [idx_i].c [c]) / (rangeMax [c] - rangeMin [c]);
                r_squared += diff * diff;
              }
      
              // Calculate attractiveness
              double beta = beta0 * MathExp (-gamma_es * r_squared);
      
              // Move agent i to agent j
              for (int c = 0; c < coords; c++)
              {
                double range = rangeMax [c] - rangeMin [c];
      
                // Firefly motion equation
                a [idx_i].c [c] += beta * (a [idx_j].c [c] - a [idx_i].c [c]) +
                                   alpha * (u.RNDfromCI (-0.5, 0.5)) * range * 0.1;
      
                // Apply borders
                a [idx_i].c [c] = u.SeInDiSp (a [idx_i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
              }
            }
          }
        }
      }
      //————————————————————————————————————————————————————————————————————
      

      Die Methode „GenerateLevyStep“ ist für die Erzeugung des Levy-Schritts zuständig, der für die Umsetzung der globalen Erkundungsstrategie im Optimierungsalgorithmus zuständig ist. Die Methode nutzt den Mantegna-Algorithmus zur Erzeugung dieser Schritte, eine Methode, die wir bereits oben erläutert haben.

      Berechnung von Sigma:

      Die Formel im Code berechnet den Sigma-Parameter. Dieser Parameter steht im Zusammenhang mit der Skala der Levy-Verteilung und hängt von der Konstante Lambda ab, die die Form der Levy-Verteilung charakterisiert (und bestimmt, wie „schwer“ die Verteilungsschwänze ausfallen). Gamma() ist die Gammafunktion, eine Verallgemeinerung der Fakultät auf reelle Zahlen (der Code für diese Methode ist weiter unten aufgeführt). Dieser Parameter wird benötigt, um normalverteilte Werte zu generieren, die anschließend im Mantegna-Algorithmus verwendet werden.

      Die Erzeugung des Levy-Schritts erfolgt für jede Koordinate (jeden Parameter) der Lösung unabhängig voneinander. Es werden zwei Zufallsvariablen u_val und v_val aus einer Normalverteilung (Gauß-Verteilung) generiert, und der Absolutwert von v_val wird gebildet. Der Levy-Schritt „levyStep [c]“ wird anhand der Formel des Mantegna-Algorithmus berechnet: u_val / Math.pow(v_val, 1.0 / lambda). Es wird eine Überprüfung durchgeführt, um eine Division durch Null zu vermeiden (falls v_val sehr klein ist). Der Levy-Schritt ist im absoluten Wert begrenzt. Dies geschieht, um zu große Sprünge zu vermeiden.

      Als Ergebnis dieser Methode wird das Array „levyStep“ erzeugt, das die Levy-Schrittwerte für jede Koordinate enthält. Diese Schritte werden dann in der Methode „GlobalExploration“ verwendet, um die Position jeder Lösung im Suchraum zu aktualisieren.

      //————————————————————————————————————————————————————————————————————
      // Generate Levy step using Mantegna algorithm
      void C_AO_ES::GenerateLevyStep ()
      {
        // Calculate sigma for Mantegna algorithm
        double numerator   = Gamma (1.0 + lambda) * MathSin (M_PI * lambda / 2.0);
        double denominator = Gamma ((1.0 + lambda) / 2.0) * lambda * MathPow (2.0, (lambda - 1.0) / 2.0);
        double sigma = MathPow (numerator / denominator, 1.0 / lambda);
      
        for (int c = 0; c < coords; c++)
        {
          // Generate u and v from normal distributions
          double u_val = GenerateGaussian () * sigma;
          double v_val = MathAbs (GenerateGaussian ());
      
          // Calculate the Levy step
          if (v_val > 1e-10)
          {
            levyStep [c] = u_val / MathPow (v_val, 1.0 / lambda);
          }
          else
          {
            levyStep [c] = 0.0;
          }
      
          // Limit extreme values
          if (MathAbs (levyStep [c]) > 10.0)
          {
            levyStep [c] = 10.0 * (levyStep [c] > 0 ? 1.0 : -1.0);
          }
        }
      }
      //————————————————————————————————————————————————————————————————————
      

      Die Methode GenerateGaussian dient zur Erzeugung von Zufallszahlen, die einer Normalverteilung (Gauß-Verteilung) mit dem Mittelwert „0“ und der Standardabweichung „1“ folgen. Es nutzt die Box-Muller-Transformation, eine für dieses Problem recht gängige Methode, die wir bereits in früheren Artikeln verwendet haben.

      Es werden die statischen Variablen „hasSpare“ und „spare“ verwendet; die Box-Muller-Transformation generiert jeweils zwei unabhängige Zufallszahlen aus einer Normalverteilung. „hasSpare“ ist eine logische Variable, die bestimmt, ob einer der generierten Werte für den nächsten Funktionsaufruf zwischengespeichert wird, „spare“ ist eine Variable, die diese „Reservwert“ speichert.

        Neue Zufallszahlen generieren (falls erforderlich):

        Wenn kein „zwischengespeicherter Wert“ vorhanden ist, werden zwei unabhängige Zufallsvariablen u_val und v_val aus einer Gleichverteilung auf dem Intervall (0, 1) generiert. Die Funktion „u.RNDfromCI“ erzeugt gleichverteilte Zahlen. Es wird die Box-Muller-Transformation angewendet:

        • Die Variable „mag“ (Magnitude) wird berechnet als Quadratwurzel aus -2,0 * Math.log(u_val + 1e-10). Es ist notwendig, eine kleine Zahl zu u_val hinzuzufügen, um zu vermeiden, dass der Logarithmus von Null berechnet wird, was unzulässig ist.
        • Die „Spare“-Zahl wird wie folgt berechnet: mag * Math.cos(2,0 * M_PI * v_val).
        • Der zurückgegebene Wert lautet mag * Math.sin(2,0 * M_PI * v_val).

        Die Methode gibt eine der generierten Zufallszahlen zurück, die einer Normalverteilung folgt.
        //————————————————————————————————————————————————————————————————————
        // Generate a Gaussian random number using the Box-Muller transform
        double C_AO_ES::GenerateGaussian ()
        {
          static bool hasSpare = false;
          static double spare;
        
          if (hasSpare)
          {
            hasSpare = false;
            return spare;
          }
        
          hasSpare = true;
          double u_val = u.RNDfromCI (0.0, 1.0);
          double v_val = u.RNDfromCI (0.0, 1.0);
        
          double mag = MathSqrt (-2.0 * MathLog (u_val + 1e-10));
          spare = mag * MathCos (2.0 * M_PI * v_val);
        
          return mag * MathSin (2.0 * M_PI * v_val);
        }
        //————————————————————————————————————————————————————————————————————
        

        Die Gamma-Methode ist eine Funktion, die die Gammafunktion für ein gegebenes Argument „z“ approximiert. Da die Gammafunktion eine wichtige mathematische Funktion ist, insbesondere in der Statistik und der Optimierung, ihre exakte Berechnung jedoch schwierig und rechenintensiv sein kann, werden häufig Näherungswerte verwendet. Wir verwenden die Lanczos-Näherung, die bei relativ geringem Rechenaufwand eine gute Genauigkeit bietet. Lassen Sie uns die wichtigsten Punkte besprechen.

        Ist das Argument „z“ kleiner als „0,5“, wird die Euler-Reflexionsformel angewendet. Auf diese Weise lässt sich die Gammafunktion für Argumente nahe Null berechnen. Die Reflexionsformel setzt die Gammafunktion von „z“ in Beziehung zur Gammafunktion von „1 – z“. Wir werden feste Lanczos-Koeffizienten verwenden, die sorgfältig ausgewählt wurden, um eine hohe Näherungsgenauigkeit zu gewährleisten, sowie Koeffizienten und eine Formel, die Potenz- und Exponentialfunktionen enthalten. Die Methode gibt den Näherungswert der Gammafunktion für das angegebene Argument „z“ zurück.

        Die Lanczos-Näherung bietet eine gute Genauigkeit, die für die meisten praktischen Anwendungen ausreicht. Der Algorithmus ist relativ effizient, da er nur wenige Rechenoperationen und das Nachschlagen von Koeffizienten in einer Tabelle erfordert. Sie eignet sich für eine Vielzahl von Argumentwerten, insbesondere in Verbindung mit der Reflexionsformel. Im Allgemeinen ist die Gamma-Methode eine recht genaue Methode zur Annäherung an die Gammafunktion.

        //————————————————————————————————————————————————————————————————————
        // Approximation of the gamma function using Lanczos approximation
        double C_AO_ES::Gamma (double z)
        {
          if (z < 0.5)
          {
            // Reflection formula for z < 0.5
            return M_PI / (MathSin (M_PI * z) * Gamma (1.0 - z));
          }
        
          // Lanczos coefficients
          const double g = 7.0;
          double coef [] =
          {
            0.99999999999980993,
            676.5203681218851,
            -1259.1392167224028,
            771.32342877765313,
            -176.61502916214059,
            12.507343278686905,
            -0.13857109526572012,
            9.9843695780195716e-6,
            1.5056327351493116e-7
          };
        
          z -= 1.0;
          double x = coef [0];
        
          for (int i = 1; i < 9; i++)
          {
            x += coef [i] / (z + i);
          }
        
          double t = z + g + 0.5;
          double sqrt2pi = MathSqrt (2.0 * M_PI);
        
          return sqrt2pi * MathPow (t, z + 0.5) * MathExp (-t) * x;
        }
        //————————————————————————————————————————————————————————————————————
        


        Testergebnisse

        Auch wenn der Algorithmus es nicht in unsere Rangliste geschafft hat, sind seine Ergebnisse doch bemerkenswert.

        ES|Eagle Strategy|100.0|1.0|0.1|20.0|0.1|1.2|
        =============================
        5 Hilly's; Func runs: 10000; result: 0.7077570016043803
        25 Hilly's; Func runs: 10000; result: 0.4307775904381953
        500 Hilly's; Func runs: 10000; result: 0.2747545259235643
        =============================
        5 Forest's; Func runs: 10000; result: 0.7173720280531499
        25 Forest's; Func runs: 10000; result: 0.3448423301485268
        500 Forest's; Func runs: 10000; result: 0.1597390810339928
        =============================
        5 Megacity's; Func runs: 10000; result: 0.5184615384615384
        25 Megacity's; Func runs: 10000; result: 0.2775384615384615
        500 Megacity's; Func runs: 10000; result: 0.11063076923077016
        =============================
        All score: 3.54187 (39.35%)

        Die Visualisierung zeigt deutlich, wie sich die Suchphasen je nach Strategie aufteilen und wann lange Sprünge und kurze Verfeinerungszüge stattfinden.

        Hilly

        ES mit der Testfunktion Hilly

        Forest

        ES mit der Testfunktion Forest

        Megacity

        ES mit der Testfunktion Megacity

        Der ES-Algorithmus ist der Bewertungstabelle der Vollständigkeit halber beigefügt.

        # AO Beschreibung Hilly Hilly
        Final
        Forest Forest
        Final
        Megacity (discrete) Megacity
        Final
        Final
        Result
        % of
        MAX
        10 p (5 F) 50 p (25 F) 1000 p (500 F) 10 p (5 F) 50 p (25 F) 1000 p (500 F) 10 p (5 F) 50 p (25 F) 1000 p (500 F)
        1 ANS Suche über die gesamte Nachbarschaft 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 Code-Lock-Algorithmus (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 Optimierung der Tiermigration 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) Entwicklungsstrategien 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 Kometenschweif-Algorithmus (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 Zeit-Evolutions-Reise-Algorithmus (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 stochastische Diffusionssuche 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 Billard-Optimierungsalgorithmus 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 Algorithmus für das Bogenschießen 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 Entwicklung sozialer Gruppen (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 Simuliertes isotropes Glühen (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 BBO biogeografisch basierte Optimierung 0.94912 0.69456 0.35031 1.99399 0.93820 0.67365 0.25682 1.86867 0.74615 0.48277 0.17369 1.40261 5.265 58.50
        13 ACS künstliche, kooperative Suche 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
        14 DA dialektischer Algorithmus 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
        15 BHAm Algorithmus für schwarze Löcher 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
        16 ASO Anarchische Gesellschaftsoptimierung 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
        17 RFO Optimierung des Royal Flush (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
        18 AOSm Suche nach atomaren Orbitalen 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
        19 TSEA Schildkrötenpanzer-Evolutionsalgorithmus (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
        20 DE differentielle Evolution 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
        21 SRA Algorithmus für erfolgreiche Gastronomen (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
        22 CRO Optimierung chemischer Reaktionen 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
        23 BIO Optimierung der Blutvererbung (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
        24 BSA Vogelschwarm-Algorithmus 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
        25 HS Harmoniesuche 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
        26 SSG Setzen, Säen und Wachsen 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
        27 BCOm Optimierung mit der bakteriellen Chemotaxis 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
        28 ABO Optimierung des afrikanischen Büffels 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
        29 (PO)ES (PO) Entwicklungsstrategien 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
        30 FBA Fraktal-basierter Algorithmus 0.79000 0.65134 0.28965 1.73099 0.87158 0.56823 0.18877 1.62858 0.61077 0.46062 0.12398 1.19537 4.555 50.61
        31 TSm Tabu-Suche 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
        32 BSO Brainstorming-Optimierung 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
        33 WOAm Wal-Optimierungsalgorithmus 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
        34 AEFA Algorithmus für künstliche elektrische Felder 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
        35 AEO Algorithmus zur Optimierung auf der Grundlage künstlicher Ökosysteme 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
        36 CAm Kamel-Algorithmus M 0.78684 0.56042 0.35133 1.69859 0.82772 0.56041 0.24336 1.63149 0.64846 0.33092 0.13418 1.11356 4.444 49.37
        37 ACOm Ameisen-Kolonie-Optimierung 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
        38 BFO-GA Optimierung der bakteriellen Futtersuche — 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
        39 SOA einfacher Optimierungsalgorithmus 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
        40 ABHA Algorithmus für künstliche Bienenstöcke 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
        41 ACMO Optimierung atmosphärischer Wolkenmodelle 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
        42 ADAMm adaptive Momentabschätzung 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
        43 CGO Chaos Game Optimierung 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
        44 CROm Korallenriff-Optimierung M 0.78512 0.46032 0.25958 1.50502 0.86688 0.35297 0.16267 1.38252 0.63231 0.26738 0.10734 1.00703 3.895 43.27
        45 ATAm Algorithmus für künstliche Stämme 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
        ES eagle_strategy_optimization 0.70776 0.43078 0.27475 1.41328 0.71737 0.34484 0.15973 1.22194 0.51846 0.27754 0.11063 0.90663 3.542 39.35
        RW Neuroboids Optimierungsalgorithmus 2(joo) 0.48754 0.32159 0.25781 1.06694 0.37554 0.21944 0.15877 0.75375 0.27969 0.14917 0.09847 0.52734 2.348 26.09


        Zusammenfassung

        Der ES-Algorithmus schnitt im Benchmark zur Populationsoptimierung durchschnittlich ab und erreichte 39 % der maximal möglichen Punkte, wodurch er nicht unter die 45 besten Algorithmen kam.

        Dennoch ist der Algorithmus aufgrund seines originellen zweistufigen Suchkonzepts, das auf einem biologisch inspirierten Modell des Jagdverhaltens von Adlern basiert, eine Überlegung wert. Die Verwendung von Levy-Flügen für die globale Erkundung ist eine mathematisch elegante Lösung, die sich theoretisch als optimal für Probleme der zufälligen Suche erwiesen hat. Adaptive Umsetzungsmechanismen, darunter der dynamische Wechsel zwischen den Phasen und die automatische Parameteranpassung während der Stagnationsphase, bieten Potenzial zur Verbesserung des Grundkonzepts.

        Der Algorithmus könnte sich in bestimmten Problemklassen bewähren, in denen ein Gleichgewicht zwischen globaler Suche und lokaler Ausnutzung wichtig ist, insbesondere bei Vorhandensein mehrerer lokaler Optima und verrauschter Daten. Die Einbindung des Firefly-Algorithmus in die lokale Suche schafft eine interessante Synergie zwischen zwei von der Natur inspirierten Methoden, auch wenn die Gesamtleistung darauf hindeutet, dass eine weitere Parameteroptimierung und möglicherweise eine Anpassung der zugrunde liegenden Mechanismen erforderlich sind.

        Der ES-Algorithmus kann als anschauliches Beispiel für einen hybriden Optimierungsansatz und als Grundlage für die Entwicklung fortgeschrittenerer Methoden angesehen werden, die verschiedene Metaheuristiken kombinieren. Dank seiner relativ einfachen Umsetzung und seiner intuitiven Logik eignet sich der Algorithmus für Forschungsexperimente im Bereich der evolutionären Optimierung.

        Tabelle

        Abbildung 2. Farbskala der Algorithmen nach den entsprechenden Tests

        Histogramm

        Abbildung 3. Histogramm der Algorithmus-Testergebnisse (Skala von 0 bis 100, je höher, desto besser, wobei 100 das maximal mögliche theoretische Ergebnis ist; im Archiv befindet sich ein Skript zur Berechnung der Bewertungstabelle)

        Vor- und Nachteile von ES:

        Vorteile:

        1. Schnell.

        Nachteile:

        1. Eine Vielzahl von Parametern.

        Dem Artikel ist ein Archiv mit den aktuellen Versionen der Algorithmuscodes beigefügt. Der Autor des Artikels übernimmt keine Verantwortung für die absolute Richtigkeit der Beschreibung der kanonischen Algorithmen. An vielen von ihnen wurden Änderungen vorgenommen, um die Suchmöglichkeiten zu verbessern. Die in den Artikeln dargelegten Schlussfolgerungen und Urteile beruhen auf den Ergebnissen der Experimente.


        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_ES.mq5
        Skript ES-Testumgebung

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

        Beigefügte Dateien |
        ES.zip (224.37 KB)
        Entwicklung eines Toolkits für die Price-Action-Analyse (Teil 29): Boom and Crash Interceptor EA Entwicklung eines Toolkits für die Price-Action-Analyse (Teil 29): Boom and Crash Interceptor EA
        Erfahren Sie, wie der „Boom & Crash Interceptor EA“ Ihre Charts in ein proaktives Warnsystem verwandelt – indem er explosive Kursbewegungen durch blitzschnelle Scans, Prüfungen auf Volatilitätsschübe, Trendbestätigungen und Pivot-Zone-Filter erkennt. Mit den klar erkennbaren Pfeilen, grün für „Boom“ und rot für „Crash“, die Sie bei jeder Entscheidung leiten, filtert dieses Tool das Marktrauschen heraus und ermöglicht es Ihnen, von Kurssprüngen zu profitieren wie nie zuvor. Tauchen Sie ein und erfahren Sie, wie es funktioniert und warum es zu Ihrem nächsten entscheidenden Vorteil werden kann.
        Marktsimulation (Teil 24): Erste Schritte mit SQL (VII) Marktsimulation (Teil 24): Erste Schritte mit SQL (VII)
        Im vorherigen Artikel haben wir die notwendige Einführung in SQL abgeschlossen. Und meiner Meinung nach haben wir klar dargelegt, was wir über SQL zeigen und erklären wollten. Dies geschah, damit sich jeder, der sich das derzeit im Aufbau befindliche Markt-Replay-/Simulationssystem ansieht, zumindest ein Bild davon machen kann, was dort vor sich geht. Der Punkt ist, dass es keinen Sinn ergibt, Funktionen selbst zu programmieren, die SQL bereits perfekt übernimmt.
        IWF-Daten mit Python herunterladen IWF-Daten mit Python herunterladen
        Die Daten des Internationalen Währungsfonds in Python abrufen: Auswertung von IWF-Daten zur Verwendung in makroökonomischen Währungsstrategien. Inwiefern kann die Makroökonomie einem normalen wie auch einem algorithmischen Trader helfen?
        Analyse der Bilanzdaten von Zentralbanken zur Einschätzung der globalen Liquidität Analyse der Bilanzdaten von Zentralbanken zur Einschätzung der globalen Liquidität
        Die Auswertung der Bilanzdaten der Zentralbanken vermittelt ein Bild der globalen Liquidität am Devisenmarkt und der Leitwährungen. Wir fassen Daten der Fed, der EZB, der BOJ und der PBoC zu einem zusammengesetzten Index zusammen und nutzen maschinelles Lernen, um verborgene Muster aufzudecken. Dieser Ansatz wandelt Rohdaten durch die Kombination von Fundamentalanalyse und technischer Analyse in konkrete Handelssignale um.