English Русский 中文 Deutsch 日本語 Português
preview
Estrategia del águila — Eagle Strategy (ES)

Estrategia del águila — Eagle Strategy (ES)

MetaTrader 5 — Trading |
21 0
Andrey Dik
Andrey Dik

Índice

  1. Introducción
  2. Implementación del algoritmo
  3. Resultados de las pruebas


Introducción

En el mundo de la programación, especialmente en el desarrollo de asesores expertos de trading, una optimización eficaz desempeña un papel fundamental. Las tareas altamente especializadas que consisten en buscar la solución óptima entre un enorme conjunto de opciones posibles requieren el uso de algoritmos avanzados. Los métodos tradicionales de optimización suelen resultar ineficaces, ya que se quedan atascados en extremos locales y no encuentran la mejor solución global.

En este sentido, cada vez se presta más atención a los algoritmos metaheurísticos, inspirados en la propia naturaleza, métodos que han experimentado una evolución natural. Estos algoritmos son capaces de encontrar soluciones buenas y, a menudo, cercanas a las óptimas, incluso en problemas en los que la velocidad de cálculo es de vital importancia.

En un contexto de búsqueda constante de soluciones para problemas cada vez más complejos y que requieren muchos recursos, hoy analizaremos otro intento prometedor: el algoritmo Eagle Strategy (ES). Este algoritmo, inspirado en el comportamiento de caza del águila, constituye una nueva metaheurística diseñada para resolver problemas de optimización mediante la imitación de la estrategia de búsqueda visual y persecución dinámica de la presa.


Implementación del algoritmo

Imagine un águila real que planea a gran altura en busca de una presa. Su estrategia de caza es sorprendentemente eficaz y consta de dos fases bien definidas: primero vuela a gran altura, inspeccionando un territorio enorme con movimientos caóticos en zigzag, y luego, al avistar a su presa, se lanza en picado a gran velocidad, concentrando todos sus esfuerzos en una presa concreta. Precisamente esta sabiduría natural sirvió de base para el algoritmo Eagle Strategy (Estrategia del águila), desarrollado en 2010 para resolver problemas complejos de optimización.

En la primera etapa, el algoritmo utiliza los denominados «vuelos de Lévy», un modelo matemático que describe el movimiento en el espacio. A diferencia de la caminata aleatoria habitual, en la que los pasos son más o menos iguales, el vuelo de Lévy consiste en una sucesión de pequeños pasos intercalados con saltos poco frecuentes, pero muy largos.

Cuando el algoritmo encuentra una zona prometedora (como un águila que ha avistado a su presa), se activa la segunda fase: una búsqueda local intensiva mediante el algoritmo de las luciérnagas (Firefly), un algoritmo muy conocido que ya hemos analizado en uno de nuestros artículos. Varios agentes de búsqueda, como luciérnagas en la noche, se sienten atraídos por los vecinos más brillantes (exitosos). El brillo de una luciérnaga se corresponde con la calidad de la solución que ha encontrado, y su fuerza de atracción disminuye con la distancia siguiendo una ley exponencial. Esto crea un equilibrio entre la exploración de los entornos y el avance hacia la mejor solución conocida.

La innovación clave del ES radica en la alternancia cíclica de estos dos modos. Tras una búsqueda local intensiva, el algoritmo vuelve a pasar a la exploración global, dando un gran salto hacia una nueva zona inexplorada del espacio de búsqueda. Esto evita la convergencia prematura y permite encontrar el óptimo global incluso en paisajes muy complejos con numerosos extremos locales.

Los parámetros del algoritmo son intuitivos y fáciles de configurar. El tamaño de la población determina el número de agentes que exploran simultáneamente; el parámetro de Lévy controla la relación entre los saltos cortos y los largos, el radio de la hiperesfera define el área de búsqueda local intensiva, y el coeficiente de aleatorización añade un elemento de azar para salir de situaciones sin salida. Esta flexibilidad permite adaptar el algoritmo a una tarea concreta sin necesidad de tener un conocimiento profundo de la teoría matemática.

La filosofía del algoritmo ES es sencilla y elegante: una visión global combinada con precisión local. Del mismo modo que el águila combina un vuelo de reconocimiento con un ataque certero, el algoritmo busca el equilibrio entre la exploración de áreas desconocidas y la explotación de las soluciones prometedoras que encuentra.

Las características clave del algoritmo son: el cambio automático entre fases cuando mejora la solución, los parámetros adaptativos (λ disminuye en caso de estancamiento, el tamaño del paso se reduce con el tiempo) y el equilibrio entre la exploración de nuevas áreas y la explotación de las soluciones encontradas.

eagle_strategy

Figura 1. Ilustración del funcionamiento del algoritmo ES

La ilustración muestra las dos fases de funcionamiento del algoritmo: las trayectorias de los vuelos de Lévy (líneas rojas punteadas), la hiperesfera de búsqueda local (círculo azul), el movimiento de las luciérnagas dentro de la esfera (puntos verdes con halos) y las líneas de contorno de la función de optimización.

Pasemos a escribir el pseudocódigo del algoritmo.

INICIALIZACIÓN:
1. Crear una población de agentes con posiciones aleatorias
2. Establecer el indicador de búsqueda global (fase = global)
3. Inicializar los contadores de estancamiento y progreso

CICLO PRINCIPAL (hasta que se cumpla el criterio de parada):
  
SI (fase = global):
PARA cada agente:
- generar un paso de Lévy utilizando el algoritmo de Mantegna
- calcular la escala adaptativa del paso (mayor al principio, menor al final)
- actualizar la posición: nueva_posición = posición_actual + paso de Lévy × escala
- aplicar las restricciones de contorno
    
