English Русский Português
preview
Algoritmo de monos azules — Algoritmo Blue Monkey (BM)

Algoritmo de monos azules — Algoritmo Blue Monkey (BM)

MetaTrader 5 — Trading |
26 0
Andrey Dik
Andrey Dik

Índice

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


Introducción

Para encontrar el algoritmo de optimización más adecuado para los problemas discretos de los robots de trading, analizaremos otro método y sus posibilidades. En el corazón de la selva africana habitan unas criaturas cuya vida, llena de interacciones complejas y de una sabia jerarquía, se ha convertido en fuente de inspiración para crear uno de los métodos de optimización. Se trata de los monos azules, o Cercopithecus mitis, pequeños y ágiles habitantes de las copas de los árboles. Su comportamiento, aparentemente caótico pero regido en realidad por profundas leyes biológicas, inspiró el desarrollo de un algoritmo bautizado en su honor: Blue Monkey.

El algoritmo fue desarrollado por M. Mahmood y B. Al-Khateeb y publicado en 2019 en «Periodicals of Engineering and Natural Sciences» (PEN), 7(3):1054-1066, DOI:10.21533/pen.v7i3.621.

Este ecosistema complejo, pero armonioso, se refleja en la abstracción matemática del algoritmo Blue Monkey:

  • Grupos como equipos. La población de agentes se divide en equipos independientes, cada uno con su propio líder, que desempeña un papel similar al de un macho dominante.
  • El líder: la encarnación de la perfección. El mejor individuo de cada grupo, que posee la mayor «aptitud» (fitness), personifica al macho dominante: un modelo a seguir.
  • Las crías: la nueva generación. Los individuos jóvenes, las «crías», representan una nueva etapa en la evolución: están aprendiendo y están preparados para sustituir a sus mayores, aportando ideas y soluciones novedosas.
  • Migración: renovación del sistema. El proceso de migración consiste en que las mejores «crías» pueden sustituir a los «adultos» menos aptos, lo que garantiza una renovación constante y una mejora de la eficiencia.
  • El aprendizaje social: el camino hacia el líder. El movimiento de las «crías» hacia el líder del grupo, regido por fórmulas matemáticas, imita el proceso de aprendizaje social, en el que los jóvenes aprenden de los más experimentados.


Implementación del algoritmo

Imagine que dirige una expedición de investigación en la selva que busca una ciudad perdida. Usted cuenta con 50 investigadores con experiencia (monos adultos) y 15 aprendices (crías, que representan el 30 % de los adultos).

Organización del trabajo:

  • dividimos a los investigadores experimentados en 5 equipos de 10 personas cada uno;
  • cada equipo explora su zona de la selva por su cuenta;
  • 5 equipos = 5 zonas de búsqueda diferentes: el equipo A se dirigirá al norte, el equipo B se dirigirá al sur, el equipo C buscará en el este, el equipo D explorará en el oeste y el equipo E se quedará en el centro.

Si la ciudad perdida está en el norte, el equipo A la encontrará antes, y los demás se irán acercando paulatinamente. Los aprendices trabajan todos juntos como un grupo de formación. Mecanismo principal: 5 equipos = 5 zonas de búsqueda diferentes. «Sigue el ejemplo de los que tienen éxito». En cada equipo se identifica al líder —aquel que ha encontrado la dirección más acertada—; los demás ajustan su ruta hacia él, ¡pero no van directamente a su encuentro! Mantienen el 70 % de su rumbo y lo ajustan un 30 % hacia el líder. No abandonamos nuestra ruta de inmediato (por si acaso también hay algo allí), pero nos vamos desplazando poco a poco hacia los lugares donde se han obtenido mejores resultados.

Formación de los jóvenes: los aprendices hacen lo mismo, pero todos siguen al mejor estudiante en prácticas. Buscamos al peor de cada equipo y lo comparamos con los mejores estudiantes; si el estudiante es mejor, consigue un puesto fijo, y el que no da la talla es despedido. Los aprendices aportan nuevas ideas: puede que un investigador con experiencia se haya quedado atascado en una zona poco prometedora, mientras que un joven haya dado con una mina de oro por casualidad.

blue_monkey

Figura 1. Esquema de funcionamiento del algoritmo BM

En el esquema se muestra el funcionamiento del algoritmo BM, con la lógica dividida en dos ramas independientes para los individuos adultos y las crías, junto con las fórmulas principales. Podemos pasar a redactar el pseudocódigo del algoritmo BM.

Inicialización:

  1. Crear una población de monos adultos (tamaño N)
    • A cada mono se le asigna una posición aleatoria en el espacio de búsqueda
    • Inicializar la velocidad de desplazamiento en cero
    • Asignar un peso inicial de entre 4 y 6
  2. Crear una población de crías (el 30 % del tamaño de la población adulta)
    • Cada cría recibe una posición aleatoria
    • Inicializar la velocidad de desplazamiento en cero
    • Asignar un peso inicial de entre 4 y 6
  3. Dividir a los adultos en grupos
    • Dividir la población en T grupos de manera uniforme
    • El mono con índice i entra en el grupo (i módulo T)
  4. Calcular el fitness inicial
    • Evaluar la calidad de la posición de cada adulto
    • Evaluar la calidad de la posición de cada cría

