English Русский Español Português
preview
Backtracking-Suchalgorithmus (BSA)

Backtracking-Suchalgorithmus (BSA)

MetaTrader 5Handelssysteme |
10 0
Andrey Dik
Andrey Dik


Inhalt

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


Einführung

Im endlosen Labyrinth der Möglichkeiten, in dem jede Abzweigung entweder zum Triumph oder in eine Sackgasse führen kann, hinterlässt der kluge Reisende unsichtbare Spuren – etwas Vergängliches und doch Zuverlässigeres: die Erinnerung an die zurückgelegten Wege. Dieser Gedanke (in die Vergangenheit blicken, um die Zukunft zu sehen) bildet den Kern des Optimierungsalgorithmus. Jeder Schritt ins Unbekannte erfolgt unter Berücksichtigung vergangener Erfahrungen, wobei die Geschichte zum Kompass und die Erinnerung zur Landkarte wird.

In diesem Artikel werde ich mich mit einem Algorithmus befassen, den ich aufgrund seines Suchkonzepts sehr interessant fand. Der Backtracking-Suchalgorithmus (BSA) ist ein neuer evolutionärer Algorithmus (EA) zur Lösung realwertiger numerischer Optimierungsprobleme, der 2013 von Pinar Civicioglu vorgeschlagen wurde. Es handelt sich um eine Methode zur Ermittlung der besten Lösung, die „aus früheren Erfahrungen lernen“ kann. 


Implementierung des Algorithmus

BSA basiert auf dem Prinzip evolutionärer Algorithmen, weist jedoch einzigartige Merkmale auf und nutzt zwei Populationen:
  • aktuelle Population (P) – eine sich aktiv entwickelnde Population
  • historische Population (oldP) – eine zufällig ausgewählte Population aus früheren Generationen

BSA nutzt eine Strategie mit zufälligen Mutationen, die für jedes Individuum nur eine gerichtete Lösung erzeugt. Mutationsformel:

Mutant = P + F × (oldP - P)

wobei F der Amplitudenfaktor ist, der die Schrittweite der Suche bestimmt.

Der BSA-Crossover-Prozess umfasst zwei Schritte. Der erste Schritt nutzt „mixrate“. Beim zweiten Schritt darf in jedem Testvektor nur eine zufällig ausgewählte Koordinate verändert werden. Stellen Sie sich vor, Sie und Ihre Freunde (das ist unsere Population) sind auf der Suche nach der besten Pizzeria der Stadt. 

Ausgangssituation:

  • Sie haben 10 Freunde (Populationsgröße),
  • Jeder beginnt, in einem beliebigen Stadtteil zu suchen,
  • Jeder hat ein Smartphone mit einer Karte seiner vergangenen Reisen (historisches Gedächtnis).

Tag 1: Alle gingen in die Stadt. Der erste Freund fand eine Pizzeria mit einer Bewertung von 7/10, der zweite Freund fand eine Pizzeria mit einer Bewertung von 5/10, der dritte entdeckte eine Pizzeria mit einer Bewertung von 8/10 … und so weiter.

Tag 2: Nutzung der gesammelten Erfahrungen.

Schritt 1: „Erinnerungen an die Vergangenheit“ (Selection-I). Algorithmus: „Lass uns eine Münze werfen!“ Wenn die Münze auf Kopf fällt, dann „Erinnere dich daran, wo jeder gestern war“ (Historie aktualisieren); wenn sie auf Zahl fällt, „Verwende alte Erinnerungen“ (nicht aktualisieren), dann „Mische die Erinnerungen“ (wie ein Kartenspiel).

Schritt 2: „Folge der Spur“ (Mutation). Jeder Freund denkt: „Wo bin ich gerade? (aktuelle Position) und wo war ich zuvor? (frühere Position).“ Dann lautet die Bewegungsformel: Neue Position = Aktuelle Position + Zufallswert × (Alte Position – Aktuelle Position). Beispiel: Der erste Freund befindet sich jetzt in der Straße A, Nr. 10, sein „Geist aus der Vergangenheit“ war in der Straße B, Nr. 20, Zufallsschritt = 2. Neue Position = A.,10 + 2 × (B.,20 - A.,10) = Bewegung in Richtung B-Straße, aber doppelt so weit!

„Kurskorrektur“ (Crossover). Der Algorithmus wirft eine Münze und wählt eine Strategie aus. Strategie A: „Teilweise Änderung“. Der erste Freund wollte zu einer bestimmten Adresse gehen, aber der Algorithmus sagt: „Ändere nur die Straße, lass das Haus so, wie es war.“ Ergebnis: Okay, wir ändern die Straße, behalten aber das Haus. Strategie B: „Minimale Änderung“. Der erste Freund wollte zu einer bestimmten Adresse gehen, aber der Algorithmus sagt: „Bleib am alten Ort, aber ändere nur die Hausnummer.“ Ergebnis: Okay, wir ändern die Hausnummer.

„Gesamtergebnis prüfen“ (Selection-II). Freund 1 ist an einen neuen Ort gezogen:

  • Wenn die neue Pizzeria besser ist (Bewertung 9/10): „Super, ich bleibe hier!“
  • Ist sie schlechter (Bewertung 4/10): „Nein, ich komme zurück!“

Die folgende Abbildung veranschaulicht die Funktionsweise des Algorithmus.

bsa

Abb. 1. Phasen des BSA-Algorithmus

Diese Abbildung zeigt, wie der BSA-Algorithmus in einem zweidimensionalen Raum nach einer optimalen Lösung sucht. Stellen Sie sich vor, Sie betrachten eine topografische Karte von oben, wobei es darum geht, den höchsten Punkt (rot in der Mitte) zu finden.

Die Abbildung ist in drei Felder unterteilt, die die Historie der Suche zeigen: Iteration 1 – Initialisierung mit Zufallswerten. Das quadratische Suchfeld mit Konturlinien (wie auf einer topografischen Karte) weist in der Mitte einen roten Punkt auf, der das globale Optimum (die beste Lösung) darstellt, und vier blaue Punkte (P1, P2, P3, P4) sind die anfänglichen Zufallspositionen der „Suchagenten“. Der Algorithmus platziert vier Agenten zufällig im gesamten Suchraum. Zu diesem Zeitpunkt gilt oldP = P (die historische Population entspricht der aktuellen), und dies ist der Ausgangspunkt der Suche.