SI (se ha encontrado una mejora del mejor global):
- Cambiar a la fase local
- Encontrar al mejor agente como centro de la búsqueda local
- Reiniciar el contador de estancamiento
SINO:
- Aumentar el contador de estancamiento
- SI (estancamiento > 5 iteraciones):
- Reducir el parámetro λ para vuelos más agresivos
  
SINO (fase = local):
Con una probabilidad del 80 %:
- Determinar los agentes en la hiperesfera de radio 0,1 alrededor del mejor
- SI (agentes < 5):
- Seleccionar los 5 vecinos más cercanos o el 30 % de la población
      
PARA cada agente del grupo local:
PARA cada uno de los demás agentes del grupo:
SI (el otro agente es mejor):
- calcular el atractivo β = β₀ × exp(-γ × distancia²)
- desplazar al agente: posición += β × (mejor - actual) + ruido_aleatorio
    
Con una probabilidad del 20 %:
- Copiar parcialmente las coordenadas del mejor global en agentes aleatorios
    
- Incrementar el contador de iteraciones locales
- SI (se han realizado 20 iteraciones locales):
- Volver a la fase global
- Restaurar el parámetro original λ

ACTUALIZACIÓN:
PARA cada agente:
- calcular la aptitud (fitness)
- actualizar el mejor personal
- actualizar el mejor global

En esta implementación del algoritmo ES, para generar números con distribución de Lévy se emplea un método numérico: el algoritmo de Mantegna, propuesto por R. N. Mantegna en 1994 y se convirtió en el método estándar para simular los vuelos de Lévy en los algoritmos de optimización. Mantegna demostró matemáticamente que la relación entre dos magnitudes gaussianas escaladas específicamente da lugar a una distribución muy similar a la distribución de Lévy para un rango de valores relevante en aplicaciones prácticas. Su esencia consiste en lo siguiente:

// Cálculo de sigma para el algoritmo de Mantegna
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);

// Generación de u y v a partir de distribuciones normales
double u_val = GenerateGaussian() * sigma;
double v_val = MathAbs(GenerateGaussian());

// Cálculo del paso de Lévy
levyStep[c] = u_val / MathPow(v_val, 1.0 / lambda);

Tomemos dos variables aleatorias:

  • u: procedente de una distribución normal N(0, σ²)
  • v: procedente de una distribución normal N(0, 1)
Fórmula especial para σ:
σ = [Γ(1+λ) × sin(πλ/2) / (Γ((1+λ)/2) × λ × 2^((λ-1)/2))]^(1/λ)

donde Γ es la función gamma y λ es el parámetro de la distribución de Lévy (1 < λ ≤ 3).

Es importante señalar que, al definir la función gamma (para calcular el parámetro σ en el método de Mantegna), se utiliza la aproximación de Lanczos, un método numérico de alta precisión para calcular la función gamma. Es una de las formas más eficaces de calcular Γ(z) y, en el código, se implementa mediante una función independiente, que analizaremos al final.

La fórmula principal es la siguiente:

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

donde: g es un parámetro (normalmente 7); Ag(z) es una serie con coeficientes calculados previamente que, para g = 7 y 9 coeficientes, ofrece una precisión de aproximadamente 15 cifras significativas.

Para z < 0,5 se utiliza la relación de la fórmula de reflexión, lo que permite calcular la función gamma para todos los números reales:

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

Sin un cálculo eficaz de la función gamma, la generación de vuelos de Lévy sería computacionalmente costosa, lo que ralentizaría considerablemente todo el algoritmo de optimización.

Paso de Lévy final:

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

El algoritmo genera una secuencia de pasos con un comportamiento característico de los vuelos de Lévy: muchos pasos pequeños (exploración local), saltos poco frecuentes pero muy grandes (exploración global) y una ley de potencias para la distribución de las longitudes de los pasos.

Las ventajas de este método son la sencillez de la solución —puesto que solo se necesita un generador de distribución normal— y la estabilidad, es decir, la robustez numérica del algoritmo. Es precisamente esta propiedad la que hace que los vuelos de Lévy sean eficaces para la optimización: proporcionan un equilibrio óptimo entre la exploración detallada de las áreas locales y la transición rápida a nuevas regiones del espacio de búsqueda.

Tras analizar en detalle los métodos utilizados en el algoritmo ES, podemos pasar directamente a escribir la clase «C_AO_ES», que representa la implementación del método de optimización basado en la estrategia de caza del águila y hereda de la clase base «C_AO». El método utiliza un enfoque en dos fases: primero se lleva a cabo una búsqueda global para identificar áreas potencialmente prometedoras y, a continuación, una búsqueda local dentro de la parte seleccionada del espacio de búsqueda para refinar el resultado.

El tamaño de la población «popSize» determina el número de soluciones «candidate» que participan en la búsqueda. El parámetro de Lévy «lambda» controla la distribución de los pasos aleatorios. El radio de la hiperesfera «sphereRadius» define el área de búsqueda local. El número de iteraciones de la búsqueda local «localIterations» indica cuántas veces se refina la solución dentro de la hiperesfera. Los parámetros de aleatorización «alpha» y de atracción «beta0» regulan los componentes de los modelos de búsqueda, como los movimientos aleatorios y la influencia de la «luz» (en términos metafóricos).