Ciclo principal (hasta que se cumpla el criterio de parada):

  1. Actualizar los pesos en función del fitness
    • Encontrar los valores mínimo y máximo de fitness entre los adultos
    • Normalizar los pesos: peso = 4 + 2 × (fitness - mín.)/(máx. - mín.)
    • Repetir con las crías
  2. Sustituir los peores por los mejores (a partir de la segunda iteración)
    • Para cada grupo de adultos:
      • Encontrar al mono con peor fitness del grupo
      • Seleccionar la siguiente cría con mejor fitness que aún no se haya utilizado
      • Si el fitness de la cría es mejor que el del adulto:
        • Sustituir al adulto por la cría
        • Crear una nueva cría en la plaza que ha quedado libre
  3. Actualizar las posiciones de los monos adultos
    • Para cada mono del grupo:
      • Encontrar al líder del grupo (el mono con mejor fitness)
      • Actualizar la velocidad: nueva velocidad = 0,7 × velocidad anterior + (peso del líder - peso propio) × valor aleatorio × (posición del líder - posición propia)
      • Actualizar la posición: nueva posición = posición anterior + velocidad × valor aleatorio
      • Restringir la posición a los límites del espacio de búsqueda
  4. Actualizar las posiciones de las crías
    • Encontrar la mejor cría entre todas
    • Para cada una de las demás crías:
      • Actualizar la velocidad: nueva velocidad = 0,7 × velocidad anterior + (peso de la mejor cría - peso propio) × valor aleatorio × (posición de la mejor cría - posición propia)
      • Actualizar la posición: nueva posición = posición anterior + velocidad × valor aleatorio
      • Restringir la posición a los límites
  5. Calcular el nuevo fitness
    • Evaluar la calidad de las nuevas posiciones de todos los monos
    • Evaluar la calidad de las nuevas posiciones de todas las crías
  6. Actualizar la mejor solución global
    • Si se encuentra una solución mejor que el récord actual:
      • Guardarlo como un nuevo récord
    • Aumentar el contador de iteraciones
  7. Comprobar el criterio de parada
    • Si se ha alcanzado el número máximo de iteraciones O BIEN
    • Si se ha alcanzado la precisión requerida:
      • Salir del bucle
    • De lo contrario: volver al paso 5

Finalización:

  1. Devolver el resultado
    • Devolver la mejor posición encontrada
    • Devolver el valor de fitness en esta posición

Ahora vamos a convertir el pseudocódigo en código real. Vamos a crear la clase «C_AO_BM». Esta clase deriva de «C_AO», que es la clase base. La clase «C_AO_BM» hereda todos los campos públicos y protegidos de la clase «C_AO».

Campos públicos:

El constructor «C_AO_BM» inicializa las propiedades básicas del algoritmo y establece sus parámetros iniciales: «popSize» (tamaño de la población), «numGroups» (número de grupos) y «childrenRatio» (proporción de crías). Configura el array «params», que se utiliza para almacenar los parámetros configurables del algoritmo. Para cada parámetro se indica su nombre y su valor actual. Establece los valores mínimo «minW» y máximo «maxW» para los pesos utilizados en el algoritmo.
  • SetParams(): método para establecer los parámetros del algoritmo a partir del array «params». Lee los valores «val» de «params» y los convierte a los tipos de datos correspondientes (int o double), asignándolos a los miembros de la clase.
  • Init(): método de inicialización que prepara el algoritmo para su funcionamiento utilizando los rangos de entrada (rangeMinP, rangeMaxP, rangeStepP) y el número de épocas (epochsP).
  • Moving (): método que implementa la lógica principal para desplazar o actualizar los individuos de la población según el algoritmo.
  • Revision (): método que se utiliza para revisar o evaluar el estado actual de la población, así como para su posible selección y actualización.
Campos públicos:
  • numGroups: el número de grupos en los que se divide la población.
  • childrenRatio: porcentaje de la población que estará representado por «crías» (nuevos individuos).
Campos privados:
  • numChildren: el número de «crías» de la población, que se calcula a partir de «popSize» y «childrenRatio».
  • minW, maxW: valores mínimo y máximo de los pesos.
  • Arrays para los monos adultos:
    • monkeyRate: almacena la «velocidad» de cada individuo de la población. El tamaño del array depende de «popSize» y del número de dimensiones (coordenadas) en el espacio de búsqueda.
    • monkeyWeight: almacena los «pesos» (W) de cada individuo adulto.
    • groupId: un array que indica a qué grupo pertenece cada individuo adulto.
  • Los arrays para las crías almacenan:
    • childPos: posiciones de las «crías» en el espacio de búsqueda.
    • childRate: la «velocidad» de las «crías».
    • childWeight: los «pesos» de las «crías».
    • childFitness: valor de aptitud (fitness) para las «crías».
  • Métodos privados:
    • SwapWorstWithBest () — implementa el paso 8 del algoritmo, que consiste en sustituir el peor individuo del grupo por el mejor.
    • UpdatePositions () — implementa los pasos 9 y 10 del algoritmo, encargados de actualizar las posiciones de los individuos.
    • UpdateWeights () — implementa el paso 2 del algoritmo y la actualización general de los pesos.
    • FindGroupBest () — encuentra el mejor individuo del grupo indicado.
    • FindGroupWorst () — encuentra el peor individuo del grupo indicado.

El algoritmo divide la población en grupos, utiliza los conceptos de «velocidad» (rate) y «pesos» (weight) para desplazar a los individuos, e incluye mecanismos para la creación de «crías» y la sustitución de los peores individuos por los mejores dentro de los grupos. Los miembros privados están destinados a la gestión interna del estado del algoritmo, mientras que los miembros públicos proporcionan una interfaz para configurar y ejecutar la optimización.

//————————————————————————————————————————————————————————————————————
class C_AO_BM : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_BM () { }
  C_AO_BM ()
  {
    ao_name = "BM";
    ao_desc = "Blue Monkey Algorithm";
    ao_link = "https://www.mql5.com/ru/articles/19757";

    popSize        = 50;    // tamaño de la población
    numGroups      = 5;     // número de grupos T
    childrenRatio  = 0.3;   // porcentaje de crías respecto a la población

    ArrayResize (params, 3);
    params [0].name = "popSize";       params [0].val = popSize;
    params [1].name = "numGroups";     params [1].val = numGroups;
    params [2].name = "childrenRatio"; params [2].val = childrenRatio;

    minW           = 4.0;
    maxW           = 6.0;
  }

  void SetParams ()
  {
    popSize       = (int)params [0].val;
    numGroups     = (int)params [1].val;
    childrenRatio = params      [2].val;
  }

  bool Init (const double &rangeMinP  [],
             const double &rangeMaxP  [],
             const double &rangeStepP [],
             const int     epochsP = 0);

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  int    numGroups;      // número de grupos T
  double childrenRatio;  // porcentaje de crías

  private: //---------------------------------------------------------
  int    numChildren;         // número de crías
  double minW;
  double maxW;

  // Arrays para monos adultos
  double monkeyRate   [];     // Rate: velocidad de cambio [popSize * coords]
  double monkeyWeight [];     // W: pesos de los monos
  int    groupId      [];     // pertenencia a un grupo

  // Arrays para crías
  double childPos     [];      // posiciones de las crías [numChildren * coords]
  double childRate    [];      // Velocidad de las crías
  double childWeight  [];      // W para crías
  double childFitness [];      // fitness de las crías

  void SwapWorstWithBest ();   // Paso 8
  void UpdatePositions   ();   // Pasos 9-10
  void UpdateWeights     ();   // Paso 2 y actualización
  int  FindGroupBest     (int groupId);
  int  FindGroupWorst    (int groupId);
};
//————————————————————————————————————————————————————————————————————