Iteration 2 – Mutationsschritt. Blaue Punkte stellen die aktuellen Positionen der Agenten dar, grüne, durchsichtige Punkte sind Positionen aus dem historischen Speicher (gemischt), rote Pfeile zeigen die Entwicklung des Suchprozesses während der Mutation an.

Schlüsselelemente: Die roten Pfeile veranschaulichen die Funktionsweise der Mutationsformel: M = P + F × (oldP - P). Jeder Agent bewegt sich im Verhältnis zu seinem „historischen Gegenstück“. Manche Pfeile zeigen auf historische Positionen, andere weg von ihnen (abhängig vom F-Zeichen). Formel im roten Kasten: F = 3 × randn(); M = P + F × (oldP - P). Dies ist die Schlüsselformel für die BSA-Mutation.

Iteration 3 – nach Crossover und Selektion. Lila Punkte kennzeichnen neue Positionen nach dem Crossover (Versuchspopulation), durchscheinende blaue Punkte zeigen frühere Positionen (zum Vergleich), und grüne Pfeile markieren ausschließlich Verbesserungen (d. h. Fälle, in denen die neue Position besser ist als die alte). Crossover: Der Algorithmus kombinierte Informationen aus der mutierten und der aktuellen Population. Bei der Greedy-Selektion wurden nur jene Änderungen beibehalten, die die Lösung verbesserten. Die Population hat sich dem Optimum angenähert (roter Mittelpunkt).

Zusammenfassung des BSA-Prozesses: Der gesamte Algorithmuszyklus als Abfolge farbiger Kreise:
  1. Init (blau) – zufälliger Start
  2. Select-I (grün) – Speicheraktualisierung mit einer Wahrscheinlichkeit von 50 %
  3. Mutieren (rot) – Anwendung der Mutationsformel
  4. Crossover (lila) – Kombination verschiedener Lösungen
  5. Bewerten (orange) – Fitnessberechnung
  6. Select-II (türkis) – eine Greedy-Selektion der Besten

Der gepunktete Pfeil zeigt an, dass der Vorgang so lange wiederholt wird, bis eine Konvergenz erreicht ist. Dieses Bild verdeutlicht, warum BSA so effektiv ist: Es „merkt“ sich, wo es bereits gewesen ist, und nutzt diese Informationen, um intelligenter zu suchen als bei einem einfachen Zufallsspaziergang.

Nun können wir uns dem BSA-Pseudocode zuwenden.

Arbeitsvorbereitung

Ausgangsparameter:

  • Populationsgröße festlegen 
  • Crossover-Parameter mixrate festlegen 
  • Erstelle leere Container für:
    • aktuelle Population
    • historische Population
    • mutierte Population
    • Versuchspopulation
    • Crossover-Karten für jedes Individuum

Initialisierung:

  • Ordne alle Individuen der historischen Population zufällig im Suchraum an
  • Berücksichtige die Diskretisierung, wenn der Schritt angegeben ist
  • Setze das Flag für „selection required“ auf „Nein“

Die Hauptschleife des Algorithmus

SCHRITT 1: Erster Start

Wenn dies die erste Iteration ist:

  • Verteile die aktuelle Population zufällig
  • Koordinaten diskretisieren
  • Markiere, dass die Initialisierung abgeschlossen ist
  • Beende den Schritt und warte auf die Berechnung der Fitness

SCHRITT 2: Greedy-Selektion (falls erforderlich)

Wenn das Flag „selection required“ gesetzt ist:

  • Vergleiche für jeden Agenten:
    • Wenn die neue Lösung schlechter ist als die gespeicherte, geben wir die alten Koordinaten und die Fitness zurück
    • Wenn es besser ist, behalten wir das neue.
  • Flag der Selektion zurücksetzen

SCHRITT 3: Den aktuellen Zustand speichern

Für jeden einzelnen Agenten:

  • Aktuellen Fitnesswert speichern
  • Speichern Sie die Kopie der aktuellen Koordinaten

SCHRITT 4: Das historische Gedächtnis aktualisieren (Selection-I)

  • Wirf eine Münze (50 % Wahrscheinlichkeit)
  • Wenn die Münze auf Kopf fällt:
    • Die gesamte aktuelle Population in den historischen Speicher kopieren
    • Fitness sichern
  • Mische die historische Population wie ein Kartenspiel:
    • Gehe von dem letzten Agenten zum ersten
    • Wähle für jedes Element eine zufällige Position für den Austausch aus
    • Vertausche

SCHRITT 5: Mutation

  • Erzeuge einen Bewegungsfaktor F aus einer Normalverteilung
    • Durchschnitt = 0, Bereich von -3 bis +3
    • Wie der Wurf eines Würfels, aber mit einer Glockenkurve
  • Für jeden Agenten und jede Koordinate:
    • Neue Position = Aktuell + F × (Historisch – Aktuell)
    • Wenn F positiv ist → Bewegung zur historischen Position
    • Wenn F negativ ist → Bewegung weg von der historischen Position

SCHRITT 6: Crossover

  • Kopiere die mutierte Population in die Versuchspopulation
  • Wirf eine gewichtete Münze (40 % / 60 %)

Wenn der 40-%-Zweig ausgewählt wird (Strategie 1): 

Für jedes Individuum:

  • Bestimme, wie viele Koordinaten geändert werden sollen (von 0 bis alle, unter Verwendung von mixrate)
  • Wähle die Koordinaten zufällig aus
  • Nimm für die ausgewählten Koordinaten Werte aus der aktuellen Population
  • Übernimm alle übrigen Koordinaten der Mutanten

Wenn das Ergebnis 60 % beträgt (Strategie 2):

Für jeden einzelnen Agenten:

  • Wähle nur eine zufällige Koordinate aus
  • Ersetze sie durch den Wert aus der aktuellen Population
  • Alle übrigen Koordinaten bleiben unverändert aus der mutierten Population.

SCHRITT 7: Kontrolle der Begrenzung

Für jeden einzelnen Agenten der Versuchspopulation:

  • Überprüfe jede Koordinate
  • Wenn die Grenzen überschritten werden:
    • Wirf eine Münze (50:50)
    • Bei „Kopf“ → generiere einen neuen Zufallswert innerhalb der Grenzen
    • Bei „Zahl“ → platziere an der nächstgelegenen Grenze
  • Diskretisierung anwenden, wenn der Schritt angegeben ist

SCHRITT 8: Bereite die Bewertung vor

  • Kopiere die Versuchspopulation zur Auswertung in die Hauptpopulation
  • Setze das Flag „selection required“ auf „Ja“
  • Die Kontrolle an die Fitnessberechnung übergeben