La fase de exploración global (GlobalExploration) se centra en la búsqueda de áreas «prometedoras» en todo el espacio de búsqueda, utilizando pasos aleatorios generados según la distribución de Lévy. Este enfoque favorece una búsqueda de amplio alcance y la escalabilidad en espacios de gran tamaño.

La fase de explotación local (LocalExploitation) realiza una búsqueda más exhaustiva dentro de la hiperesfera que rodea el punto seleccionado. En este caso se utilizan pasos más pequeños y precisos, propios de la optimización local.

La generación de pasos de Lévy (GenerateLevyStep) crea movimientos aleatorios según la distribución de Lévy, lo que permite saltos tanto pequeños como grandes en el espacio de búsqueda para equilibrar la exploración y la explotación.

La clase contiene mecanismos para realizar un seguimiento del progreso de la búsqueda, como el almacenamiento de la mejor solución encontrada, contadores de estancamiento (si la solución no mejora), así como bucles por épocas para limitar la ejecución del algoritmo por tiempo o iteraciones, cálculos de los parámetros de las distribuciones para los pasos aleatorios —en particular, la generación de distribuciones gaussianas y de Lévy— y métodos para gestionar las fases de búsqueda, lo que permite alternar entre la etapa global y la local, así como controlar su ejecución.

En resumen, el método constituye una estrategia de búsqueda que combina una amplia exploración del espacio para identificar áreas potenciales, seguida de una optimización local minuciosa para alcanzar soluciones precisas, utilizando parámetros y mecanismos que permiten adaptar su comportamiento a tareas y condiciones concretas.

//————————————————————————————————————————————————————————————————————
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/ru/articles/18460";

    popSize         = 100;   // tamaño de la población
    lambda          = 1.0;  // parámetro de la distribución de Lévy (1 &lt; λ ≤ 3)
    sphereRadius    = 0.1;  // radio de la hiperesfera para la búsqueda local
    localIterations = 20;   // número de iteraciones de la búsqueda local
    alpha           = 0.1;  // parámetro de aleatorización para Firefly
    beta0           = 1.2;  // atractivo inicial

    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  [],  // valores mínimos
             const double &rangeMaxP  [],  // valores máximos
             const double &rangeStepP [],  // paso de variación
             const int     epochsP = 0);   // número de épocas

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  double lambda;          // parámetro de la distribución de Lévy (1 &lt; λ ≤ 3)
  double sphereRadius;    // radio de la hiperesfera para la búsqueda local
  int    localIterations; // número de iteraciones de la búsqueda local
  double alpha;           // parámetro de aleatorización
  double beta0;           // atractivo inicial

  private: //---------------------------------------------------------
  double gamma_es;           // coeficiente de absorción de luz
  double levyStep [];        // array de pasos de Lévy

  // Seguimiento de fases
  bool   inLocalSearchPhase; // indicador de búsqueda local
  int    localSearchCenter;  // centro de búsqueda local
  int    localSearchCounter; // contador de iteraciones de la búsqueda local

  // Seguimiento de la convergencia
  double prevBestFitness;    // mejor valor anterior
  int    stagnationCounter;  // contador de estancamiento

  // Seguimiento de épocas
  int    epochCurrent;       // época actual
  int    epochMax;           // número máximo de épocas

  // Métodos auxiliares
  void   GlobalExploration  ();
  void   LocalExploitation  ();
  void   GenerateLevyStep   ();
  double GenerateGaussian   ();
  double Gamma              (double z);
};
//————————————————————————————————————————————————————————————————————

El método «Init» de la clase «C_AO_ES» inicializa el algoritmo de optimización antes de iniciar el proceso de búsqueda. En primer lugar, se invoca el método «StandardInit», encargado de la inicialización estándar del algoritmo; este configura los parámetros generales y las estructuras de datos. Si «StandardInit» falla, el método completo devuelve «false», lo que indica que la inicialización no se ha realizado correctamente.

A continuación, se asigna memoria para el array «levyStep» con un tamaño igual al número de coordenadas «coords». Este array se utilizará para almacenar los pasos generados de acuerdo con la distribución de Lévy. Al indicador «inLocalSearchPhase» se le asigna el valor «false»; el algoritmo se encuentra inicialmente en la fase de búsqueda global. Las variables «localSearchCenter» y «localSearchCounter» se establecen en «0» para preparar los contadores de la búsqueda local.

Inicialización de los parámetros de convergencia:

  • prevBestFitness se establece en el valor mínimo posible, para garantizar que la primera solución encontrada se considere mejor que la anterior.
  • El contador de estancamiento se establece en «0» para llevar la cuenta del número de iteraciones sin que se mejore la mejor solución encontrada.

Inicialización de los parámetros de las épocas:

  • A «epochMax» se le asigna el valor del parámetro de entrada «epochsP», que establece el número máximo de épocas (iteraciones) del algoritmo.
  • epochCurrent se establece en «0» para realizar el seguimiento de la época actual.

Configuración de un parámetro fijo: a la variable «gamma_es» se le asigna el valor «1.0». Este parámetro se utiliza en el algoritmo de las luciérnagas (Firefly), que forma parte de la estrategia general.

Se inicializa la población inicial de soluciones «a». Para cada solución de la población:

  • Cada coordenada del vector de la solución (a[i].c[c]) se inicializa con un valor aleatorio tomado del intervalo [rangeMin[c], rangeMax[c]].
  • El valor de cada coordenada se «redondea» al valor admisible más cercano, teniendo en cuenta el paso (rangeStep[c]), mediante el método «u.SeInDiSp».
  • El valor de la función objetivo (a[i].f y a[i].fB) para cada solución se establece en «-DBL_MAX».