La función «Init» realiza las siguientes acciones:

Inicialización estándar. En primer lugar, se llama a la función básica de inicialización «StandardInit» con los parámetros de rango (rangeMinP, rangeMaxP, rangeStepP) pasados como argumentos. Si esta inicialización básica falla, la función «Init» también finaliza devolviendo «false».

Ajuste de los parámetros:

  • Luego se comprueba el número de grupos (numGroups).
  • El número de «crías» (numChildren) se calcula en función del tamaño de la población y de la proporción de crías (childrenRatio).
Cambio del tamaño de los arrays:
  • Se modifica el tamaño de los arrays destinados a almacenar datos de individuos «adultos». Esto incluye «monkeyRate» (donde se almacena la velocidad para cada dimensión), «monkeyWeight» (el peso de cada individuo) y «groupId» (la pertenencia del individuo a un grupo).
  • Después se modifica el tamaño de los arrays correspondientes a las crías, incluyendo sus posiciones (childPos), velocidades (childRate), pesos (childWeight) y valores de aptitud (childFitness).

Inicialización de las velocidades. Los arrays «monkeyRate» y «childRate» (que se utilizan para determinar la dirección del movimiento) se inicializan con valores iguales a cero.

Inicialización de los pesos: Los pesos (monkeyWeight y childWeight) para los «adultos» y las «crías» se inicializan con valores aleatorios en un rango que va de «minW» a «maxW»; los pesos reales se recalcularán tras el primer cálculo de la aptitud.

Distribución por grupos:

  • Los individuos adultos se distribuyen en grupos (groupId) mediante la operación módulo. Es decir, el primer individuo se asigna al grupo 0, el segundo al grupo 1, …, el individuo número numGroups al grupo numGroups - 1, el individuo número (numGroups + 1) de nuevo al grupo 0, y así sucesivamente.
  • Todas las crías formarán parte inicialmente de un mismo equipo.

Inicialización de la aptitud de las crías: El array «childFitness» se inicializa con el valor mínimo posible. Esto se hace para que cualquier valor real calculado de la aptitud sea mayor que este valor inicial.

La función devuelve «true» si la inicialización se ha ejecutado correctamente. De este modo, la función «Init» se encarga de todo el trabajo preparatorio: establece los tamaños necesarios de las estructuras de datos, fija los valores iniciales de la velocidad y los pesos, distribuye los individuos por grupos y los prepara para el primer ciclo de cálculos.

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

  //------------------------------------------------------------------
  // Ajuste de los parámetros
  if (numGroups < 1) numGroups = 1;
  if (numGroups > popSize) numGroups = popSize;

  numChildren = (int)(popSize * childrenRatio);
  if (numChildren < 1) numChildren = 1;

  // Inicialización de arrays para adultos
  ArrayResize (monkeyRate, popSize * coords);
  ArrayResize (monkeyWeight, popSize);
  ArrayResize (groupId, popSize);

  // Inicialización de arrays para las crías
  ArrayResize (childPos, numChildren * coords);
  ArrayResize (childRate, numChildren * coords);
  ArrayResize (childWeight, numChildren);
  ArrayResize (childFitness, numChildren);

  // Paso 2: Inicializar la velocidad Rate y el peso W
  // Rate ∈ [0, 1], W ∈ [4, 6]
  ArrayInitialize (monkeyRate, 0.0);
  ArrayInitialize (childRate, 0.0);

  // Los pesos se actualizarán tras el primer cálculo del fitness
  for (int i = 0; i < popSize; i++)
  {
    monkeyWeight [i] = u.RNDfromCI (minW, maxW);
  }

  for (int i = 0; i < numChildren; i++)
  {
    childWeight [i] = u.RNDfromCI (minW, maxW);
  }

  // Paso 3: Distribución por grupos
  // Los adultos se distribuyen en T grupos
  for (int i = 0; i < popSize; i++)
  {
    groupId [i] = i % numGroups;
  }
  // «while all children in one team» — todas las crías en un mismo equipo desde el principio

  ArrayInitialize (childFitness, -DBL_MAX);

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

El método «Moving» realiza las siguientes acciones: primero se comprueba el indicador «revision» y, si «revision» es «false» (primera ejecución):

Inicialización de la población de adultos (Paso 1). Para cada individuo adulto «i» (de 0 a popSize - 1) y para cada dimensión «j» (de 0 a coords - 1), la posición (a [i].c [j]) se inicializa con un número aleatorio dentro del intervalo especificado (rangeMin [j], rangeMax [j]). A continuación, el valor de la posición se corrige mediante la función «u.SeInDiSp», que garantiza que se mantenga dentro de los límites establecidos y se ajuste al paso (rangeStep [j]).

Inicialización de la población de crías (Paso 1). Para cada cría «i» (de 0 a numChildren - 1) y para cada dimensión «j» (de 0 a coords - 1), la posición (childPos [i * coords + j]) se inicializa con un número aleatorio dentro del intervalo especificado (rangeMin, rangeMax). El valor de la posición también se ajusta mediante la función «u.SeInDiSp».

El indicador «revision» se establece en «true» para que, en las siguientes llamadas al método «Moving», se ejecute otra parte de la lógica. El método finaliza su ejecución. Si «revision» es «true» (ejecuciones posteriores), se invoca el método «UpdatePositions()» para actualizar las posiciones. Este método se encarga de desplazar a los individuos siguiendo la lógica del algoritmo del «mono azul».

De este modo, el método «Moving» tiene dos objetivos principales:

  • La primera vez que se ejecuta, inicializa por completo las posiciones de todos los individuos adultos y crías dentro de los rangos establecidos.
  • En las llamadas posteriores, delega la tarea principal de desplazar a los individuos en el método «UpdatePositions()», que implementa el núcleo del movimiento del algoritmo.