Nach der Berechnung der Fitness 

  • Finde den Agenten mit der besten Fitness
  • Wenn ein besserer Wert als der bisher globale Bestwert gefunden wird:
    • aktualisiere den globalen Bestwert
    • die Koordinaten der besten Lösung speichern

Wiederholung

Kehre zu SCHRITT 2 zurück und fahre fort, bis:

  • Konvergenz erreicht ist
  • oder die Iterationsgrenze überschritten wird

Nachdem wir nun die wichtigsten Punkte verstanden haben, fangen wir damit an, den Algorithmus zu programmieren. Die Klasse C_AO_BSA_Backtracking wird eine Implementierung des BSA-Backtracking-Algorithmus zur Lösung von Optimierungsproblemen darstellen. Sie wird die Basisklasse C_AO erben, die eine gemeinsame Schnittstelle für Optimierungsalgorithmen definiert. 

Hauptmerkmale und Zweck:

Optimierungsparameter:
  • popSize – die Populationsgröße, also die Anzahl der „Agenten“ oder „Lösungen“, die der Algorithmus gleichzeitig berücksichtigt. 
  • mixrate – ein Crossover-Parameter, der den Grad der „Vermischung“ von Informationen zwischen Agenten bei der Erzeugung neuer Lösungen steuert. 
Initialisierung:
  • Der Klassenkonstruktor initialisiert die Basisparameter und fügt „popSize“ und „mixrate“ dem Array „params“ hinzu, das zur Steuerung der Algorithmusparameter dient.
  • Mit der Methode „SetParams“ können Sie die internen Parameter des Algorithmus anhand der Werte aus dem Array „params“ aktualisieren. Außerdem umfasst es grundlegende Prüfungen der Gültigkeit der Werte.
Lebenszyklus eines Algorithmus (Methoden):
  • Init – für die Ersteinrichtung des Algorithmus, einschließlich der Festlegung der Variablenbereiche (Mindest-, Höchstwerte und Schrittweiten) sowie der Anzahl der Optimierungsiterationen.
  • Moving – bezeichnet die Hauptiteration des Algorithmus, die dafür zuständig ist, auf der Grundlage der aktuellen Population neue „Kandidaten“ oder „Moving“-Lösungen zu generieren.
  • Revision – bewertet die generierten Lösungen und aktualisiert das aktuell beste Ergebnis.
Interne Datenstrukturen für den Algorithmus:
  • oldP – ein Array, das die „historische“ oder frühere Population von Agenten darstellt.
  • M – ein Array für die durch die Mutation entstandene Population.
  • T – ein Array für die Versuchspopulation, das vor dem Treffen von Entscheidungen zum Vergleich mit der aktuellen Population herangezogen wird.
  • F – Amplitudenfaktor für Mutationen, der das Ausmaß der Veränderung bei einer Mutation beeinflusst.
  • needSelection – Flag, das angibt, dass die zweite Stufe der Selektion durchgeführt werden muss.
  • prevFitness – Array zur Speicherung der Fitnesswerte der Agenten aus der vorherigen Iteration.
  • S_Map – Hilfsstruktur, die eine binäre Maske enthält und während des Crossover-Prozesses verwendet wird, um zu bestimmen, welche Variablen der Agenten aus verschiedenen Elternteilen „gemischt“ werden.
  • map – ein Array dieser binären Masken, eine für jeden Agenten.
Die wichtigsten Schritte des Algorithmus (interne Methoden):
  • SelectionI – die erste Phase der Selektion, in der die Agenten für die Entwicklung neuer Lösungen bestimmt werden.
  • Mutation – Wende die Mutationsoperation auf die ausgewählten Agenten an.
  • Crossover – Führe einen Crossover-Vorgang (Kombination) zwischen Agenten durch, um neue „Test“-Lösungen zu erstellen.
  • BoundaryControl – Überprüft, ob die Werte der Agentenparameter innerhalb der festgelegten Grenzen (Mindest- und Höchstwerte) bleiben.
  • ShufflePopulation – Methode zum zufälligen Mischen einer Population.

Somit implementiert die Klasse C_AO_BSA_Backtracking einen evolutionären Optimierungsalgorithmus, der die Konzepte Population, Mutation und Crossover sowie BSA-spezifische Mechanismen wie Backtracking nutzt, um Optimierungsprobleme zu lösen.

//————————————————————————————————————————————————————————————————————
class C_AO_BSA_Backtracking : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_BSA_Backtracking () { }
  C_AO_BSA_Backtracking ()
  {
    ao_name = "BSA";
    ao_desc = "Backtracking Search Algorithm";
    ao_link = "https://www.mql5.com/en/articles/18568";

    popSize = 10;     // population size
    mixrate = 1.0;    // crossover parameter

    ArrayResize (params, 2);

    params [0].name = "popSize"; params [0].val = popSize;
    params [1].name = "mixrate"; params [1].val = mixrate;
  }

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

    // Check the parameters validity
    //if (popSize < 2) popSize = 2;
    if (mixrate < 0.0) mixrate = 0.0;
    if (mixrate > 1.0) mixrate = 1.0;
  }

  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 mixrate;        // crossover parameter

  private: //---------------------------------------------------------
  S_AO_Agent oldP [];    // historical population
  S_AO_Agent M    [];    // mutant population (Mutant)
  S_AO_Agent T    [];    // trial population (Trial)

  double F;              // amplitude factor for mutation
  bool   needSelection;  // flag for the necessity of executing Selection-II
  double prevFitness []; // array for storing previous fitness

  // Auxiliary structures for crossover
  struct S_Map
  {
      int val [];      // binary map for crossover

      void Init (int size)
      {
        ArrayResize (val, size);
        ArrayInitialize (val, 0);
      }
  };

  S_Map map [];        // array of binary maps for each agent

  // Algorithm methods
  void SelectionI        ();
  void Mutation          ();
  void Crossover         ();
  void BoundaryControl   (S_AO_Agent &agent);
  void ShufflePopulation (S_AO_Agent &pop []);
};
//————————————————————————————————————————————————————————————————————

Die Methode „Init“ ist dafür zuständig, den Algorithmus vor Beginn der Optimierung für den Betrieb vorzubereiten. Zunächst wird die grundlegende Initialisierungsfunktion aufgerufen, die die grundlegenden Parameter für die Wertebereiche der Variablen festlegt (Mindest- und Höchstwerte sowie Schrittweite). Wenn dieser grundlegende Schritt fehlschlägt, bricht die Methode ab und gibt einen Fehler zurück.