//————————————————————————————————————————————————————————————————————
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;

  //------------------------------------------------------------------
  // Inicialización de arrays
  ArrayResize (levyStep, coords);

  // Inicialización del seguimiento de fases
  inLocalSearchPhase = false;
  localSearchCenter  = 0;
  localSearchCounter = 0;

  // Inicialización del seguimiento de la convergencia
  prevBestFitness   = -DBL_MAX;
  stagnationCounter = 0;

  // Inicialización del seguimiento de épocas
  epochMax     = epochsP;
  epochCurrent = 0;

  // Parámetro fijo de Firefly
  gamma_es = 1.0;

  // Inicialización aleatoria de la población
  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;
}
//————————————————————————————————————————————————————————————————————

El método «Moving» implementa la lógica principal del proceso iterativo de optimización, alternando entre la búsqueda global y la explotación local. Cada vez que se invoca el método, se incrementa el contador de épocas para realizar un seguimiento del progreso del algoritmo.

Procesamiento de las fases de búsqueda:
  • si la fase actual es búsqueda global, se lleva a cabo la exploración del espacio mediante pasos modelados según la distribución de Lévy, y se generan nuevas soluciones en todo el espacio para buscar nuevas áreas con potencial;

  • si tras la búsqueda global se encuentra una mejora, el algoritmo pasa a la fase local. En este caso, se elige la solución (agente) más prometedora, en torno a la cual se llevará a cabo la búsqueda con el fin de refinarla;

  • si no se producen mejoras a lo largo de varias iteraciones (se acumula el estancamiento), se intensifica la búsqueda de nuevas soluciones mediante pasos de vuelo más agresivos, reduciendo el parámetro «lambda» para explorar un espacio más amplio;

  • en caso de que el algoritmo se encuentre en la fase de búsqueda local, con una probabilidad del 80 % se lleva a cabo una optimización local utilizando un método que simula el algoritmo de las luciérnagas (Firefly). En este caso, se refina la solución seleccionada y se incrementa el contador de iteraciones locales; una vez alcanzado el número establecido de iteraciones de búsqueda local, o según la lógica establecida, se vuelve a la búsqueda global.