//————————————————————————————————————————————————————————————————————
void C_AO_BM::Moving ()
{
  if (!revision)
  {
    // Paso 1: Inicialización de la población de adultos
    for (int i = 0; i < popSize; i++)
    {
      for (int j = 0; j < coords; j++)
      {
        a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
        a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]);
      }
    }

    // Paso 1: Inicialización de la población de crías
    for (int i = 0; i < numChildren; i++)
    {
      for (int j = 0; j < coords; j++)
      {
        childPos [i * coords + j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
        childPos [i * coords + j] = u.SeInDiSp (childPos [i * coords + j], rangeMin [j], rangeMax [j], rangeStep [j]);
      }
    }

    revision = true;
    return;
  }

  //------------------------------------------------------------------
  UpdatePositions ();
}
//————————————————————————————————————————————————————————————————————

El método «Revision» realiza las siguientes acciones:

Actualización de los pesos. En primer lugar, se invoca el método «UpdateWeights()». Esto significa que los pesos de cada individuo se recalculan en función de sus valores actuales de aptitud.
Intercambio del peor por el mejor. A continuación, se invoca el método «SwapWorstWithBest()». Este método identifica al individuo con el peor valor de aptitud y lo sustituye por el individuo con el mejor valor de aptitud.

    Actualización del mejor individuo. Para los individuos adultos, se recorre cada individuo adulto «i» (de 0 a popSize - 1) y, si su valor actual de aptitud (a [i].f) es mayor que el mejor valor de aptitud que ha alcanzado hasta el momento (a [i].fB), se actualiza el valor de aptitud del individuo. La posición del mejor resultado del individuo (a [i].cB) se copia a partir de la posición actual del individuo (a [i].c).

    Del mismo modo, si el valor actual de aptitud de un individuo adulto (a [i].f) es mayor que el mejor valor de aptitud global de toda la población (fB), se actualiza el mejor valor de aptitud global (fB). La posición de la mejor solución global (cB) se copia a partir de la posición actual del individuo (a [i].c).

    Para las crías. Se itera sobre todas las crías «i» (desde 0 hasta numChildren - 1). Si el fitness de la cría (childFitness [i]) es mayor que el mejor valor global de aptitud (fB), se actualiza el mejor valor global de aptitud (fB). La posición de la mejor solución global (cB) se actualiza copiando las coordenadas de la cría (childPos).

      En general, el método «Revision» está diseñado para mejorar la calidad de las soluciones mediante el seguimiento y el almacenamiento de la mejor solución encontrada tanto para cada individuo (récord personal) como para toda la población en su conjunto (récord global), teniendo en cuenta tanto a los individuos adultos como a las crías.

      //————————————————————————————————————————————————————————————————————
      void C_AO_BM::Revision ()
      {
        // Actualizamos los pesos en función del fitness actual
        UpdateWeights ();
      
        // Paso 8: Intercambio
        SwapWorstWithBest ();
      
        // Paso 12: Actualizar el mejor resultado actual
        for (int i = 0; i < popSize; i++)
        {
          if (a [i].f > a [i].fB)
          {
            a [i].fB = a [i].f;
            ArrayCopy (a [i].cB, a [i].c, 0, 0, coords);
          }
      
          if (a [i].f > fB)
          {
            fB = a [i].f;
            ArrayCopy (cB, a [i].c, 0, 0, coords);
          }
        }
      
        // Comprobación de las crías para actualizar el mejor resultado global
        for (int i = 0; i < numChildren; i++)
        {
          if (childFitness [i] > fB)
          {
            fB = childFitness [i];
            for (int j = 0; j < coords; j++)
            {
              cB [j] = childPos [i * coords + j];
            }
          }
        }
      }
      //————————————————————————————————————————————————————————————————————

      El método «UpdatePositions» de la clase «C_AO_BM» implementa la lógica básica del movimiento de los individuos de la población, siguiendo unas ecuaciones matemáticas determinadas. El método se divide en dos partes: la actualización de las posiciones de los individuos adultos (los llamados «monos azules») y la actualización de las posiciones de las crías.

      Actualización de las posiciones de los individuos adultos (monos azules):

      El método recorre cada individuo adulto «i» (desde 0 hasta popSize - 1). Búsqueda del líder del grupo: para cada individuo actual se determina su grupo (groupId [i]) y se calcula el índice del mejor individuo de ese grupo (bestIdx = FindGroupBest). Si el individuo en cuestión es el propio líder del grupo o no se ha encontrado al líder, pasamos al siguiente individuo. Actualización según las ecuaciones 1 y 2 (para cada dimensión).

      Actualización de la velocidad (ecuación 1):

      • Se obtienen los pesos del líder del grupo «Wleader» y del individuo actual «Wi».
      • Se obtienen las posiciones actuales del líder «Xbest» y del individuo «Xi» en esta dimensión.
      • Se genera un número aleatorio «rand1» comprendido entre 0 y 1.
      • Se obtiene la nueva velocidad «monkeyRate» para el individuo actual. Se trata de una combinación de la velocidad anterior (con un coeficiente de 0,9) y del efecto de la diferencia entre los pesos del líder y del individuo actual, multiplicado por un número aleatorio y por la diferencia entre las posiciones del líder y del individuo actual.

      Actualización de la posición (ecuación 2):

      Se genera otro número aleatorio «rand2» comprendido entre 0 y 1. La nueva posición del individuo se calcula sumando a la posición actual el producto de la nueva velocidad «monkeyRate» y el número aleatorio «rand2». La nueva posición obtenida se limita obligatoriamente a los intervalos especificados (u.SeInDiSp) para que permanezca dentro del área de búsqueda admisible.

        Actualización de las posiciones de las crías:

        1. Búsqueda de la mejor cría: en primer lugar, el método recorre todas las crías «i» de 0 a numChildren - 1 para encontrar la que tenga la mejor aptitud (bestChildFitness) y determinar su índice (bestChildIdx).
        2. Actualización según las ecuaciones 3 y 4 (si se ha encontrado la mejor cría):
          • Si se ha encontrado la mejor cría (bestChildIdx &gt;= 0), el método recorre todas las crías «i» de 0 a numChildren - 1, excepto la mejor.
          • Actualización de la velocidad de la cría (ecuación 3):
            • Al igual que en el caso de los individuos adultos, se determinan los pesos de la mejor cría, «Wchleader», y de la cría actual, «Wchi».
            • Se obtienen las posiciones actuales de la mejor cría «Xchbest» y de la cría actual «Xchi» en esta dimensión.
            • Se genera un número aleatorio «rand1» comprendido entre 0 y 1.
            • Se calcula la nueva velocidad (childRate) para la cría actual mediante una fórmula similar a la de la ecuación 1.
          • Actualización de la posición de la cría (ecuación 4):
            • Se genera un número aleatorio «rand2» entre 0 y 1.
            • La nueva posición de la cría se calcula sumando a su posición actual el producto de su nueva velocidad (childRate) y el número aleatorio «rand2».
          • Restricción de la posición: la nueva posición de la cría también se limita a los intervalos establecidos.

        De este modo, el método «UpdatePositions» implementa:

        • Modelo de comportamiento de los «monos azules»: los individuos adultos se desplazan guiándose por el líder de su grupo, y la velocidad de desplazamiento depende de la diferencia de peso y de la diferencia de posiciones.
        • Modelo de comportamiento de las crías: las crías se desplazan de forma análoga, guiándose por la mejor cría.
        • Mantenimiento de los individuos dentro de la región de búsqueda admisible mediante la función «SeInDiSp».
        //————————————————————————————————————————————————————————————————————
        void C_AO_BM::UpdatePositions ()
        {
          // Paso 9: Actualizar la posición de los monos azules mediante las ecuaciones 1 y 2
          for (int i = 0; i < popSize; i++)
          {
            int bestIdx = FindGroupBest (groupId [i]);
            if (bestIdx < 0 || bestIdx == i) continue;
        
            for (int j = 0; j < coords; j++)
            {
              // Ecuación (1): Actualización de la velocidad
              double Wleader = monkeyWeight [bestIdx];
              double Wi      = monkeyWeight [i];
              double Xbest   = a [bestIdx].c [j];
              double Xi      = a [i].c [j];
              double rand1   = u.RNDfromCI (0, 1);
        
              // Rate_{i,t} = (0.7 * Rate) + (W_leader - W_i) * rand * (X_best - X_i)
              monkeyRate [i * coords + j] = 0.9 * monkeyRate [i * coords + j] +
                                            (Wleader - Wi) * rand1 * (Xbest - Xi);
        
              // Ecuación (2): Actualización de la posición
              // X_{i,t} = X_i + Rate_{i,t} * rand
              double rand2 = u.RNDfromCI (0, 1);
              a [i].c [j] = a [i].c [j] + monkeyRate [i * coords + j] * rand2;
        
              // Limitación dentro del rango
              a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]);
            }
          }
        
          // Paso 10: Actualizar la posición de las crías según las ecuaciones 3 y 4
          // Encontramos a la mejor cría
          int bestChildIdx = -1;
          double bestChildFitness = -DBL_MAX;
          for (int i = 0; i < numChildren; i++)
          {
            if (childFitness [i] > bestChildFitness)
            {
              bestChildFitness = childFitness [i];
              bestChildIdx = i;
            }
          }
        
          if (bestChildIdx >= 0)
          {
            for (int i = 0; i < numChildren; i++)
            {
              if (i == bestChildIdx) continue;
        
              for (int j = 0; j < coords; j++)
              {
                // Ecuación (3): Actualización de la velocidad de las crías
                double Wchleader = childWeight [bestChildIdx];
                double Wchi      = childWeight [i];
                double Xchbest   = childPos [bestChildIdx * coords + j];
                double Xchi      = childPos [i * coords + j];
                double rand1     = u.RNDfromCI (0, 1);
        
                childRate [i * coords + j] = 0.9 * childRate [i * coords + j] +
                                             (Wchleader - Wchi) * rand1 * (Xchbest - Xchi);
        
                // Ecuación (4): Actualización de la posición de las crías
                double rand2 = u.RNDfromCI (0, 1);
                childPos [i * coords + j] = childPos [i * coords + j] + childRate [i * coords + j] * rand2;
        
                childPos [i * coords + j] = u.SeInDiSp (childPos [i * coords + j], rangeMin [j], rangeMax [j], rangeStep [j]);
              }
            }
          }
        }
        //————————————————————————————————————————————————————————————————————

        El método «SwapWorstWithBest» realiza las siguientes acciones:

        Preparación de las crías. Se crea un array «childIndices» para almacenar los índices de todas las crías. Este array se rellena con índices que van desde 0 hasta el número total de crías. A continuación, se ordenan los índices de las crías en orden descendente según su valor de aptitud (childFitness). Esto se hace mediante un sencillo algoritmo de ordenación por burbujas. Como resultado, «childIndices [0]» contendrá el índice de la mejor cría, «childIndices [1]» el índice de la segunda mejor, y así sucesivamente. Se crea un array booleano «childUsed» para llevar un control de si cada cría se ha utilizado para la sustitución.

        Iteración por grupos de individuos adultos. El método recorre cada grupo de individuos adultos (desde g = 0 hasta numGroups - 1). Para cada grupo, si ya se han utilizado todas las crías, se interrumpe el procesamiento posterior. Dentro de cada grupo se identifica al individuo adulto con peor aptitud (worstAdultIdx). Se selecciona la mejor cría disponible en ese momento (utilizando el array ordenado «childIndices» y el contador «childIndex»). Se comprueba si la aptitud de la cría seleccionada (childFitness [currentChildIdx]) es mejor que la del individuo adulto con peor aptitud del grupo (a [worstAdultIdx].f).

        • Sustitución (si es mejor):
          • Si la cría es mejor:
            • La posición y la velocidad del individuo adulto con peor rendimiento se sustituyen por la posición y la velocidad de la cría seleccionada.
            • El valor de la aptitud del individuo adulto se actualiza al valor de la aptitud de la cría.
            • El peso del individuo adulto se actualiza al peso de la cría.
            • La cría se marca como utilizada (childUsed[currentChildIdx] = true) y el contador «childIndex» se incrementa para pasar a la siguiente cría más adecuada.
        • Interrupción de la sustitución (si es peor):
          • Si ni siquiera la mejor cría disponible es mejor que el individuo adulto con peor aptitud del grupo actual, se interrumpen los intentos posteriores de sustitución en este grupo y en los siguientes. Esto se debe a que las crías están ordenadas por aptitud y, si la peor de las mejores crías no supera al individuo con peor aptitud, las crías siguientes tampoco aportarán ninguna ventaja.
          Creación de nuevas crías. Una vez realizadas todas las sustituciones posibles, el método recorre todas las crías. Si se ha utilizado una cría como sustituta (childUsed [i] es igual a true), su posición se vuelve a generar por completo dentro de los rangos especificados (utilizando u.RNDfromCI y u.SeInDiSp), su velocidad se restablece a «0» y su valor de aptitud se restablece al mínimo posible (lo que indica que aún no se ha evaluado ni actualizado). Se le asigna un nuevo peso aleatorio dentro de los límites iniciales (minW, maxW).

            De este modo, el método «SwapWorstWithBest» desempeña la siguiente función: utiliza activamente a las mejores crías para sustituir a los individuos adultos con peor aptitud dentro de sus grupos, siempre que ello suponga una mejora, trasladando así las mejores soluciones jóvenes a los estratos más «adultos» de la población. Las crías que se han utilizado para la sustitución se «regeneran» a continuación con nuevos parámetros aleatorios.

            //————————————————————————————————————————————————————————————————————
            void C_AO_BM::SwapWorstWithBest ()
            {
              // Paso 8: Sustituir el peor fitness de cada grupo por el mejor fitness del grupo de crías
            
              // Creamos un array de índices de las crías y lo ordenamos por fitness
              int childIndices [];
              ArrayResize (childIndices, numChildren);
              for (int i = 0; i < numChildren; i++)
              {
                childIndices [i] = i;
              }
            
              // Ordenamos los índices de las crías en orden descendente según su fitness
              for (int i = 0; i < numChildren - 1; i++)
              {
                for (int j = 0; j < numChildren - i - 1; j++)
                {
                  if (childFitness [childIndices [j]] < childFitness [childIndices [j + 1]])
                  {
                    int temp = childIndices [j];
                    childIndices [j] = childIndices [j + 1];
                    childIndices [j + 1] = temp;
                  }
                }
              }
            
              // Para el seguimiento de las crías utilizadas
              bool childUsed [];
              ArrayResize (childUsed, numChildren);
              ArrayInitialize (childUsed, false);
            
              // Intentamos sustituir al peor de cada grupo
              int childIndex = 0;
            
              for (int g = 0; g < numGroups; g++)
              {
                if (childIndex >= numChildren) break;
            
                // Encontramos al peor del grupo
                int worstAdultIdx = FindGroupWorst (g);
                if (worstAdultIdx < 0) continue;
            
                // Utilizamos la mejor cría disponible
                int currentChildIdx = childIndices [childIndex];
            
                // Comprobamos si la cría es mejor
                if (childFitness [currentChildIdx] > a [worstAdultIdx].f)
                {
                  // Realizamos la sustitución
                  for (int j = 0; j < coords; j++)
                  {
                    a [worstAdultIdx].c [j] = childPos [currentChildIdx * coords + j];
                    monkeyRate [worstAdultIdx * coords + j] = childRate [currentChildIdx * coords + j];
                  }
                  a [worstAdultIdx].f = childFitness [currentChildIdx];
                  monkeyWeight [worstAdultIdx] = childWeight [currentChildIdx];
            
                  childUsed [currentChildIdx] = true;
                  childIndex++;
                }
                else
                {
                  // Si la mejor cría no es mejor que el peor adulto,
                  // No tiene sentido seguir comprobando (ya están ordenados)
                  break;
                }
              }
            
              // Generamos nuevas crías para sustituir a las que se han utilizado
              for (int i = 0; i < numChildren; i++)
              {
                if (childUsed [i])
                {
                  for (int j = 0; j < coords; j++)
                  {
                    childPos [i * coords + j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
                    childPos [i * coords + j] = u.SeInDiSp (childPos [i * coords + j],
                                                            rangeMin [j], rangeMax [j], rangeStep [j]);
                    childRate [i * coords + j] = 0.0;
                  }
                  childFitness [i] = -DBL_MAX;
                  childWeight [i] = u.RNDfromCI (minW, maxW); // Nuevo peso según las condiciones iniciales
                }
              }
            }
            //————————————————————————————————————————————————————————————————————

            La función «UpdateWeights» se encarga de ajustar los pesos de dos grupos de entidades: «monos» (individuos progenitores) y «crías». El ajuste de los pesos se basa en el principio de normalización y escalado en función de la aptitud (fitness) de cada entidad.

            Determinación del rango de aptitud. En primer lugar, la función calcula los valores mínimo (minFit) y máximo (maxFit) de aptitud entre todos los «monos» de la población.

            Tratamiento del caso de valores de aptitud iguales. Si la diferencia entre la aptitud máxima y la aptitud mínima es muy pequeña (inferior a 1e-10, lo que en la práctica significa que todos los valores de aptitud son aproximadamente iguales), a todos los «monos» se les asigna un peso fijo igual a 5,0 (el valor medio del rango deseado).

            Normalización y escalado de los pesos de los «monos». Si el rango de aptitud es suficientemente amplio (superior a 1e-10), ocurre lo siguiente para cada «mono»:

            • Su aptitud (a [i].f) se normaliza en el intervalo [0, 1]. Esto se hace restando la aptitud mínima (minFit) y dividiendo el resultado por la diferencia entre la aptitud máxima y la mínima (maxFit - minFit).
            • A continuación, el valor normalizado obtenido se escala para obtener un peso final en el intervalo [4, 6]. Esto se consigue multiplicando el valor normalizado por 2,0 y sumándole el peso mínimo (minW). De este modo, los individuos con menor aptitud obtienen un peso cercano a 4,0, mientras que los que tienen mayor aptitud obtienen un peso cercano a 6,0.

              Procesamiento de los pesos de las «crías». Tras procesar los «monos», el proceso se repite con las «crías». En primer lugar, se determinan la aptitud mínima (minChildFit) y la máxima (maxChildFit) entre todas las «crías». En este caso, se ignoran las «crías» cuya aptitud no se haya inicializado (se supone que la aptitud no inicializada tiene un valor muy bajo, cercano a -DBL_MAX). Al igual que con los «monos»:

              • si la diferencia entre la aptitud máxima y la mínima de las «crías» es pequeña, a todas las «crías» inicializadas se les asigna un peso de 5,0;
              • si el rango de aptitud de las «crías» es suficiente, su aptitud se normaliza en el rango [0, 1] y, a continuación, se escala al rango [4, 6] de forma similar a como ocurre con los «monos».

                En general, la función «UpdateWeights» transforma los valores de aptitud de los individuos (tanto de los «monos» como de las «crías») en pesos que oscilan entre 4 y 6. Esto se hace con el fin de aumentar o reducir la influencia de los individuos con distintos grados de aptitud en las etapas posteriores del algoritmo. Los individuos con mayor aptitud obtienen un peso más alto, lo que podría indicar que son más importantes.

                //————————————————————————————————————————————————————————————————————
                void C_AO_BM::UpdateWeights ()
                {
                  double minFit = DBL_MAX, maxFit = -DBL_MAX;
                
                  for (int i = 0; i < popSize; i++)
                  {
                    if (a [i].f > maxFit) maxFit = a [i].f;
                    if (a [i].f < minFit) minFit = a [i].f;
                  }
                
                  if (maxFit - minFit > 1e-10)
                  {
                    for (int i = 0; i < popSize; i++)
                    {
                      // Normalizamos en [0,1] y escalamos en [4,6]
                      double normalized = (a [i].f - minFit) / (maxFit - minFit);
                      monkeyWeight [i] = minW + normalized * 2.0; // Obtenemos un valor en el intervalo [4,6]
                    }
                  }
                  else
                  {
                    for (int i = 0; i < popSize; i++)
                    {
                      monkeyWeight [i] = 5.0; // Valor medio
                    }
                  }
                
                  // Actualización de los pesos de las crías
                  double minChildFit = DBL_MAX, maxChildFit = -DBL_MAX;
                
                  for (int i = 0; i < numChildren; i++)
                  {
                    if (childFitness [i] > -DBL_MAX + 1) // Comprobación de la inicialización
                    {
                      if (childFitness [i] > maxChildFit) maxChildFit = childFitness [i];
                      if (childFitness [i] < minChildFit) minChildFit = childFitness [i];
                    }
                  }
                
                  if (maxChildFit - minChildFit > 1e-10)
                  {
                    for (int i = 0; i < numChildren; i++)
                    {
                      if (childFitness [i] > -DBL_MAX + 1)
                      {
                        double normalized = (childFitness [i] - minChildFit) /
                                            (maxChildFit - minChildFit);
                        childWeight [i] = minW + normalized * 2.0; // En el intervalo [4,6]
                      }
                    }
                  }
                  else
                  {
                    for (int i = 0; i < numChildren; i++)
                    {
                      if (childFitness [i] > -DBL_MAX + 1)
                      {
                        childWeight [i] = 5.0;
                      }
                    }
                  }
                }
                //————————————————————————————————————————————————————————————————————

                La función «FindGroupBest» sirve para buscar el mejor individuo dentro de un grupo determinado. La función recorre cada individuo de la población total mediante un bucle (de 0 a popSize - 1).

                  Comprobación de la pertenencia al grupo y comparación de la aptitud. Para cada individuo se comprueba si pertenece al grupo objetivo y, si es así, se comprueba su aptitud (a [i].f). Si el fitness actual (a [i].f) es mayor que el valor máximo actual de la aptitud (bestFitness), entonces:
                  • bestFitness se actualiza con el valor de aptitud del individuo actual.
                  • bestIdx se actualiza con el índice «i» del individuo actual.

                  Una vez finalizado el recorrido completo por la población, la función devuelve el valor «bestIdx». Será el índice del individuo con mayor aptitud dentro del grupo indicado. Si el grupo está vacío o no contiene individuos con una aptitud superior al valor mínimo inicial, la función devolverá -1.

                  Por lo tanto, la función «FindGroupBest» es una herramienta auxiliar para determinar el líder o la mejor solución (individuo) dentro de un subgrupo concreto de una población.

                  //————————————————————————————————————————————————————————————————————
                  int C_AO_BM::FindGroupBest (int grpId)
                  {
                    int bestIdx = -1;
                    double bestFitness = -DBL_MAX;
                  
                    for (int i = 0; i < popSize; i++)
                    {
                      if (groupId [i] == grpId && a [i].f > bestFitness)
                      {
                        bestFitness = a [i].f;
                        bestIdx = i;
                      }
                    }
                  
                    return bestIdx;
                  }
                  //————————————————————————————————————————————————————————————————————

                  La función «FindGroupWorst» recorre cada individuo, empezando por el índice 0 y terminando por el último individuo de la población (popSize - 1).

                  Comprobación de la pertenencia al grupo y comparación de la aptitud. Para cada individuo del bucle, primero se comprueba si pertenece al grupo objetivo, definido por el parámetro «grpId». Si un individuo pertenece al grupo que nos interesa, se compara su valor de aptitud (a [i].f) con el peor valor actual (worstFitness). Si la aptitud del individuo actual es inferior al valor actual más desfavorable (worstFitness), esto significa que hemos encontrado un nuevo individuo aún peor. En ese caso:

                  • worstFitness se actualiza con el valor de aptitud de este nuevo individuo, que es peor;
                  • worstIdx se actualiza para almacenar el índice «i» de este nuevo individuo, que es peor.
                  Una vez finalizado el ciclo (es decir, cuando se hayan comprobado todos los individuos de la población), la función devolverá el valor «worstIdx». Será el índice del individuo con menor aptitud en el grupo indicado. Si el grupo está vacío, se devolverá el valor inicial -1. En otras palabras, la función «FindGroupWorst» busca y devuelve el índice del individuo con el valor de aptitud más bajo dentro del grupo cuyo identificador se ha especificado.

                  //————————————————————————————————————————————————————————————————————
                  int C_AO_BM::FindGroupWorst (int grpId)
                  {
                    int worstIdx = -1;
                    double worstFitness = DBL_MAX;
                  
                    for (int i = 0; i < popSize; i++)
                    {
                      if (groupId [i] == grpId && a [i].f < worstFitness)
                      {
                        worstFitness = a [i].f;
                        worstIdx = i;
                      }
                    }
                  
                    return worstIdx;
                  }
                  //————————————————————————————————————————————————————————————————————


                  Resultados de las pruebas

                  Echemos un vistazo a los resultados; por desgracia, no es suficiente para entrar en la clasificación.

                  BM|Blue Monkey Algorithm|50.0|3.0|0.7|
                  =============================
                  5 Hilly's; Func runs: 10000; result: 0.6730156841791012
                  25 Hilly's; Func runs: 10000; result: 0.3811383925125479
                  500 Hilly's; Func runs: 10000; result: 0.26883473381494066
                  =============================
                  5 Forest's; Func runs: 10000; result: 0.6116553050534701
                  25 Forest's; Func runs: 10000; result: 0.3051920014986885
                  500 Forest's; Func runs: 10000; result: 0.18691769556662757
                  =============================
                  5 Megacity's; Func runs: 10000; result: 0.4384615384615385
                  25 Megacity's; Func runs: 10000; result: 0.19353846153846152
                  500 Megacity's; Func runs: 10000; result: 0.1037384615384623
                  =============================
                  Puntuación total: 3,16249 (35,14 %)

                  En la visualización se aprecia muy bien la división en grupos y las zonas de concentración de los individuos.

                  Hilly

                  BM en la función de prueba «Hilly»

                  Forest

                  BM en la función de prueba Forest

                  Megacity

                  BM en la función de prueba «Megacity»

                  En la tabla de clasificación, el algoritmo BM se presenta a título informativo.

                  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)
                  DOAdingom dingo_optimization_algorithm_M 0,47968 0,45367 0,46369 1,39704 0,94145 0,87909 0,91454 2,73508 0,78615 0,86061 0,84805 2,49481 6,627 73,63
                  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
                  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
                  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
                  (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
                  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
                  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
                  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
                  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
                  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
                  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
                  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
                  EOm extremal_optimization_M 0,76166 0,77242 0,31747 1,85155 0,99999 0,76751 0,23527 2,00277 0,74769 0,53969 0,14249 1,42987 5,284 58,71
                  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
                  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
                  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
                  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
                  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
                  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
                  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
                  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
                  BSA backtracking_search_algorithm 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
                  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
                  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
                  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
                  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
                  DOA dream_optimization_algorithm 0,85556 0,70085 0,37280 1,92921 0,73421 0,48905 0,24147 1,46473 0,77231 0,47354 0,18561 1,43146 4,825 53,62
                  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
                  DEA dolphin_echolocation_algorithm 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
                  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
                  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
                  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
                  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
                  (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
                  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
                  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
                  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
                  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
                  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
                  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
                  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
                  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
                  CMAES covariance_matrix_adaptation_evolution_strategy 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
                  DA_duelist duelist_algorithm 0,92782 0,53778 0,27792 1,74352 0,86957 0,47536 0,18193 1,52686 0,62153 0,33569 0,11715 1,07437 4,345 48,28
                  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
                  BM blue_monkey _algorithm 0,67301 0,38113 0,26883 1,32297 0,61165 0,30519 0,18691 1,10375 0,43846 0,19354 0,10373 0,73573 3,162 35,14
                  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


                  Conclusiones

                  El algoritmo demostró ser funcional en las funciones de prueba, pero no obtuvo resultados lo suficientemente competitivos como para ser incluido en la tabla de clasificación. Los resultados apuntan a la necesidad de seguir optimizando y perfeccionando los mecanismos clave. La estructura en grupos permite explorar el espacio de búsqueda de forma paralela, y el mecanismo de renovación generacional aporta un elemento de actualización a la población; sin embargo, la velocidad de convergencia hacia la solución óptima es inferior a la esperada.

                  El algoritmo Blue Monkey ofrece un enfoque interesante para resolver problemas de optimización, basado en la simulación de procesos naturales. A pesar de las limitaciones actuales en cuanto al rendimiento, el concepto tiene potencial para desarrollarse. La versión actual puede recomendarse con fines educativos y como base para futuras investigaciones en el ámbito de los algoritmos metaheurísticos de optimización.

                  tab

                  Figura 2. Escala de colores 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 es 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 BM:

                  Ventajas:

                  1. Un número reducido de parámetros externos.

                  Desventajas:

                  1. Baja eficacia en la resolución de problemas, especialmente con funciones discretas.

                  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. 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_BM.mq5
                  Script Banco de pruebas para BM

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

                  Archivos adjuntos |
                  BM.ZIP (348.08 KB)
                  Automatización de estrategias de trading en MQL5 (Parte 36): Trading basado en la oferta y la demanda con el modelo de retesteo e impulso Automatización de estrategias de trading en MQL5 (Parte 36): Trading basado en la oferta y la demanda con el modelo de retesteo e impulso
                  En este artículo, crearemos un sistema de trading basado en la oferta y la demanda en MQL5 que identifica las zonas de oferta y demanda mediante rangos de consolidación, las valida con movimientos impulsivos y opera en los retesteos con confirmación de la tendencia y parámetros de riesgo personalizables. El sistema muestra las zonas con etiquetas y colores dinámicos y permite utilizar stops dinámicos para la gestión del riesgo.
                  Redes neuronales en el trading: Entrenamiento de modelos de picos profundos (SEW-ResNet) Redes neuronales en el trading: Entrenamiento de modelos de picos profundos (SEW-ResNet)
                  Le invitamos a conocer el framework SEW-ResNet, que permite construir modelos de picos profundos sin problemas de degradación y con una gestión eficaz de los gradientes. En este artículo mostramos cómo implementar una neurona de pico básica y sus algoritmos utilizando MQL5.
                  Redes neuronales en el trading: Entrenamiento de modelos de picos profundos (Integración de picos) Redes neuronales en el trading: Entrenamiento de modelos de picos profundos (Integración de picos)
                  En este artículo se presenta la implementación práctica de los componentes clave del framework SEW-ResNet usando MQL5. El uso de arrays dinámicos y mecanismos de picos permite diseñar con flexibilidad la arquitectura del modelo y procesar de forma eficaz las series temporales financieras. Las soluciones propuestas muestran cómo SEW-ResNet puede optimizar los cálculos y mejorar la extracción de características relevantes.
                  Automatización de estrategias de trading en MQL5 (Parte 35): Creación de un sistema de trading con bloques de ruptura Automatización de estrategias de trading en MQL5 (Parte 35): Creación de un sistema de trading con bloques de ruptura
                  En este artículo, creamos un sistema de trading con bloques de ruptura en MQL5 que identifica rangos de consolidación, detecta rupturas y valida los bloques de ruptura mediante puntos de oscilación para operar en los retesteos con parámetros de riesgo definidos. El sistema muestra los bloques de órdenes y de ruptura con etiquetas y flechas dinámicas, y permite el trading automatizado y el uso de stops dinámicos.