Anschließend werden die für den BSA-Algorithmus spezifischen internen Datenstrukturen zugewiesen und vorbereitet. Insbesondere werden Populations-Arrays erstellt und auf die erforderliche Größe gebracht: die historische Population oldP, die mutierte Population M, die Versuchspopulation T sowie ein Array mit binären Masken für den Crossover „map“ und das Array prevFitness zur Speicherung der vorherigen Fitnesswerte jedes Agenten. Das Flag für die Notwendigkeit einer zusätzlichen Selektion ist zunächst auf „false“ gesetzt.

Anschließend werden für jeden Agenten in der Population Initialisierungsmethoden aufgerufen, die die internen Strukturen der einzelnen Agenten entsprechend der Anzahl der Aufgabenparameter vorbereiten.

Anschließend wird die historische Population mit Anfangswerten gefüllt. Für jeden Agenten und jeden Parameter in seiner Menge wird ein Zufallswert innerhalb eines vorgegebenen Bereichs generiert; anschließend wird dieser Wert entsprechend einer vorgegebenen Änderungsschrittweite angepasst. Wenn alle diese Schritte erfolgreich abgeschlossen wurden, gibt die Methode einen Wert zurück, der anzeigt, dass das Algorithmusobjekt erfolgreich initialisiert wurde und für die weitere Optimierung bereit ist.