Un mecanismo adicional de aleatorización con cierta probabilidad de modificar las soluciones de la población tiene como objetivo preservar la diversidad y evitar que esta quede atrapada en «trampas» locales.

    De este modo, el método aplica una estrategia de búsqueda gradual y flexible, que alterna entre la exploración global del espacio y la optimización local específica. Esto permite encontrar un equilibrio entre la exploración de nuevas áreas y la mejora de las soluciones existentes.

    //————————————————————————————————————————————————————————————————————
    void C_AO_ES::Moving ()
    {
      epochCurrent++;
    
      // TOMA DE DECISIONES SOBRE LA FASE: Alternancia entre la búsqueda global y la búsqueda local
      if (!inLocalSearchPhase)
      {
        // FASE 1: EXPLORACIÓN GLOBAL mediante vuelos de Lévy
        GlobalExploration ();
    
        // Comprobación de si es necesario cambiar a la búsqueda local
        // Cambiamos de estrategia si encontramos un área prometedora (mejora de la mejor aptitud)
        if (fB > prevBestFitness)
        {
          inLocalSearchPhase = true;
          localSearchCounter = 0;
          prevBestFitness    = fB;
          stagnationCounter  = 0;
    
          // Búsqueda del mejor agente para centrar la búsqueda local
          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++;
    
          // En caso de estancamiento, ampliamos la exploración
          if (stagnationCounter > 5)
          {
            lambda = MathMax (1.0, lambda - 0.1); // Hacemos más agresivos los vuelos de Lévy
          }
        }
      }
      else
      {
        if (u.RNDprobab () < 0.8)
        {
          // FASE 2: EXPLOTACIÓN LOCAL mediante el algoritmo de las luciérnagas (Firefly)
          LocalExploitation ();
    
          localSearchCounter++;
    
          // Volver a la búsqueda global tras completar las iteraciones locales
          if (localSearchCounter >= localIterations)
          {
            inLocalSearchPhase = false;
            lambda = params [1].val; // Restablecimiento de lambda a su valor inicial
          }
        }
        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];
              }
            }
          }
        }
      }
    }
    //————————————————————————————————————————————————————————————————————

    El método «Revision» está diseñado para actualizar la información sobre las mejores soluciones durante la ejecución del algoritmo. El método recorre todos los elementos de la población actual de soluciones y, para cada solución, compara su aptitud (fitness) actual con la almacenada en su mejor personal. Si el valor actual es mejor, se actualiza el mejor personal y la solución correspondiente (se guarda la mejor solución de la iteración actual).

    A continuación, el método compara la aptitud (fitness) de cada solución con el mejor global actual encontrado en toda la población y, si el resultado actual es el mejor, se actualiza el mejor global y se guarda la solución correspondiente. De este modo, el método mantiene información actualizada sobre las soluciones óptimas locales y globales en el estado actual del algoritmo, lo que garantiza que se conserven las mejores soluciones encontradas en cada paso.

    //————————————————————————————————————————————————————————————————————
    void C_AO_ES::Revision ()
    {
      for (int i = 0; i < popSize; i++)
      {
        // Actualización del mejor personal
        if (a [i].f > a [i].fB)
        {
          a [i].fB = a [i].f;
          ArrayCopy (a [i].cB, a [i].c, 0, 0, WHOLE_ARRAY);
        }
    
        // Actualización del mejor global
        if (a [i].f > fB)
        {
          fB = a [i].f;
          ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);
        }
      }
    }
    //————————————————————————————————————————————————————————————————————

    El método «GlobalExploration» implementa la fase de búsqueda global utilizando vuelos de Lévy para explorar el espacio de soluciones. En un ciclo por toda la población, para cada solución se genera un paso aleatorio utilizando la distribución de Lévy; este determina la dirección y la longitud del desplazamiento en el espacio de soluciones.

    La distribución de Lévy se caracteriza por sus «colas pesadas», lo que permite tanto dar pequeños pasos de precisión como realizar grandes saltos poco frecuentes para explorar regiones alejadas. En el ciclo, se calcula para cada coordenada de la solución:

    • amplitud de búsqueda (diferencia entre el valor máximo y el mínimo de una coordenada),
    • coeficiente de escala adaptativo «stepScale». Se va reduciendo a medida que avanza la búsqueda (cuanto más cerca del final, menores son los pasos), lo que contribuye a estrechar el área de búsqueda en torno a las soluciones prometedoras, mientras que al principio de la búsqueda los pasos son más grandes para permitir una exploración más amplia.
    • Se aplica el paso de Lévy: la coordenada de la solución actual varía en una cantidad proporcional al paso de Lévy, a la amplitud de búsqueda y al coeficiente de escala.
    • Corrección de límites: se comprueba si la nueva coordenada se sale de los límites permitidos; si es así, el valor se corrige para que se mantenga dentro del rango establecido.
    En definitiva, el método actualiza la posición de cada solución en la población utilizando pasos obtenidos a partir de la distribución de Lévy, lo que garantiza una exploración global del espacio de soluciones con un control adaptativo del tamaño del paso en función del progreso de la optimización. Esto permite explorar el espacio al principio e ir refinando los resultados progresivamente a medida que se avanza hacia la solución óptima.
    //————————————————————————————————————————————————————————————————————
    // FASE 1: Exploración global mediante vuelos de Lévy
    void C_AO_ES::GlobalExploration ()
    {
      for (int i = 0; i < popSize; i++)
      {
        // Generación del paso de Lévy
        GenerateLevyStep ();
    
        // Actualización de la posición mediante un vuelo de Lévy
        for (int c = 0; c < coords; c++)
        {
          double range = rangeMax [c] - rangeMin [c];
    
          // Tamaño de paso adaptativo en función del progreso de la búsqueda
          double progress = (epochMax > 0) ? (double)epochCurrent / (double)epochMax : 0.5;
          double stepScale = 0.01 + 0.2 * (1.0 - progress); // Empezamos con pasos grandes
    
          // Aplicación del paso de Lévy
          a [i].c [c] += levyStep [c] * range * stepScale;
    
          // Restricciones de límites
          a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
    }
    //————————————————————————————————————————————————————————————————————

    El método «LocalExploitation» lleva a cabo la fase de mejora local de las soluciones mediante el algoritmo de las luciérnagas (Firefly). En la fase inicial se identifican los agentes que se encuentran dentro de una hiperesfera dada alrededor de la mejor solución. Para ello, se calcula la distancia de cada agente al centro de la hiperesfera y, si es menor que el radio o si el agente es el centro de búsqueda, se incluye en el grupo seleccionado.

    Si hay muy pocos agentes dentro de la hiperesfera, se amplía el área de búsqueda incluyendo a los vecinos más cercanos. Para ello, se calcula la distancia al centro de todos los agentes y se seleccionan los más cercanos, ya sea por cantidad numérica (por ejemplo, 5) o por proporción de la población (por ejemplo, el 30 %). Estos agentes se añaden al grupo.

    Aplicación del algoritmo de las luciérnagas (Firefly): los agentes seleccionados interactúan de acuerdo con las reglas del algoritmo:
    • Para cada agente, se comprueba si hay otro agente en el grupo con mejor aptitud (fitness).
    • Si lo hay, el agente actual se desplaza hacia el agente más atractivo (mejor), calculándose al mismo tiempo la distancia entre ambos.
    • Cuanto más atractivo es otro agente, mayor es su «luminosidad» (depende de la distancia y de los parámetros del algoritmo).
    • El desplazamiento incluye un ajuste cuidadoso: el agente se desplaza hacia el agente más atractivo, con la adición de fluctuaciones aleatorias para diversificar la búsqueda.
    • Tras los desplazamientos, se comprueban los límites de las coordenadas para garantizar que las soluciones se mantengan dentro del rango admisible.

      Como resultado, el método permite mejorar localmente las soluciones en las proximidades de los mejores puntos encontrados, utilizando la interacción entre los agentes según el modelo de estrategia del algoritmo de las luciérnagas (Firefly): las mejores soluciones atraen a las peores, lo que favorece la optimización puntual del espacio de soluciones.

      //————————————————————————————————————————————————————————————————————
      // FASE 2: Explotación local mediante el algoritmo de las luciérnagas (Firefly)
      void C_AO_ES::LocalExploitation ()
      {
        // Identificación de agentes dentro de la hiperesfera que rodea la mejor solución
        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);
      
          // Incluimos a los agentes que se encuentran dentro de la esfera o en el propio centro
          if (normalized_dist <= sphereRadius || i == localSearchCenter)
          {
            int size = ArraySize (agents_in_sphere);
            ArrayResize (agents_in_sphere, size + 1);
            agents_in_sphere [size] = i;
          }
        }
      
        // Si hay muy pocos agentes, ampliamos la búsqueda a los vecinos más cercanos
        if (ArraySize (agents_in_sphere) < 5)
        {
          ArrayResize (agents_in_sphere, 0);
      
          // Calculamos las distancias para todos los agentes
          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);
            }
          }
      
          // Tomamos los 5 agentes más cercanos o el 30 % de la población
          int numAgents = MathMin (popSize, MathMax (5, popSize / 3));
          ArrayResize (agents_in_sphere, numAgents);
      
          // Una forma sencilla de elegir los agentes más cercanos
          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;
          }
        }
      
        // Ejecución del algoritmo de las luciérnagas (Firefly) en los agentes seleccionados
        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];
      
            // Si el agente j es mejor que el agente i, movemos a i hacia j
            if (a [idx_j].f > a [idx_i].f)
            {
              // Cálculo de la distancia
              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;
              }
      
              // Cálculo del atractivo
              double beta = beta0 * MathExp (-gamma_es * r_squared);
      
              // Desplazamiento del agente i hacia el agente j
              for (int c = 0; c < coords; c++)
              {
                double range = rangeMax [c] - rangeMin [c];
      
                // Ecuación de movimiento del algoritmo de las luciérnagas (Firefly)
                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;
      
                // Aplicación de los límites
                a [idx_i].c [c] = u.SeInDiSp (a [idx_i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
              }
            }
          }
        }
      }
      //————————————————————————————————————————————————————————————————————

      El método «GenerateLevyStep» se encarga de generar el paso de Lévy necesario para implementar la estrategia de exploración global en el algoritmo de optimización. El método utiliza el algoritmo de Mantegna para generar estos pasos; ya hemos hablado de este método anteriormente.

      Cálculo de sigma:

      La fórmula del código calcula el parámetro «sigma». Este parámetro está relacionado con la escala de la distribución de Lévy y depende de la constante «lambda», que caracteriza la forma de la distribución de Lévy (determina lo «pesadas» que serán las colas de la distribución). Gamma() es la función gamma, una generalización del factorial a los números reales (el código de este método se muestra a continuación). Este parámetro es necesario para generar valores que sigan una distribución normal, los cuales se utilizan posteriormente en el algoritmo de Mantegna.

      La generación del paso de Lévy se realiza de forma independiente para cada coordenada (parámetro) de la solución. Se generan dos variables aleatorias, «u_val» y «v_val», a partir de una distribución normal (gaussiana); se toma el valor absoluto de «v_val». El paso de Lévy «levyStep[c]» se calcula mediante la fórmula del algoritmo de Mantegna: u_val / Math.pow(v_val, 1.0 / lambda). Se comprueba si se produce una división por cero (si v_val es muy pequeña). El paso de Lévy se limita en valor absoluto. Esto se hace para evitar saltos demasiado grandes.

      Como resultado de la aplicación del método, se genera un array denominado «levyStep» que contiene los valores de los pasos de Lévy para cada coordenada. Estos pasos se utilizan posteriormente en el método «GlobalExploration» para actualizar la posición de cada solución en el espacio de búsqueda.

      //————————————————————————————————————————————————————————————————————
      // Generación del paso de Lévy mediante el algoritmo de Mantegna
      void C_AO_ES::GenerateLevyStep ()
      {
        // Cálculo de sigma para el algoritmo de Mantegna
        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++)
        {
          // Generación de u y v a partir de distribuciones normales
          double u_val = GenerateGaussian () * sigma;
          double v_val = MathAbs (GenerateGaussian ());
      
          // Cálculo del paso de Lévy
          if (v_val > 1e-10)
          {
            levyStep [c] = u_val / MathPow (v_val, 1.0 / lambda);
          }
          else
          {
            levyStep [c] = 0.0;
          }
      
          // Limitación de los valores extremos
          if (MathAbs (levyStep [c]) > 10.0)
          {
            levyStep [c] = 10.0 * (levyStep [c] > 0 ? 1.0 : -1.0);
          }
        }
      }
      //————————————————————————————————————————————————————————————————————

      El método «GenerateGaussian» genera números aleatorios que siguen una distribución normal (gaussiana) con una media de «0» y una desviación estándar de «1». Utiliza la transformación de Box-Muller, un método bastante habitual para esta tarea; ya hemos aplicado este método en artículos anteriores.

      Se utilizan las variables estáticas «hasSpare» y «spare»; la transformación de Box-Muller genera dos números aleatorios independientes con distribución normal cada vez; «hasSpare» es una variable booleana que determina si se ha guardado uno de los números generados para la siguiente llamada a la función, y «spare» es la variable que almacena ese número «de reserva».

        Generación de nuevos números aleatorios (si es necesario):

        Si no hay un número «de reserva», se generan dos variables aleatorias independientes, «u_val» y «v_val», a partir de una distribución uniforme en el intervalo (0, 1). La función «u.RNDfromCI» se encarga de generar números distribuidos uniformemente. Se aplica la transformación de Box-Muller:

        • Se calcula «mag» (magnitud) como la raíz cuadrada de -2.0 * Math.log(u_val + 1e-10). Es necesario sumar un número pequeño a «u_val» para evitar calcular el logaritmo de cero, lo cual no está permitido.
        • El número «de reserva» «spare» se calcula como mag * Math.cos(2.0 * M_PI * v_val).
        • Devuelve el valor mag * Math.sin(2.0 * M_PI * v_val).

        El método devuelve uno de los números aleatorios generados que siguen una distribución normal.

        //————————————————————————————————————————————————————————————————————
        // Generación de un número aleatorio gaussiano mediante la transformación de Box-Muller
        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);
        }
        //————————————————————————————————————————————————————————————————————

        El método «Gamma» es una función que aproxima la función gamma para un argumento «z» dado. Dado que la función gamma es una función matemática importante, especialmente en estadística y optimización, pero su cálculo exacto puede resultar complicado y consumir muchos recursos, a menudo se recurre a aproximaciones. Utilizamos la aproximación de Lanczos, que ofrece una buena precisión con un esfuerzo computacional relativamente reducido. Repasemos los puntos principales.

        Si el argumento «z» es menor que «0,5», se aplica la fórmula de reflexión de Euler. Esto permite calcular la función gamma para argumentos cercanos a cero. La fórmula de reflexión relaciona la función gamma de «z» con la función gamma de «1 - z». Utilizaremos los coeficientes fijos de Lanczos, cuidadosamente seleccionados para garantizar una alta precisión de la aproximación, así como una fórmula con coeficientes que incluye funciones de potencia y exponenciales. El método devuelve un valor aproximado de la función gamma para el argumento «z» especificado.

        La aproximación de Lanczos ofrece una buena precisión, suficiente para la mayoría de las aplicaciones prácticas. El algoritmo es relativamente eficiente, ya que solo requiere unas pocas operaciones aritméticas y una consulta en una tabla de coeficientes. Es adecuado para un amplio rango de valores del argumento, especialmente en combinación con la fórmula de reflexión. En general, el método «Gamma» constituye una forma bastante precisa de aproximar la función gamma.

        //————————————————————————————————————————————————————————————————————
        // Aproximación de la función gamma mediante la aproximación de Lanczos
        double C_AO_ES::Gamma (double z)
        {
          if (z < 0.5)
          {
            // Fórmula de reflexión para z &lt; 0,5
            return M_PI / (MathSin (M_PI * z) * Gamma (1.0 - z));
          }
        
          // Coeficientes de Lanczos
          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;
        }
        //————————————————————————————————————————————————————————————————————


        Resultados de las pruebas

        Aunque el algoritmo no logró entrar en nuestra tabla de clasificación, los resultados de su desempeño merecen atención.

        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%)

        En la visualización se aprecia claramente cómo se separan las fases de búsqueda según la estrategia, cuando se producen desplazamientos largos y movimientos cortos de refinamiento.

        Hilly

        ES en la función de prueba Hilly

        Forest

        ES en la función de prueba Forest

        Megacity

        ES en la función de prueba Megacity

        El algoritmo ES se presenta en la tabla de clasificación con fines informativos.

        N.º AO Description 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 across neighbourhood search 0,94948 0,84776 0,43857 2,23581 1,00000 0,92334 0,39988 2,32323 0,70923 0,63477 0,23091 1,57491 6,134 68,15
        2 CLA code lock algorithm (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 animal migration optimization 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) evolution strategies 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 comet tail algorithm (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 time evolution travel algorithm (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 stochastic diffusion search 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 billiards optimization algorithm 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 archery algorithm 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 evolution of social groups (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 simulated isotropic annealing (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 biogeography based optimization 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 artificial cooperative search 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 dialectical algorithm 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 black hole algorithm 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 anarchy society optimization 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 royal flush optimization (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 atomic orbital search 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 turtle shell evolution algorithm (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 differential 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 successful restaurateur algorithm (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 chemical reaction optimisation 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 blood inheritance optimization (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 bird swarm algorithm 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 harmony search 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 saplings sowing and growing 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 bacterial chemotaxis optimization 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 african buffalo optimization 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) evolution strategies 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 fractal-based Algorithm 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 search 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 brain storm optimization 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 wale optimization algorithm 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 artificial electric field algorithm 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 artificial ecosystem-based optimization algorithm 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 camel algorithm 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 ant colony optimization 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 bacterial foraging optimization - 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 simple optimization algorithm 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 artificial bee hive algorithm 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 atmospheric cloud model optimization 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 moment estimation 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 optimization 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 coral reefs optimization 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 artificial tribe algorithm 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 optimization algorithm 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


        Conclusiones

        El algoritmo ES obtiene resultados medios en las pruebas comparativas de métodos de optimización poblacionales, con un 39 % de la puntuación máxima posible, lo que no le ha permitido entrar en la tabla de clasificación de los 45 algoritmos más potentes.

        A pesar de ello, el algoritmo merece atención gracias a su original concepto de búsqueda en dos fases, basado en un modelo biológicamente fundamentado del comportamiento de caza del águila. El uso de los vuelos de Lévy para la exploración global constituye una solución matemáticamente elegante, cuya optimalidad para los problemas de búsqueda aleatoria ha quedado demostrada teóricamente. Los mecanismos adaptativos de implementación, incluidos el cambio dinámico entre fases y el ajuste automático de parámetros en caso de estancamiento, muestran potencial para mejorar el concepto básico.

        El algoritmo puede encontrar su nicho en clases específicas de problemas en las que es importante el equilibrio entre la exploración global y la explotación local, especialmente cuando hay múltiples óptimos locales y los datos contienen ruido. La integración del algoritmo de las luciérnagas (Firefly) para la búsqueda local genera una interesante sinergia entre dos métodos inspirados en la naturaleza, aunque el rendimiento general apunta a la necesidad de seguir optimizando los parámetros y de una posible modificación de los mecanismos básicos.

        El algoritmo ES puede considerarse un ejemplo ilustrativo de un enfoque híbrido de optimización y una base para el desarrollo de métodos más avanzados que combinen diversas metaheurísticas. Su implementación relativamente sencilla y su lógica intuitiva hacen que este algoritmo sea adecuado para experimentos de investigación en el ámbito de la computación evolutiva.

        tab

        Figura 2. Gradación cromática de los algoritmos según las pruebas correspondientes

        gráfico

        Figura 3. Histograma de los resultados de las pruebas de los algoritmos (en una escala de 0 a 100; cuanto mayor sea el valor, mejor, donde 100 representa el resultado teórico máximo posible; en el archivo se incluye el script para calcular la tabla de clasificación)

        Ventajas e inconvenientes del algoritmo ES:

        Ventajas:

        1. Es rápido.

        Desventajas:

        1. Tiene una gran cantidad de parámetros.

        Se adjunta al artículo un archivo comprimido con las versiones actualizadas de los códigos de los algoritmos. El autor del artículo no se hace responsable de la exactitud absoluta en la descripción de los algoritmos canónicos, ya que en muchos de ellos se han introducido modificaciones para mejorar las capacidades de búsqueda. Las conclusiones y consideraciones que figuran en los artículos se basan en los resultados de los experimentos realizados.

        Programas utilizados en el artículo

        # Nombre Tipo Descripción
        1 #C_AO.mqh
        Archivo de inclusión
        Clase padre de los algoritmos de optimización poblacional
        2 #C_AO_enum.mqh
        Archivo de inclusión
        Enumeración de algoritmos de optimización poblacional
        3 TestFunctions.mqh
        Archivo de inclusión
        Biblioteca de funciones de prueba
        4
        TestStandFunctions.mqh
        Archivo de inclusión
        Biblioteca de funciones del banco de pruebas
        5
        Utilities.mqh
        Archivo de inclusión
        Biblioteca de funciones auxiliares
        6
        CalculationTestResults.mqh
        Archivo de inclusión
        Script para calcular los resultados destinados a la tabla comparativa
        7
        Testing AOs.mq5
        Script Banco de pruebas unificado para todos los algoritmos de optimización poblacional
        8
        Simple use of population optimization algorithms.mq5
        Script
        Ejemplo sencillo de uso de algoritmos de optimización poblacional sin visualización
        9
        Test_AO_ES.mq5
        Script Banco de pruebas para ES

        Traducción del ruso hecha por MetaQuotes Ltd.
        Artículo original: https://www.mql5.com/ru/articles/18460

        Archivos adjuntos |
        ES.ZIP (290.51 KB)
        Características del Wizard MQL5 que debe conocer (Parte 79): Uso de los osciladores Gator y Acumulación/Distribución con aprendizaje supervisado Características del Wizard MQL5 que debe conocer (Parte 79): Uso de los osciladores Gator y Acumulación/Distribución con aprendizaje supervisado
        En el último apartado, concluimos nuestro análisis del emparejamiento del oscilador caimán y el oscilador de acumulación/distribución cuando se utilizan en su configuración típica de las señales brutas que generan. Estos dos indicadores son complementarios, ya que funcionan como indicadores de tendencia y volumen, respectivamente. A continuación, damos seguimiento a ese artículo examinando el efecto que el aprendizaje supervisado puede tener en la mejora de algunos de los patrones de características que habíamos revisado. Nuestro enfoque de aprendizaje supervisado es una CNN que utiliza regresión por kernels y similitud de producto escalar para dimensionar sus núcleos y canales. Como siempre, lo hacemos en un archivo de clase de señal personalizado que funciona con el asistente MQL5 para ensamblar un Asesor Experto.
        Aprendiendo MQL5 de principiante a profesional (Parte VII): Principios de depuración de aplicaciones MQL Aprendiendo MQL5 de principiante a profesional (Parte VII): Principios de depuración de aplicaciones MQL
        La corrección de errores es una parte indispensable del ciclo de programación. En este artículo se analizan las técnicas habituales para corregir errores (depuración) en cualquier aplicación que se ejecute en el entorno de MetaTrader 5.
        Evaluación de la calidad de la operativa con spreads en función de los factores de estacionalidad en el mercado Forex en el terminal MetaTrader 5 Evaluación de la calidad de la operativa con spreads en función de los factores de estacionalidad en el mercado Forex en el terminal MetaTrader 5
        En este artículo se analiza la evaluación de la calidad del enfoque de trading estacional en el marco temporal diario, tanto para símbolos individuales como para spreads. Se presta especial atención a la identificación de ciclos mensuales recurrentes y a las posibilidades de aplicarlos en el trading durante el año en curso.
        Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 36): Cómo habilitar el acceso directo desde Python a los flujos de mercado de MetaTrader 5 Desarrollo de un kit de herramientas para el análisis de la acción del precio (Parte 36): Cómo habilitar el acceso directo desde Python a los flujos de mercado de MetaTrader 5
        Aproveche todo el potencial de su terminal de MetaTrader 5 utilizando el ecosistema de ciencia de datos de Python y la biblioteca cliente oficial de MetaTrader 5. En este artículo se explica cómo autenticarse y transmitir datos en tiempo real de ticks y de barras de un minuto directamente al almacenamiento Parquet, aplicar técnicas sofisticadas de ingeniería de características con Ta y Prophet, y entrenar un modelo de Gradient Boosting sensible al tiempo. A continuación, implementamos un servicio ligero basado en Flask para proporcionar señales de trading en tiempo real. Tanto si está desarrollando un framework cuantitativo híbrido como si está mejorando su EA con aprendizaje automático, obtendrá un proceso sólido e integral para el trading algorítmico basado en datos.