//————————————————————————————————————————————————————————————————————
//--- Initialization
bool C_AO_BSA_Backtracking::Init (const double &rangeMinP  [],
                                  const double &rangeMaxP  [],
                                  const double &rangeStepP [],
                                  const int epochsP = 0)
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //------------------------------------------------------------------
  // Initialize additional BSA structures
  ArrayResize (oldP, popSize);
  ArrayResize (M,    popSize);
  ArrayResize (T,    popSize);
  ArrayResize (map,  popSize);
  ArrayResize (prevFitness, popSize);

  needSelection = false;

  for (int i = 0; i < popSize; i++)
  {
    oldP [i].Init (coords);
    M    [i].Init (coords);
    T    [i].Init (coords);
    map  [i].Init (coords);
  }

  // Initialize oldP historical population
  for (int p = 0; p < popSize; p++)
  {
    for (int c = 0; c < coords; c++)
    {
      oldP [p].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
      oldP [p].c [c] = u.SeInDiSp (oldP [p].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }

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

Die Moving-Methode beschreibt den Hauptschritt des BSA-Optimierungsalgorithmus und ist für die Erzeugung neuer Lösungskandidaten in der Population verantwortlich. Beim ersten Aufruf der Methode, wenn das Flag „revision“ auf „false“ gesetzt ist, wird die aktive Population „a“ zunächst initialisiert. Für jeden Agenten in der Population und für jeden seiner Parameter wird ein Zufallswert innerhalb eines vorgegebenen Bereichs (Minimum und Maximum) generiert. Dieser Zufallswert wird dann entsprechend der festgelegten Schrittweite angepasst. Nach der Initialisierung wird das Flag „revision“ auf „true“ gesetzt (damit dieser Block bei nachfolgenden Aufrufen nicht ausgeführt wird), und das Flag „needSelection“ wird zurückgesetzt. Die Methode wird beendet.

Durchführung der Greedy-Selektion (Selection-II) (wird bei Bedarf durchgeführt): Wenn needSelection den Wert „true“ hat, bedeutet dies, dass im vorherigen Schritt eine neue Population generiert wurde und nun deren Fitness mit der Fitness der vorherigen Lösungen verglichen werden muss. Die Iteration wird für jeden Agenten in der Population durchgeführt. Der aktuelle Fitnesswert des Agenten „a[i].f“ (der für die „Versuchs“-Lösung berechnet wurde) wird mit seiner im Schritt zuvor ermittelten Fitness, die in prevFitness[i] gespeichert ist, verglichen. Wenn sich die „Test“-Lösung a [i] als schlechter als die vorherige erwiesen hat, werden die Koordinatenwerte des aktuellen Agenten „a [i].c“ aus „a [i].cP“ (den Koordinaten des Agenten im vorherigen Schritt) wiederhergestellt, und seine „a [i].f“-Fitness wird ebenfalls auf prevFitness [i] zurückgesetzt. Dadurch wird sichergestellt, dass sich der Zustand der Population nicht verschlechtert. Nachdem die Selektion abgeschlossen ist, wird das Flag „needSelection“ zurückgesetzt.

Grundlegende Schritte des BSA-Algorithmus (nach der Initialisierung oder Selektion): Vor der Erzeugung neuer Lösungen werden die aktuellen Fitnesswerte aller Agenten aus „a“ in prevFitness für die spätere Verwendung in Selection-II gespeichert und die aktuellen Koordinaten der Agenten aus „a [i].c“ werden in „a [i].cP“ kopiert, um ein Zurücksetzen zu ermöglichen, falls sich die neuen Lösungen als schlechter erweisen sollten.

Die interne Methode „SelectionI“ wird aufgerufen. Dieser Schritt dient der Selektion oder Vorbereitung der Operation „archive“ oldP, die für die Mutation verwendet wird. Die interne Methode „Mutation“ wird aufgerufen. In diesem Schritt wird auf der Grundlage der aktiven und/oder archivierten Populationen eine „mutierte“ Population M erzeugt. Die interne Methode „Crossover“ wird aufgerufen. In diesem Schritt wird die M-mutierte Population mit der derzeit aktiven „a“-Population vermischt, wodurch eine „Test“-Population T entsteht.

Die Koordinaten der Agenten aus der generierten T-Versuchspopulation werden in die aktive „a“-Population kopiert. Nun enthält „a“ neue „Test“-Lösungen, deren Fitness berechnet werden muss. Das Flag „needSelection“ ist auf „true“ gesetzt. Dies bedeutet, dass beim nächsten Aufruf von „Moving“ (nachdem die Fitness der neuen Lösungen in „a“ berechnet wurde) eine Greedy-Selektion (Selection-II) durchgeführt werden sollte.

    Die Methode Moving umfasst somit eine vollständige Iteration oder „Epoche“ des BSA-Algorithmus, einschließlich Initialisierung, Selektion, Mutation, Crossover und Vorbereitung für den Ergebnisvergleich.

    //————————————————————————————————————————————————————————————————————
    //--- The main step of the algorithm
    void C_AO_BSA_Backtracking::Moving ()
    {
      // Initial population setup
      if (!revision)
      {
        for (int p = 0; p < popSize; p++)
        {
          for (int c = 0; c < coords; c++)
          {
            a [p].c  [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
            a [p].c  [c] = u.SeInDiSp (a [p].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
    
        revision      = true;
        needSelection = false;
        return;
      }
    
      // If you want to perform greedy selection after calculating fitness
      if (needSelection)
      {
        // Selection-II: Greedy selection
        for (int i = 0; i < popSize; i++)
        {
          // If the current solution (from T) is worse than the previous one, return the previous one
          if (a [i].f < prevFitness [i])
          {
            ArrayCopy (a [i].c, a [i].cP, 0, 0, WHOLE_ARRAY);
            a [i].f = prevFitness [i];
          }
        }
    
        needSelection = false;
      }
    
      //--- BSA basic steps:
    
      // Save current fitness before generating a new population 
      for (int i = 0; i < popSize; i++)
      {
        prevFitness [i] = a [i].f;
        ArrayCopy (a [i].cP, a [i].c, 0, 0, WHOLE_ARRAY);
      }
    
      // 1. Selection-I
      SelectionI ();
    
      // 2. Mutation
      Mutation ();
    
      // 3. Crossover
      Crossover ();
    
      // 4. Copy the trial population T into the 'a' main population to calculate fitness
      for (int i = 0; i < popSize; i++)
      {
        ArrayCopy (a [i].c, T [i].c, 0, 0, WHOLE_ARRAY);
      }
    
      // Set the flag to execute Selection-II after calculating fitness
      needSelection = true;
    }
    //————————————————————————————————————————————————————————————————————
    

    Die Methode „SelectionI“ implementiert die erste Selektionsstufe des BSA-Algorithmus und ist für die Selektion der historischen Population zuständig, die anschließend für die Mutation verwendet wird.

    Probabilistische Aktualisierung der historischen Population: Mit einer Wahrscheinlichkeit von 50 % (oder, einfacher ausgedrückt, mit gleicher Wahrscheinlichkeit) wird die historische Population „oldP“ durch die aktuelle Population „a“ aktualisiert. Wird eine Zufallszahl generiert, die kleiner als ein vorgegebener Schwellenwert (0,5) ist, werden der Inhalt („c“, Koordinaten) jedes Agenten aus der aktuellen Population „a“ auf den entsprechenden Agenten in der historischen Population „oldP“ kopiert, und auch die Fitness „f“ des Agenten wird kopiert.

    Historische Populationsdaten mischen: Unabhängig davon, ob die historische Population im vorherigen Schritt aktualisiert wurde oder nicht, wird die Methode ShufflePopulation(oldP) aufgerufen, um die Agenten in der historischen Population neu zu mischen. Dies geschieht, um Zufälligkeit in die Mutation einzubringen. Durch das Mischen wird sichergestellt, dass die Agenten in der historischen Population in zufälliger Reihenfolge ausgewählt werden und nicht in der Reihenfolge, in der sie ursprünglich angeordnet waren.

      Somit aktualisiert „SelectionI“ entweder die historische Population mit aktuellen Lösungsvorschlägen oder behält ihren vorherigen Zustand bei; in beiden Fällen werden anschließend die Agenten in der historischen Population neu gemischt, was eine Diversifizierung der Suche während der anschließenden Mutation ermöglicht.

      //————————————————————————————————————————————————————————————————————
      //--- Selection-I: select a historical population
      void C_AO_BSA_Backtracking::SelectionI ()
      {
        // Update the historical population with a 50% probability 
        if (u.RNDprobab () < 0.5) // equivalent to if (a < b) where a,b ~ U(0,1)
        {
          // Copy the current population to the historical one
          for (int i = 0; i < popSize; i++)
          {
            ArrayCopy (oldP [i].c, a [i].c, 0, 0, WHOLE_ARRAY);
            oldP [i].f = a [i].f;
          }
        }
      
        // Shuffle the historical population
        ShufflePopulation (oldP);
      }
      //————————————————————————————————————————————————————————————————————
      

      Die Methode „ShufflePopulation“ dient dazu, Elemente in einer gegebenen Population von Agenten (dargestellt durch die Struktur „S_AO_Agent“) zufällig zu mischen. Die Funktion nimmt ein Array von pop[]-Agenten als Eingabe entgegen und führt eine In-Place-Mischung durch, d. h., sie ändert die Reihenfolge der Elemente direkt im übergebenen Array.

      Mischalgorithmus: Die Methode nutzt den Fisher-Yates-Shuffle-Algorithmus, um Elemente in einer Population zufällig zu mischen; der Algorithmus stellt sicher, dass jede Permutation (Reihenfolge) der Elemente die gleiche Wahrscheinlichkeit aufweist.

      Die „for“-Schleife beginnt beim letzten Element des Arrays (popSize - 1) und läuft in umgekehrter Reihenfolge bis zum zweiten Element (Index 1). Das erste Element (Index 0) muss nicht gemischt werden, da es zu diesem Zeitpunkt im Ablauf des Algorithmus bereits „verschoben“ worden ist. Für jedes Element mit dem Index i wird ein zufälliger Index j im Bereich von 0 bis einschließlich i ausgewählt. Dies geschieht mithilfe der Funktion u.RNDminusOne(i+1), die eine zufällige Ganzzahl im angegebenen Bereich zurückgibt (einschließlich „0“ und ohne „i+1“).

      Elemente mit den Indizes i und j werden vertauscht. Zu diesem Zweck wird die temporäre Variable „temp“ vom Typ S_AO_Agent verwendet. Da S_AO_Agent das Array „c“ (Koordinaten) enthält, wird das Array kopiert. Der f-Fitness-Wert wird ebenfalls kopiert. Die Koordinaten- und Fitnesswerte des Elements pop[i] werden in der temporären Variablen temp gespeichert, und die Koordinaten- und Fitnesswerte des Elements pop[j] werden in pop[i] kopiert. Die in „temp“ gespeicherten Werte (die ursprünglichen Werte von pop[i]) werden in pop[j] kopiert. Daher ist die Reihenfolge der Elemente im Array pop[] nach Abschluss der Schleife zufällig.

      //————————————————————————————————————————————————————————————————————
      //--- Shuffle population
      void C_AO_BSA_Backtracking::ShufflePopulation (S_AO_Agent &pop [])
      {
        for (int i = popSize - 1; i > 0; i--)
        {
          int j = u.RNDminusOne (i + 1);
      
          // Swap i and j elements
          S_AO_Agent temp;
          temp.Init (coords);
      
          ArrayCopy (temp.c, pop [i].c, 0, 0, WHOLE_ARRAY);
          temp.f = pop [i].f;
      
          ArrayCopy (pop [i].c, pop [j].c, 0, 0, WHOLE_ARRAY);
          pop [i].f = pop [j].f;
      
          ArrayCopy (pop [j].c, temp.c, 0, 0, WHOLE_ARRAY);
          pop [j].f = temp.f;
        }
      }
      //————————————————————————————————————————————————————————————————————
      

      Die Mutationsmethode ist für die Erzeugung der mutierten Population zuständig, was einen entscheidenden Schritt im BSA-Algorithmus darstellt. Sie basiert auf der Differenz zwischen der aktuellen und der historischen Population.

      Zunächst wird ein zufälliger Amplitudenfaktor F generiert, der den Einfluss der historischen Population auf die aktuelle Population bestimmt. Anschließend wird für jeden Agenten in der Population und für jede Koordinate innerhalb dieses Agenten ein neuer Wert für die Koordinate in der mutierten Population berechnet. Dies geschieht, indem man zur aktuellen Koordinate das Produkt aus dem F-Amplitudenfaktor und der Differenz zwischen der entsprechenden Koordinate in der historischen Population und der aktuellen Koordinate addiert. So entsteht eine mutierte Population, indem die aktuelle Population in die durch die historische Population vorgegebene Richtung verschoben wird, wobei die Amplitude vom F-Faktor abhängt.

      //————————————————————————————————————————————————————————————————————
      //--- Mutation: generation of a mutant population
      void C_AO_BSA_Backtracking::Mutation ()
      {
        // Generate the amplitude factor
        F = u.GaussDistribution (0.0, -3.0, 3.0, 2);
      
        // Apply mutation: M = P + F * (oldP - P)
        for (int i = 0; i < popSize; i++)
        {
          for (int j = 0; j < coords; j++)
          {
            M [i].c [j] = a [i].c [j] + F * (oldP [i].c [j] - a [i].c [j]);
          }
        }
      }
      //————————————————————————————————————————————————————————————————————
      

      Die Crossover-Methode dient dazu, auf der Grundlage der aktuellen mutierten Population unter Verwendung der gewählten Crossover-Strategie eine Versuchspopulation zu bilden. Dieser Prozess zielt darauf ab, die Eigenschaften bestehender Lösungen zu kombinieren, um optimalere Optionen zu finden.

      Zunächst wird eine Versuchspopulation aus der mutierten Population kopiert, um eine Grundlage für weitere Modifikationen zu schaffen. Dann muss eine Entscheidung über die Kreuzungsstrategie getroffen werden: Mit einer Wahrscheinlichkeit von 40 % wird die erste Strategie gewählt, andernfalls die zweite.

      Wird die erste Option gewählt, wird die Strategie „mixrate“ umgesetzt. In diesem Fall wird für jeden Agenten die Anzahl der Elemente (innerhalb der Koordinaten) bestimmt, die vom aktuellen Agenten übernommen werden, wobei der angegebene Koeffizient „mixrate“ berücksichtigt wird. Diese Elemente werden nach dem Zufallsprinzip ausgewählt, ohne Wiederholungen. Anschließend werden die entsprechenden Koordinaten aus der aktuellen Lösung von den ausgewählten Indizes in den Agenten der Versuchspopulation kopiert.

      Wird die zweite Strategie gewählt, wird für jeden Agenten ein Zufallselement (Koordinate) ausgewählt, und in der Versuchspopulation wird dieses Element an der entsprechenden Position durch seinen Wert aus der aktuellen Lösung ersetzt.

      Nach Abschluss des Crossover-Schritts wird für jeden Agenten in der Versuchspopulation eine Bereichsprüfung durchgeführt, um sicherzustellen, dass alle Entscheidungen gültig sind und die Bereichsanforderungen erfüllen.

      //————————————————————————————————————————————————————————————————————
      //--- Crossover: trial population generation
      void C_AO_BSA_Backtracking::Crossover ()
      {
        // Initialize the trial population as a copy of the mutant one
        for (int i = 0; i < popSize; i++)
        {
          ArrayCopy (T [i].c, M [i].c, 0, 0, WHOLE_ARRAY);
        }
      
        // Select a crossover strategy
        if (u.RNDprobab () < 0.4)
        {
          //--- STRATEGY 1: Using mixrate
          for (int i = 0; i < popSize; i++)
          {
            // Reset the map
            ArrayInitialize (map [i].val, 0);
      
            // Define the number of elements for the crossover
            int numElements = (int)MathCeil (mixrate * u.RNDprobab () * coords);
      
            // Generate unique indices for the crossover
            for (int n = 0; n < numElements; n++)
            {
              int idx;
              do
              {
                idx = u.RNDminusOne (coords);
              }
              while (map [i].val [idx] == 1); // until we find an unused index
      
              map [i].val [idx] = 1;
            }
      
            // Apply crossover
            for (int j = 0; j < coords; j++)
            {
              if (map [i].val [j] == 1)
              {
                T [i].c [j] = a [i].c [j];
              }
            }
          }
        }
        else
        {
          //--- STRATEGY 2: Mutation of only one element
          for (int i = 0; i < popSize; i++)
          {
            // Select one random element
            int randomIndex = u.RNDminusOne (coords);
            T [i].c [randomIndex] = a [i].c [randomIndex];
          }
        }
      
        // Boundary control for all agents in the trial population
        for (int i = 0; i < popSize; i++)
        {
          BoundaryControl (T [i]);
        }
      }
      //————————————————————————————————————————————————————————————————————
      

      Die Methode „BoundaryControl“ dient dazu, die Parameterwerte des Agenten zu überprüfen und anzupassen, um sicherzustellen, dass sie innerhalb akzeptabler Grenzen bleiben, sowie die Entscheidungen in das gewünschte diskrete Format zu konvertieren.

      Für jede Komponente der Koordinaten des Agenten wird eine Überprüfung durchgeführt: Liegt der Wert außerhalb der festgelegten Mindest- oder Höchstgrenzen, wird dieser Wert gemäß der gewählten Strategie verarbeitet. Wird die Wahrscheinlichkeit zufällig auf einen Wert unter 50 % festgelegt, erfolgt eine zufällige Neuberechnung eines Wertes innerhalb des zulässigen Bereichs. Andernfalls wird der Wert auf den nächstgelegenen Grenzwert gesetzt – entweder auf das Minimum oder das Maximum.

      Anschließend wird jeder Koordinatenwert diskretisiert – das heißt, er wird in den nächstgelegenen zulässigen Wert umgewandelt, der dem festgelegten Bereich und der Diskretisierungsschrittweite entspricht. Dadurch wird sichergestellt, dass die Lösung die Anforderungen an Datentyp und Wertebereich erfüllt.

      //————————————————————————————————————————————————————————————————————
      //--- Boundary control
      void C_AO_BSA_Backtracking::BoundaryControl (S_AO_Agent &agent)
      {
        for (int j = 0; j < coords; j++)
        {
          if (agent.c [j] < rangeMin [j] || agent.c [j] > rangeMax [j])
          {
            // Select a boundary handling strategy
            if (u.RNDprobab () < 0.5)
            {
              // Random regeneration
              agent.c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
            }
            else
            {
              // Set to the boundary
              if (agent.c [j] < rangeMin [j]) agent.c [j] = rangeMin [j];
              else agent.c [j] = rangeMax [j];
            }
          }
      
          // Discretization
          agent.c [j] = u.SeInDiSp (agent.c [j], rangeMin [j], rangeMax [j], rangeStep [j]);
        }
      }
      //————————————————————————————————————————————————————————————————————
      

      Die Methode Revision ist dafür zuständig, die beste Lösung aus der aktuellen Population auszuwählen und zu aktualisieren. Es durchläuft alle Lösungen und sucht dabei nach derjenigen mit dem höchsten Wert der Bewertungsfunktion (Fitness). Wird eine solche Lösung gefunden, wird sie als derzeit bestes Ergebnis gespeichert, und ihre Koordinaten werden in ein separates Array kopiert, das dazu dient, die aktuell beste Lösung zu speichern. Das Verfahren ermöglicht somit eine kontinuierliche Verfolgung und Aktualisierung der während der Algorithmus-Iterationen ermittelten optimalen Lösung.

      //————————————————————————————————————————————————————————————————————
      //--- Selection-II and updating the best solution
      void C_AO_BSA_Backtracking::Revision ()
      {
        int bestIND = -1;
      
        for (int i = 0; i < popSize; i++)
        {
          // Update the global best solution
          if (a [i].f > fB)
          {
            fB = a [i].f;
            bestIND = i;
          }
        }
      
        // Copy the coordinates of the best solution
        if (bestIND != -1)
        {
          ArrayCopy (cB, a [bestIND].c, 0, 0, WHOLE_ARRAY);
        }
      }
      //————————————————————————————————————————————————————————————————————
      

      Die Methode GaussDistribution generiert eine Zufallszahl mit einer gaußschen (Normal-)Verteilung, deren Mittelwert auf einem vorgegebenen Eingabewert liegt und die auf einen bestimmten Bereich begrenzt ist. Dabei wird das Box-Muller-Verfahren zur Erzeugung normalverteilter Zufallsvariablen verwendet.

      Zunächst erzeugt es zwei gleichverteilte Zufallsvariablen. Anschließend wird auf der Grundlage dieser Werte eine normalverteilte Standardzufallsvariable berechnet, mit deren Hilfe eine Gauß-Verteilung ermittelt werden kann.

      Die Methode prüft dann, ob der erzeugte Wert innerhalb des durch den Sigma-Parameter definierten Abweichungsbereichs liegt. Liegt der erzeugte Wert außerhalb dieser Grenzen, wird die Methode erneut rekursiv aufgerufen, um einen neuen Wert zu erzeugen, bis dieser innerhalb des zulässigen Bereichs liegt.

      Schließlich wird die erzeugte normalverteilte Varianz so skaliert, dass sie im Verhältnis zum Eingabewert „In“ in den vorgegebenen Ausgabebereich (von outMin bis outMax) passt. Dadurch können wir die Verteilung an Ihre gewünschten Parameter anpassen und skalieren. Das Ergebnis ist eine nach einer Gauß-Verteilung verteilte Zahl mit dem Mittelpunkt bei In, die jedoch den Einschränkungen hinsichtlich des minimalen und maximalen Ausgabewerts unterliegt.

      //——————————————————————————————————————————————————————————————————————————————
      double C_AO_Utilities :: GaussDistribution (const double In, const double outMin, const double outMax, const double sigma)
      {
        double logN = 0.0;
        double u1   = 2.0 * MathRand () / 32767.0 - 1.0;//RNDfromCI (0.0, 1.0);
        double u2   = 2.0 * MathRand () / 32767.0 - 1.0;//RNDfromCI (0.0, 1.0);
      
        logN = u1 <= 0.0 ? 0.000000000000001 : u1;
      
        double z0 = sqrt (-2 * log (logN)) * cos (2 * M_PI * u2);
      
        double sigmaN = sigma > 8.583864105157389 ? 8.583864105157389 : sigma;
        
        // If z0 is outside the range [-sigmaN, sigmaN], generate anew
        if (z0 >= sigmaN || z0 <= -sigmaN) 
        {
          return GaussDistribution(In, outMin, outMax, sigma); // Recursive call
        }
      
        if (z0 >= 0.0) z0 =  Scale (z0,        0.0, sigmaN, 0.0, outMax - In, false);
        else           z0 = -Scale (fabs (z0), 0.0, sigmaN, 0.0, In - outMin, false);
        
        return In + z0;
      }
      //——————————————————————————————————————————————————————————————————————————————
      


      Testergebnisse

      Der BSA-Algorithmus liefert sehr gute Ergebnisse.

      BSA|Backtracking Search Algorithm|10.0|1.0|
      =============================
      5 Hilly's; Func runs: 10000; result: 0.9730917210619289
      25 Hilly's; Func runs: 10000; result: 0.5453406317593932
      500 Hilly's; Func runs: 10000; result: 0.2909827609772065
      =============================
      5 Forest's; Func runs: 10000; result: 0.9999986842258451
      25 Forest's; Func runs: 10000; result: 0.5854340780208712
      500 Forest's; Func runs: 10000; result: 0.21747482800959225
      =============================
      5 Megacity's; Func runs: 10000; result: 0.8476923076923077
      25 Megacity's; Func runs: 10000; result: 0.3695384615384615
      500 Megacity's; Func runs: 10000; result: 0.12978461538461658
      =============================
      All score: 4.95934 (55.10%)

      Bei der Visualisierung des BSA-Algorithmus ist eine deutliche Streuung der Resultate bei Funktionen kleiner und mittlerer Dimension zu erkennen; mit steigenden Parametern für die Populationsgröße nimmt die Streuung jedoch ab, doch für eine bessere Konvergenz ist es notwendig, die Anzahl der Iterationen zu erhöhen.

      Hilly

      BSA mit der Testfunktion Hilly

      Forest

      BSA mit der Testfunktion Forest

      Megacity

      BSA mit der Testfunktion Megacity

      Der BSA-Algorithmus belegt Platz 20 in der Rangliste der populationsbasierten Optimierungsalgorithmen.

      # 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 BSA Backtracking-Suchalgorithmus 0.97309 0.54534 0.29098 1.80941 0.99999 0.58543 0.21747 1.80289 0.84769 0.36953 0.12978 1.34700 4.959 55.10
      21 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
      22 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
      23 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
      24 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
      25 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
      26 DEA Algorithmus zur Echoortung bei Delfinen 0.75995 0.67572 0.34171 1.77738 0.89582 0.64223 0.23941 1.77746 0.61538 0.44031 0.15115 1.20684 4.762 52.91
      27 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
      28 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
      29 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
      30 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
      31 (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
      32 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
      33 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
      34 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
      35 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
      36 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
      37 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
      38 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
      39 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
      40 CMAES Anpassung der Kovarianzmatrix mittels Evolutionsstrategie 0.76258 0.72089 0.00000 1.48347 0.82056 0.79616 0.00000 1.61672 0.75846 0.49077 0.00000 1.24923 4.349 48.33
      41 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
      42 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
      43 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
      44 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
      45 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
      RW Random Walk 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

      BSA stellt einen Kompromiss zwischen einfacher Implementierung und Sucheffizienz dar und nimmt einen stabilen Platz im Mittelfeld der Rangliste der populationsbasierten Optimierungsalgorithmen ein. Sein Hauptvorteil ist das interessante Konzept des historischen Gedächtnisses, das es dem Algorithmus ermöglicht, eine vorzeitige Konvergenz zu vermeiden, ohne das Rechenschema zu verkomplizieren. Im Gegensatz zu vielen modernen Metaheuristiken, die mit Parametern und komplexen Operatoren überladen sind, erfordert BSA nur einen minimalen Satz an Einstellungen, was es zu einer attraktiven Wahl für Praktiker macht, die keine Zeit mit der Feinabstimmung externer Parameter verbringen möchten.

      Der einzige wichtige Parameter (Mixrate) ist intuitiv und erfordert kein tiefgreifendes Verständnis der internen Funktionsweise des Algorithmus. Gleichzeitig zeigt BSA stabile Ergebnisse bei einer breiten Palette von Problemen – von einfachen unimodalen Funktionen bis hin zu komplexen Landschaften mit mehreren Extrema und zahlreichen lokalen Optima. Der Algorithmus erhebt zwar keinen Anspruch darauf, in puncto Konvergenzgeschwindigkeit oder Lösungsgenauigkeit unübertroffen zu sein, doch seine Zuverlässigkeit und Vorhersehbarkeit machen ihn zu einem bewährten Arbeitstier. Besonders wertvoll ist, dass BSA bei wachsender Population nur in geringem Maße anfällig für Stagnation ist – der Mechanismus der zufälligen Erneuerung der historischen Population sorgt selbst in den späten Phasen der Suche für einen konstanten Zufluss an Vielfalt.

      Seine Platzierung im Mittelfeld der Rangliste ist kein Zeichen von Mittelmäßigkeit, sondern vielmehr ein Beweis für seine Vielseitigkeit und Praxistauglichkeit, da die Lösung realer Probleme nicht immer das komplexeste und modernste Werkzeug erfordert. Manchmal reicht ein robuster Algorithmus mit klarer Funktionslogik aus, der ohne fachmännische Feinabstimmung gute Ergebnisse liefert, und BSA füllt diese Nische erfolgreich aus.

      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 der BSA:

      Vorteile:

      1. Gute Konvergenz bei allen getesteten Funktionen.
      2. Abgesehen von der Populationsgröße gibt es nur einen weiteren externen Parameter.

      Nachteile:

      1. Es besteht eine gewisse Tendenz, bei Problemen mit geringer Dimension und kleinen Populationen hängen zu bleiben.

      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_BSA.mq5
      Skript BSA-Testumgebung

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

      Beigefügte Dateien |
      BSA.zip (239.66 KB)
      Analyse von Kurs-Zeit-Lücken in MQL5 (Teil I): Entwicklung eines einfachen Indikators Analyse von Kurs-Zeit-Lücken in MQL5 (Teil I): Entwicklung eines einfachen Indikators
      Die Zeitlückenanalyse hilft Händlern dabei, potenzielle Wendepunkte am Markt zu erkennen. Der Artikel befasst sich damit, was eine Zeitlücke ist, wie man sie interpretiert und wie sie genutzt werden kann, um starken Volumenzustrom in den Markt zu erkennen.
      Untersuchung von Conformal Prediction bei Finanzzeitreihen Untersuchung von Conformal Prediction bei Finanzzeitreihen
      In diesem Artikel befassen wir uns mit konformen Vorhersagen und der MAPIE-Bibliothek, die diese implementiert. Dieser Ansatz gehört zu den modernsten im Bereich des maschinellen Lernens und ermöglicht es uns, uns auf das Risikomanagement für bestehende, vielfältige Modelle des maschinellen Lernens zu konzentrieren. Konforme Vorhersagen sind an sich kein Verfahren zur Erkennung von Mustern in Daten. Sie geben lediglich das Konfidenzniveau bestehender Modelle bei der Vorhersage konkreter Beispiele an und ermöglichen die Filterung zuverlässiger Vorhersagen.
      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.
      Ermittlung fairer Wechselkurse anhand von KKP- und IWF-Daten Ermittlung fairer Wechselkurse anhand von KKP- und IWF-Daten
      Entwicklung eines Python-Systems zur Wechselkursanalyse auf Basis der Kaufkraftparität (KKP). Der Autor entwickelte einen Algorithmus mit fünf Methoden zur Berechnung fairer Wechselkurse auf Basis von IWF-Daten. Ein praktischer Leitfaden zur fundamentalen Währungsanalyse, zur Auswertung wirtschaftlicher Daten und zur Integration in Handelssysteme. Der vollständige Code ist als Open Source verfügbar.