English Русский 中文 Deutsch 日本語 Português
preview
Algoritmo de optimización basado en la migración animal (Animal Migration Optimization, AMO)

Algoritmo de optimización basado en la migración animal (Animal Migration Optimization, AMO)

MetaTrader 5Probador |
246 0
Andrey Dik
Andrey Dik

Contenido
  1. Introducción
  2. Implementación del algoritmo
  3. Resultados de la prueba


Introducción

Migración animal: armonía y estrategia natural de la naturaleza. Los animales suelen migrar entre zonas de invernada y de reproducción, siguiendo rutas bien establecidas desarrolladas a lo largo de siglos de evolución. Estos viajes estacionales no son vagabundeos aleatorios, sino rutas cuidadosamente planificadas que conducen a áreas más favorables para su supervivencia y reproducción. Dependiendo de la estación del año, los animales se desplazan en busca de alimento, refugio y condiciones adecuadas para la reproducción, guiados por las necesidades naturales de su manada y sus instintos. Cada uno de estos viajes no es sólo una lucha por la existencia, sino también una manifestación de armonía con la naturaleza, donde cada individuo desempeña un papel único en el ecosistema general.

Por ejemplo, los renos migran grandes distancias en busca de mejores tierras de pastoreo, y las aves, como las grullas y los gansos, realizan largos vuelos entre las zonas de invernada y de nidificación, utilizando rutas específicas que se transmiten de generación en generación. Estas migraciones no sólo aseguran la supervivencia de especies individuales, sino que también apoyan al ecosistema en su conjunto, facilitando la polinización de las plantas y la dispersión de semillas.

Inspiración de la naturaleza. El algoritmo AMO (Animal Migration Optimization) fue propuesto en 2013 por el investigador Xiantao Li. La idea principal de este algoritmo es modelar el proceso de migración estacional de los animales en busca de condiciones óptimas para la vida y la reproducción en la naturaleza. El algoritmo se inspiró en la observación del comportamiento de animales migratorios como aves, peces y mamíferos. Estos animales realizan movimientos estacionales entre las zonas de invernada y de reproducción, siguiendo ciertas reglas de interacción desarrolladas por la naturaleza durante la migración.

El algoritmo AMO simula tres componentes principales del movimiento de animales a largas distancias: evitar colisiones con individuos vecinos, moverse en la misma dirección que la bandada (grupo) y mantener una distancia suficiente entre ellos. Estos principios no sólo ayudan a evitar conflictos, sino también a mantener el comportamiento colectivo, que es fundamental para la supervivencia en la naturaleza.

Etapas de optimización en el algoritmo AMO. El algoritmo incluye dos etapas clave de optimización en una iteración:

  • Migración: Durante esta etapa se actualiza la posición del individuo teniendo en cuenta a sus vecinos.
  • Renovación poblacional: en esta etapa los individuos son reemplazados parcialmente por otros nuevos, con una probabilidad que depende de la posición del individuo en la bandada.

Modelar el comportamiento colectivo de los animales migratorios puede ser un enfoque eficaz para resolver problemas de optimización complejos. El algoritmo intenta equilibrar la exploración del espacio de búsqueda y la explotación de las mejores soluciones encontradas, imitando los procesos naturales.


2. Implementaciones de algoritmos

En este modelo algorítmico de migración animal, el concepto básico es crear "zonas" concéntricas alrededor de cada animal. En la zona de repulsión, el animal tiende a alejarse de sus vecinos para evitar colisiones. El algoritmo de la migración animal, según el autor, se divide en dos procesos principales:

1. Migración animal. Un anillo topológico se utiliza para describir un vecindario limitado de individuos. Para mayor comodidad, la longitud del vecindario se establece en cinco para cada dimensión. La topología del vecindario permanece estacionaria y se determina en función de los índices de un individuo en la población. Si el índice del animal es "j", entonces sus vecinos tienen índices [j - 2, j - 1, j, j + 1 y j + 2]. Si el índice de un animal es "1", sus vecinos tendrán índices [N - 1, N, 1, 2, 3] y así sucesivamente. Una vez formada la topología del vecindario, se selecciona un vecino al azar y la posición del individuo se actualiza para reflejar la posición de este vecino. Esta descripción es dada por los autores del algoritmo original. En este caso, la limitación en el número de vecinos se puede pasar a los parámetros del algoritmo para encontrar, mediante experimentos, el mejor número de vecinos para asegurar la mayor eficiencia posible del algoritmo.

2. Renovación poblacional. Durante la renovación de la población, el algoritmo modela cómo algunos animales abandonan el grupo y otros se unen a la población. Los individuos pueden ser reemplazados por nuevos animales con una probabilidad de "p" determinada en función de la calidad de la función de aptitud. Ordenamos la población en orden descendente de los valores de la función de aptitud, lo que significa que la probabilidad de cambiar a un individuo con la mejor aptitud es 1/N, mientras que la probabilidad de cambiar a un individuo con la peor aptitud es 1.

La migración animal y la renovación poblacional, según la versión del autor, pueden describirse algorítmicamente, como se muestra a continuación.

Migración animal:

1. Para cada animal: a. Determinar la vecindad topológica de un animal (5 vecinos más cercanos).
   b. Seleccionar un vecino aleatorio de la lista de vecinos.
   c. Actualizar la posición del animal en la dirección del vecino seleccionado utilizando la siguiente ecuación:
      x_j_new = x_j_old + r * (x_neighbor - x_j_old), donde:
  • x_j_new — nueva posición del j animal,
  • x_j_old — posición actual,
  • x_neighbor — posición del vecino seleccionado,
  • r — número aleatorio de una distribución normal.

   d. Evaluación de la nueva posición del animal mediante la función objetivo.

Renovación poblacional:

1. Ordenar los animales según el valor de la función objetivo en orden descendente. 2. Para cada animal: a. Calcular la probabilidad de reemplazar un animal por un nuevo animal aleatorio:

      p = 1,0 - (1,0 / (x + 1)), donde x es el rango (índice) del i tercer animal de la lista ordenada.

   b. Si un número aleatorio es menor que p, reemplaza el animal (cambia la coordenada al valor promedio de las coordenadas de dos animales seleccionados al azar de la población).    c. De lo contrario, deje el animal sin cambios.

3. Estimación de una nueva población utilizando una función objetivo.

Cambio

Figura 1. Cambiar la probabilidad de un individuo en función de su posición en la población, donde «x» es el índice del individuo en la población.

Escribamos un pseudocódigo para el algoritmo de migración animal AMO.

1. Inicialización de la población animal con posiciones aleatorias.

2. Bucle principal:

   a. Para cada animal:

      Definición de un vecindario topológico.
      Seleccionar un vecino al azar.
      Actualización de la posición del animal en la dirección de su vecino.
      Evaluar un nuevo puesto.

   b. Ordenar la población por los valores de la función objetivo.

   c. Reemplazo probabilístico de animales inferiores por nuevos.

   d. Estimación de la población actualizada.

   e. Actualizando la mejor solución.

3. Repita desde el paso 2 hasta que se cumpla el criterio de detención.

Ahora que estamos familiarizados con el algoritmo, podemos pasar a escribir el código. Echemos un vistazo más de cerca al código de la clase C_AO_AMO:

1. La clase C_AO_AMO se hereda de la clase base C_AO, lo que permite utilizar su funcionalidad y ampliarla.

2. El constructor especifica las características básicas del algoritmo, como el nombre, la descripción y el enlace al artículo. También se inicializan los parámetros del algoritmo, incluido el tamaño de la población, el sesgo y el número de vecinos.

3. popSize, deviation, neighborsNumberOnSide- las variables determinan el tamaño de la población, la desviación estándar y el número de vecinos de un lado que se tendrán en cuenta durante la migración.

4. SetParams — el método se encarga de actualizar los parámetros del algoritmo en función de los valores almacenados en la matriz params.

5. Init — método de inicialización que acepta matrices para los valores de rango mínimo y máximo, pasos y número de épocas. 

6. Moving () — el método se encarga de mover los agentes en el espacio de búsqueda, Revisión () - el método comprueba y actualiza el estado de los agentes en la población.

7. S_AO_Agent population [] — el array almacena la población actual de agentes (animales), S_AO_Agent pTemp [] - array temporal para usar al ordenar la población.

8. GetNeighborsIndex — método privado utilizado para obtener índices de vecinos para un agente específico.

La clase C_AO_AMO implementa un algoritmo de optimización basado en la migración animal, proporcionando los métodos y parámetros necesarios para configurar y ejecutar el algoritmo. Hereda la funcionalidad de la clase base, lo que nos permite extender y modificar su comportamiento dependiendo de los requerimientos de la tarea.

//——————————————————————————————————————————————————————————————————————————————
class C_AO_AMOm : public C_AO
{
  public: //--------------------------------------------------------------------
  ~C_AO_AMOm () { }
  C_AO_AMOm ()
  {
    ao_name = "AMOm";
    ao_desc = "Animal Migration Optimization M";
    ao_link = "https://www.mql5.com/en/articles/15543";

    popSize               = 50;   // Population size
    deviation             = 8;
    neighborsNumberOnSide = 10;

    ArrayResize (params, 3);

    params [0].name = "popSize";               params [0].val = popSize;

    params [1].name = "deviation";             params [1].val = deviation;
    params [2].name = "neighborsNumberOnSide"; params [2].val = neighborsNumberOnSide;
  }

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

    deviation             = params      [1].val;
    neighborsNumberOnSide = (int)params [2].val;
  }

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

  void Moving   ();
  void Revision ();

  //----------------------------------------------------------------------------
  double deviation;
  int    neighborsNumberOnSide;

  S_AO_Agent population []; // Animal population
  S_AO_Agent pTemp      []; // Temporary animal population

  private: //-------------------------------------------------------------------
  int   GetNeighborsIndex (int i);
};
//——————————————————————————————————————————————————————————————————————————————

A continuación, consideremos el código del método Init de la clase C_AO_AMO. Descripción de cada parte del método:

1. rangeMinP[], rangeMaxP[], rangeStepP[] - matrices para determinar los rangos mínimo y máximo de los parámetros optimizados y sus pasos.

      2. El método StandardInit realiza una inicialización estándar basada en los rangos pasados.

      3. Cambio de tamaño de las matrices population y pTemp en popSize.

      4. La inicialización de los agentes se realiza sobre todos los elementos del array population e inicializa cada agente mediante el método Init pasándole el número de coordenadas coords

      5. Si todas las operaciones se realizan correctamente, el método devuelve true.

      El método Init de la clase C_AO_AMO se encarga de inicializar la población de agentes teniendo en cuenta los rangos y parámetros dados.

      //——————————————————————————————————————————————————————————————————————————————
      bool C_AO_AMO::Init (const double &rangeMinP  [],
                           const double &rangeMaxP  [],
                           const double &rangeStepP [],
                           const int     epochsP = 0)
      {
        if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
      
        //----------------------------------------------------------------------------
        ArrayResize (population, popSize);
        ArrayResize (pTemp,      popSize);
      
        for (int i = 0; i < popSize; i++) population [i].Init (coords);
      
        return true;
      }
      //——————————————————————————————————————————————————————————————————————————————
      

      A continuación veremos el método Moving de la clase C_AO_AMO responsable del movimiento de los agentes en la población. Principales partes del código:

      1. Comprueba el estado de revision:

      • Si revision es igual a false, se llama al método por primera vez o después de un reinicio. En este caso, se inicializa la población.
      • Para cada individuo de la población (de 0 a popSize) y para cada coordenada (de 0 a coords), se generan valores aleatorios en el rango especificado (rangeMin y rangeMax).
      • Estos valores son tratados por la función "SeInDiSp", que los ajusta teniendo en cuenta el paso especificado (rangeStep).

      2. Establecer la bandera revision:

      • Después de la inicialización, revision se establece en true, y el método termina.

      3. Ciclo básico de migración:

      • Si revision es igual a true, el método pasa a la lógica de migración principal.
      • Para cada individuo, se vuelve a iterar sobre las coordenadas.
      • GetNeighborsIndex(i) se utiliza para obtener el índice del vecino con el que se comparará el individuo actual.
      • Se calcula la distancia dist entre las coordenadas del vecino y el individuo actual.
      • A partir de esta distancia, se determinan los límites mínimo y máximo (min y max), en los que se encuentra la nueva coordenada.
      4. Ajuste de los valores:
      • Si los límites calculados están fuera del rango aceptable, se ajustan para tener en cuenta rangeMin y rangeMax.
      • A continuación, se calcula el nuevo valor de coordenadas utilizando la distribución normal (función GaussDistribution), que permite tener en cuenta la desviación estándar (deviation).
      • Como en el primer caso, el nuevo valor también se gestiona mediante SeInDiSp.

      El método Moving se encarga de actualizar las posiciones de los agentes en función de sus vecinos y de factores aleatorios. 

      //——————————————————————————————————————————————————————————————————————————————
      void C_AO_AMO::Moving ()
      {
        //----------------------------------------------------------------------------
        if (!revision)
        {
          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]);
            }
          }
      
          revision = true;
          return;
        }
      
        //----------------------------------------------------------------------------
        int    ind1    = 0;
        double dist    = 0.0;
        double x       = 0.0;
        double min     = 0.0;
        double max     = 0.0;
      
        for (int i = 0; i < popSize; i++)
        { 
          for (int c = 0; c < coords; c++)
          {
            //------------------------------------------------------------------------
            ind1 = GetNeighborsIndex (i);
      
            dist = fabs (population [ind1].c [c] - a [i].c [c]);
      
            x    = a [i].c [c];
            min  = x - dist;
            max  = x + dist;
      
            if (min < rangeMin [c]) min = rangeMin [c];
            if (max > rangeMax [c]) max = rangeMax [c];
      
            a [i].c [c] = u.GaussDistribution (x, min, max, deviation);
            a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
      }
      //——————————————————————————————————————————————————————————————————————————————
      

      El siguiente código es el método revision del C_AO_AMO. Veámoslo pieza por pieza:

      1. Encontrar al mejor individuo:

      • La variable ind se utiliza para almacenar el índice del individuo con la mejor función (f).
      • Pasando por toda la población (de 0 a popSize), el código actualiza el valor de fB si el individuo actual tiene el mejor valor de la función.
      • Si se encuentra el mejor individuo, sus características (coordenadas) se copian en la matriz cB.
      2. El ciclo básico del cambio demográfico:
      • Para cada individuo de la población (de 0 a popSize), se calcula la probabilidad prob, que depende del índice i.
      • Para cada coordenada (de 0 a coordenadas), se realiza una comparación aleatoria con la probabilidad prob.
      • Si el número aleatorio es menor que prob, se seleccionan dos individuos aleatorios ind1 y ind2.
      • Si ambos individuos coinciden, ind2 se incrementa para evitar seleccionar al mismo individuo.
      • El nuevo valor de las coordenadas del individuo actual se calcula como la media de las coordenadas de dos individuos aleatorios y, a continuación, se ajusta mediante la función "SeInDiSp", que limita el valor a un rango determinado.
      3. Actualización de la población:
      •    Una vez completados los cambios, se actualiza toda la población copiando los valores de la matriz a.
      •    A continuación se llama a la función Sorting. Ordena la población mediante la función f.
      El uso de condiciones probabilísticas y la selección aleatoria de individuos para actualizar los valores de las coordenadas sugiere que el método pretende encontrar una solución óptima basada en la interacción entre vecinos.
      //——————————————————————————————————————————————————————————————————————————————
      void C_AO_AMO::Revision ()
      {
        //----------------------------------------------------------------------------
        int ind = -1;
      
        for (int i = 0; i < popSize; i++)
        {
          if (a [i].f > fB)
          {
            fB  = a [i].f;
            ind = i;
          }
        }
      
        if (ind != -1) ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY);
      
        //----------------------------------------------------------------------------
        int    ind1 = 0;
        int    ind2 = 0;
        double dist = 0.0;
        double x    = 0.0;
        double min  = 0.0;
        double max  = 0.0;
        double prob = 0.0;
        
        for (int i = 0; i < popSize; i++)
        {
          prob = 1.0 - (1.0 / (i + 1));
          
          for (int c = 0; c < coords; c++)
          {  
            if (u.RNDprobab() < prob)
            {
              ind1 = u.RNDminusOne (popSize);
              ind2 = u.RNDminusOne (popSize);
      
              if (ind1 == ind2)
              {
                ind2++;
                if (ind2 > popSize - 1) ind2 = 0;
              }
      
              a [i].c [c] = (population [ind1].c [c] + population [ind2].c [c]) * 0.5;
              a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
            }
          }
        }
        
        //----------------------------------------------------------------------------
        for (int i = 0; i < popSize; i++) population [i] = a [i];
      
        u.Sorting (population, pTemp, popSize);
      }
      //——————————————————————————————————————————————————————————————————————————————
      

      Por último, consideremos el código del método GetNeighborsIndex de la clase C_AO_AMO encargado de obtener el índice de un vecino aleatorio para el índice i especificado teniendo en cuenta los límites del array.

      1. Inicialización de variables:

      • Ncount — número de vecinos determinado por la variable neighborsNumberOnSide.
      • — número total de vecinos, incluido el propio elemento, se define como Ncount * 2 + 1.

      2. El método utiliza sentencias condicionales para comprobar la posición del índice i respecto a los límites de la matriz.

      3. Manejo de los primeros elementos Ncount (bordes a la izquierda). Si el índice i es menor que Ncount, significa que está al principio de la matriz. Si el índice i es menor que Ncount, significa que está al principio de la matriz.

      4. Manejo de los últimos elementos Ncount (bordes a la derecha). Si el índice i es mayor o igual que popSize - Ncount, significa que se encuentra al final de la matriz. El método genera un índice de vecinos a partir de popSize - N para tener en cuenta los límites.

      5. Manejo de todos los demás elementos. Si el índice de i está en algún lugar en el medio de la matriz, el método genera un índice vecino que está desplazado por Ncount a la izquierda y añade un valor aleatorio de 0 a N-1.

      6. Al final, el método devuelve el índice de vecinos generado.

      El método GetNeighborsIndex permite obtener el índice de un vecino aleatorio para un índice dado de i considerando los límites del array.

      //——————————————————————————————————————————————————————————————————————————————
      int C_AO_AMO::GetNeighborsIndex (int i)
      {
        int Ncount = neighborsNumberOnSide;
        int N = Ncount * 2 + 1;
        int neighborIndex;
      
        // Select a random neighbor given the array boundaries
        if (i < Ncount)
        {
          // For the first Ncount elements
          neighborIndex = MathRand () % N;
        }
        else
        {
          if (i >= popSize - Ncount)
          {
            // For the last Ncount elements
            neighborIndex = (popSize - N) + MathRand () % N;
          }
          else
          {
            // For all other elements
            neighborIndex = i - Ncount + MathRand () % N;
          }
        }
      
        return neighborIndex;
      }
      //——————————————————————————————————————————————————————————————————————————————
      

      Ahora, una vez que hemos terminado de escribir el algoritmo, vamos a comprobar cómo funciona. Resultados de la versión original del algoritmo:

      AMO|Animal Migration Optimization|50.0|1.0|2.0|
      =============================
      5 Hilly's; Func runs: 10000; result: 0.43676335174918435
      25 Hilly's; Func runs: 10000; result: 0.28370099295372453
      500 Hilly's; Func runs: 10000; result: 0.25169663266864406
      =============================
      5 Forest's; Func runs: 10000; result: 0.312993148861033
      25 Forest's; Func runs: 10000; result: 0.23960515885149344
      500 Forest's; Func runs: 10000; result: 0.18938496103401775
      =============================
      5 Megacity's; Func runs: 10000; result: 0.18461538461538463
      25 Megacity's; Func runs: 10000; result: 0.12246153846153851
      500 Megacity's; Func runs: 10000; result: 0.10223076923076994
      =============================
      All score: 2.12345 (23.59%)

      Desafortunadamente, la versión original muestra cualidades de búsqueda débiles. Dichos indicadores no se incluirán en la tabla de calificación. El análisis de los resultados reveló una brecha significativa entre el algoritmo y los demás participantes, lo que me llevó a repensarlo profundamente.

      Al examinar la estrategia más de cerca, se descubrió un fallo clave: la clasificación de la población no contribuía a la acumulación de material genético de los mejores individuos. Sólo cambió su disposición topológica sin afectar su esencia. La influencia de la clasificación se limitó únicamente a ajustar la probabilidad de cambiar las coordenadas de los individuos en el espacio de búsqueda, y esta probabilidad es inversamente proporcional a su aptitud.

      Es de destacar que las nuevas coordenadas se formaron exclusivamente sobre la base de las ya existentes en la población, promediando los valores de dos individuos seleccionados al azar. El reconocimiento de estos matices condujo a la idea de ampliar la población para integrar a las crías en el grupo parental antes de realizar la clasificación. Este enfoque no sólo preservará las mejores combinaciones genéticas, sino que también desplazará naturalmente a los individuos menos adaptados. Sin duda, el problema de la actualización del acervo genético de la población sigue siendo relevante, pero la modificación propuesta debería incrementar significativamente la dinámica del proceso evolutivo. Para implementar esta idea, comenzamos cambiando el método de inicialización duplicando el tamaño de la población principal.

      Presentemos el código de inicialización completo, donde podemos ver la duplicación de la población padre.

      //——————————————————————————————————————————————————————————————————————————————
      bool C_AO_AMOm::Init (const double &rangeMinP  [],
                            const double &rangeMaxP  [],
                            const double &rangeStepP [],
                            const int     epochsP = 0)
      {
        if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
      
        //----------------------------------------------------------------------------
        ArrayResize (population, popSize * 2);
        ArrayResize (pTemp,      popSize * 2);
      
        for (int i = 0; i < popSize * 2; i++) population [i].Init (coords);
      
        return true;
      }
      //——————————————————————————————————————————————————————————————————————————————
      

      En consecuencia, es necesario corregir el método Revision:

      //----------------------------------------------------------------------------
      for (int i = 0; i < popSize; i++) population [i + popSize] = a [i];
      
      u.Sorting (population, pTemp, popSize * 2);
      

      Después de los ajustes apropiados, probaremos nuevamente el algoritmo y compararemos los resultados:

      AMOm|Animal Migration Optimization M|50.0|1.0|2.0|
      =============================
      5 Hilly's; Func runs: 10000; result: 0.4759595972704031
      25 Hilly's; Func runs: 10000; result: 0.31711543296080447
      500 Hilly's; Func runs: 10000; result: 0.2540492181444619
      =============================
      5 Forest's; Func runs: 10000; result: 0.40387880560608347
      25 Forest's; Func runs: 10000; result: 0.27049305409901064
      500 Forest's; Func runs: 10000; result: 0.19135802944407254
      =============================
      5 Megacity's; Func runs: 10000; result: 0.23692307692307696
      25 Megacity's; Func runs: 10000; result: 0.14461538461538465
      500 Megacity's; Func runs: 10000; result: 0.10109230769230851
      =============================
      All score: 2.39548 (26.62%)

      En este caso, vemos una mejora en el resultado general del 3%, lo que indica las posibilidades de éxito en el camino elegido.

      A continuación, intentaremos transferir el cambio probabilístico de los individuos en función del rango al método Moving. Esto permitirá realizar cambios en las coordenadas de las personas inmediatamente después de recibir nuevas coordenadas de sus vecinos más cercanos.

      //----------------------------------------------------------------------------
      int    ind1 = 0;
      int    ind2 = 0;
      double dist = 0.0;
      double x    = 0.0;
      double min  = 0.0;
      double max  = 0.0;
      double prob = 0.0;
      
      for (int i = 0; i < popSize; i++)
      {
        prob = 1.0 - (1.0 / (i + 1));
          
        for (int c = 0; c < coords; c++)
        {
          //------------------------------------------------------------------------
          ind1 = GetNeighborsIndex (i);
      
          dist = fabs (population [ind1].c [c] - a [i].c [c]);
      
          x    = a [i].c [c];
          min  = x - dist;
          max  = x + dist;
      
          if (min < rangeMin [c]) min = rangeMin [c];
          if (max > rangeMax [c]) max = rangeMax [c];
      
          a [i].c [c] = u.GaussDistribution (x, min, max, deviation);
            
          //----------------------------------------------------------------------------
          if (u.RNDprobab() < prob)
          {
            ind1 = u.RNDminusOne (popSize);
            ind2 = u.RNDminusOne (popSize);
      
            if (ind1 == ind2)
            {
              ind2++;
              if (ind2 > popSize - 1) ind2 = 0;
            }
      
            a [i].c [c] = (population [ind1].c [c] + population [ind2].c [c]) * 0.5;
          }
            
          a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
        }
      }
      

      Revisemos nuevamente los resultados:

      AMO|Animal Migration Optimization|50.0|1.0|2.0|
      =============================
      5 Hilly's; Func runs: 10000; result: 0.7204154413083147
      25 Hilly's; Func runs: 10000; result: 0.4480389094268583
      500 Hilly's; Func runs: 10000; result: 0.25286213277651365
      =============================
      5 Forest's; Func runs: 10000; result: 0.7097109421461968
      25 Forest's; Func runs: 10000; result: 0.3299544372347476
      500 Forest's; Func runs: 10000; result: 0.18667655927410348
      =============================
      5 Megacity's; Func runs: 10000; result: 0.41076923076923083
      25 Megacity's; Func runs: 10000; result: 0.20400000000000001
      500 Megacity's; Func runs: 10000; result: 0.09586153846153929
      =============================
      All score: 3.35829 (37.31%)

      Esto es mucho mejor y vale la pena continuar. Después de algunos experimentos con el código, obtuvimos la versión final del método Moving:

      //----------------------------------------------------------------------------
        int    ind1    = 0;
        int    ind2    = 0;
        double dist    = 0.0;
        double x       = 0.0;
        double min     = 0.0;
        double max     = 0.0;
        double prob    = 0.0;
      
        for (int i = 0; i < popSize; i++)
        {
          prob = 1.0 - (1.0 / (i + 1));
          
          for (int c = 0; c < coords; c++)
          {
            //------------------------------------------------------------------------
            ind1 = GetNeighborsIndex (i);
      
            dist = fabs (population [ind1].c [c] - a [i].c [c]);
      
            x    = population [ind1].c [c];
            min  = x - dist;
            max  = x + dist;
      
            if (min < rangeMin [c]) min = rangeMin [c];
            if (max > rangeMax [c]) max = rangeMax [c];
      
            a [i].c [c] = u.GaussDistribution (x, min, max, deviation);
            a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      
      
            //------------------------------------------------------------------------
            if (u.RNDprobab() < prob)
            {
              if (u.RNDprobab() <= 0.01)
              {
                ind1 = u.RNDminusOne (popSize);
                ind2 = u.RNDminusOne (popSize);
      
                //if (ind1 == ind2)
                {
                  //ind2++;
                  //if (ind2 > popSize - 1) ind2 = 0;
      
                  a [i].c [c] = (population [ind1].c [c] + population [ind2].c [c]) * 0.5;
                  a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
                }
              }
              //ind1 = u.RNDminusOne (popSize);
              //a [i].c [c] = population [ind1].c [c];
            }
          }
        }
      }
      //——————————————————————————————————————————————————————————————————————————————
      

      Pasemos del método Moving a la versión final del método Revision del C_AO_AMO encargado de actualizar y ordenar la población de agentes.

      //——————————————————————————————————————————————————————————————————————————————
      void C_AO_AMO::Revision ()
      {
        //----------------------------------------------------------------------------
        int ind = -1;
      
        for (int i = 0; i < popSize; i++)
        {
          if (a [i].f > fB)
          {
            fB  = a [i].f;
            ind = i;
          }
        }
      
        if (ind != -1) ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY);
      
        
        //----------------------------------------------------------------------------
        for (int i = 0; i < popSize; i++) population [popSize + i] = a [i];
      
        u.Sorting (population, pTemp, popSize * 2);
      }
      //——————————————————————————————————————————————————————————————————————————————
      
      Una vez que el código está finalmente formado, pasamos a realizar pruebas nuevamente.


      3. Resultados de la prueba

      Resultados del banco de pruebas AMO:

      AMOm|Animal Migration Optimization|50.0|8.0|10.0|
      =============================
      5 Hilly's; Func runs: 10000; result: 0.9627642143272663
      25 Hilly's; Func runs: 10000; result: 0.8703754433240446
      500 Hilly's; Func runs: 10000; result: 0.467183248460726
      =============================
      5 Forest's; Func runs: 10000; result: 0.9681183408862706
      25 Forest's; Func runs: 10000; result: 0.9109372988714968
      500 Forest's; Func runs: 10000; result: 0.4719026790932256
      =============================
      5 Megacity's; Func runs: 10000; result: 0.6676923076923076
      25 Megacity's; Func runs: 10000; result: 0.5886153846153845
      500 Megacity's; Func runs: 10000; result: 0.23546153846153978
      =============================
      All score: 6.14305 (68.26%)

      Podemos ver resultados elevados en la tabla de clasificación. Sin embargo, el precio es una gran diferencia de valores finales en funciones de pequeñas dimensiones. Realicemos 50 pruebas en lugar de las 10 habituales.

      AMOm|Animal Migration Optimization|50.0|8.0|10.0|
      =============================
      5 Hilly's; Func runs: 10000; result: 0.903577388020872
      25 Hilly's; Func runs: 10000; result: 0.8431723262743616
      500 Hilly's; Func runs: 10000; result: 0.46284266807030283
      =============================
      5 Forest's; Func runs: 10000; result: 0.9900061169785055
      25 Forest's; Func runs: 10000; result: 0.9243600311397848
      500 Forest's; Func runs: 10000; result: 0.4659761237381695
      =============================
      5 Megacity's; Func runs: 10000; result: 0.5676923076923077
      25 Megacity's; Func runs: 10000; result: 0.5913230769230771
      500 Megacity's; Func runs: 10000; result: 0.23773230769230896
      =============================
      All score: 5.98668 (66.52%)

      Ahora los resultados son más realistas, pero la eficiencia también ha disminuido ligeramente.

      Hilly

       AMOm sobre la función Hilly.

      Forest

      AMOm sobre la función Forest

      Megacity

      AMOm sobre la función Megacity

      Después de algunas transformaciones sorprendentes, el algoritmo ocupa con confianza el tercer lugar en la tabla de clasificación.

      # AO Descripción Hilly Hilly final Forest Forest final Megaciudad (discreto) Megacity final Resultado final % 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 búsqueda en todo el vecindario 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 algoritmo de bloqueo de código 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 Optimización de la migración animal 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 Estrategias de evolución (P+O) 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 algoritmo de cola de cometa 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 SDSm búsqueda por difusión estocástica 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
      7 ESG evolución de los grupos sociales 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
      8 SIA recocido isotrópico simulado 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
      9 ACS búsqueda cooperativa artificial 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
      10 ASO anarquía sociedad optimización 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
      11 TSEA algoritmo de evolución del caparazón de tortuga 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
      12 DE evolución diferencial 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
      13 CRO optimización de reacciones químicas 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
      14 BSA algoritmo de enjambre de aves 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
      15 HS búsqueda de armonías 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
      16 SSG árboles, siembra y crecimiento 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
      17 (PO)ES (PO) estrategias de evolución 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
      18 BSO optimización brain storm 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
      19 WOAm algoritmo de optimización de wale 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
      20 AEFA algoritmo de campo eléctrico artificial 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
      21 ACOm optimización de colonias de hormigas 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
      22 BFO-GA Optimización de la alimentación bacteriana - 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
      23 ABHA algoritmo de colmena artificial 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
      24 ASBO optimización del comportamiento social adaptativo 0.76331 0.49253 0.32619 1.58202 0.79546 0.40035 0.26097 1.45677 0.26462 0.17169 0.18200 0.61831 3.657 40.63
      25 MEC computación evolutiva de la mente 0.69533 0.53376 0.32661 1.55569 0.72464 0.33036 0.07198 1.12698 0.52500 0.22000 0.04198 0.78698 3.470 38.55
      26 IWO optimización de malezas invasoras 0.72679 0.52256 0.33123 1.58058 0.70756 0.33955 0.07484 1.12196 0.42333 0.23067 0.04617 0.70017 3.403 37.81
      27 Micro-AIS microsistema inmunológico artificial 0.79547 0.51922 0.30861 1.62330 0.72956 0.36879 0.09398 1.19233 0.37667 0.15867 0.02802 0.56335 3.379 37.54
      28 COAm algoritmo de optimización de cuco M 0.75820 0.48652 0.31369 1.55841 0.74054 0.28051 0.05599 1.07704 0.50500 0.17467 0.03380 0.71347 3.349 37.21
      29 SDOm Optimización de la dinámica espiral M 0.74601 0.44623 0.29687 1.48912 0.70204 0.34678 0.10944 1.15826 0.42833 0.16767 0.03663 0.63263 3.280 36.44
      30 NMm Método de Nelder-Mead M 0.73807 0.50598 0.31342 1.55747 0.63674 0.28302 0.08221 1.00197 0.44667 0.18667 0.04028 0.67362 3.233 35.92
      31 FAm algoritmo de luciérnaga M 0.58634 0.47228 0.32276 1.38138 0.68467 0.37439 0.10908 1.16814 0.28667 0.16467 0.04722 0.49855 3.048 33.87
      32 GSA algoritmo de búsqueda gravitacional 0.64757 0.49197 0.30062 1.44016 0.53962 0.36353 0.09945 1.00260 0.32667 0.12200 0.01917 0.46783 2.911 32.34
      33 BFO optimización de la búsqueda de alimento bacteriano 0.61171 0.43270 0.31318 1.35759 0.54410 0.21511 0.05676 0.81597 0.42167 0.13800 0.03195 0.59162 2.765 30.72
      34 ABC colonia de abejas artificial 0.63377 0.42402 0.30892 1.36671 0.55103 0.21874 0.05623 0.82600 0.34000 0.14200 0.03102 0.51302 2.706 30.06
      35 BA algoritmo de murciélago 0.59761 0.45911 0.35242 1.40915 0.40321 0.19313 0.07175 0.66810 0.21000 0.10100 0.03517 0.34617 2.423 26.93
      36 SA recocido simulado 0.55787 0.42177 0.31549 1.29513 0.34998 0.15259 0.05023 0.55280 0.31167 0.10033 0.02883 0.44083 2.289 25.43
      37 IWDm Gotas de agua inteligentes M 0.54501 0.37897 0.30124 1.22522 0.46104 0.14704 0.04369 0.65177 0.25833 0.09700 0.02308 0.37842 2.255 25.06
      38 PSO optimización del enjambre de partículas 0.59726 0.36923 0.29928 1.26577 0.37237 0.16324 0.07010 0.60572 0.25667 0.08000 0.02157 0.35823 2.230 24.77
      39 Boids algoritmo de boids 0.43340 0.30581 0.25425 0.99346 0.35718 0.20160 0.15708 0.71586 0.27846 0.14277 0.09834 0.51957 2.229 24.77
      40 MA algoritmo del mono 0.59107 0.42681 0.31816 1.33604 0.31138 0.14069 0.06612 0.51819 0.22833 0.08567 0.02790 0.34190 2.196 24.40
      41 SFL Salto de rana barajado 0.53925 0.35816 0.29809 1.19551 0.37141 0.11427 0.04051 0.52618 0.27167 0.08667 0.02402 0.38235 2.104 23.38
      42 FSS búsqueda de bancos de peces 0.55669 0.39992 0.31172 1.26833 0.31009 0.11889 0.04569 0.47467 0.21167 0.07633 0.02488 0.31288 2.056 22.84
      43 RND aleatorio 0.52033 0.36068 0.30133 1.18234 0.31335 0.11787 0.04354 0.47476 0.25333 0.07933 0.02382 0.35648 2.014 22.37
      44 GWO optimizador de lobo gris 0.59169 0.36561 0.29595 1.25326 0.24499 0.09047 0.03612 0.37158 0.27667 0.08567 0.02170 0.38403 2.009 22.32
      45 CSS búsqueda de sistema cargado 0.44252 0.35454 0.35201 1.14907 0.24140 0.11345 0.06814 0.42299 0.18333 0.06300 0.02322 0.26955 1.842 20.46



      Resumen

      Con base en los resultados del algoritmo AMOm en funciones de prueba, se pueden extraer las siguientes conclusiones: a pesar de la dispersión de valores en funciones de pequeña dimensión, el algoritmo muestra una excelente escalabilidad en funciones de gran dimensión. Los principales cambios realizados a la versión original del algoritmo mejoraron significativamente su rendimiento. En este caso, duplicar la población principal (para ordenar junto con los individuos hija) y cambiar la secuencia de ejecución de las etapas del algoritmo hicieron posible obtener una gama más amplia de soluciones diversas. Este algoritmo se convirtió en un claro ejemplo de las posibilidades de utilizar técnicas adicionales para su modificación, lo que condujo a mejoras significativas. Esto fue posible gracias a la mejora de la propia lógica del algoritmo, que no siempre funciona en relación con otros algoritmos de optimización. 

      pestaña

      Figura 2. Gradación por colores de los algoritmos según las pruebas pertinentes Los resultados superiores o iguales a 0,99 se resaltan en blanco

      gráfico

      Figura 3. El histograma de los resultados de la prueba del algoritmo (en una escala de 0 a 100, cuanto más, mejor).

      Donde 100 es el resultado teórico máximo posible, el archivo incluye un script para calcular la tabla de calificación)


      Pros y contras de AMOm:

      Ventajas:

      1. Excelente convergencia.
      2. Alta escalabilidad.
      3. Pocos parámetros.
      4. Implementación sencilla.

      Desventajas:

      1. Dispersión de resultados en funciones de baja dimensión.

      El artículo se acompaña de un archivo con las versiones actuales de los códigos del algoritmo. El autor del artículo no es responsable de la exactitud absoluta en la descripción de los algoritmos canónicos. Se han realizado cambios en muchos de ellos para mejorar las capacidades de búsqueda. Las conclusiones y juicios presentados en los artículos se basan en los resultados de los experimentos.


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

      Archivos adjuntos |
      AMO.zip (31.3 KB)
      Redes neuronales en el trading: Modelo Universal de Generación de Trayectorias (UniTraj) Redes neuronales en el trading: Modelo Universal de Generación de Trayectorias (UniTraj)
      La comprensión del comportamiento de los agentes es importante en distintos ámbitos, pero la mayoría de los métodos se centran en una única tarea (comprensión, eliminación del ruido, predicción), lo cual reduce su eficacia en escenarios del mundo real. En este artículo, propongo al lector introducir un modelo capaz de adaptarse a diferentes tareas.
      Formulación de un Asesor Experto Multipar Dinámico (Parte 1): Correlación de divisas y correlación inversa Formulación de un Asesor Experto Multipar Dinámico (Parte 1): Correlación de divisas y correlación inversa
      El asesor experto dinámico de múltiples pares aprovecha las estrategias de correlación y correlación inversa para optimizar el rendimiento comercial. Al analizar datos del mercado en tiempo real, identifica y explota la relación entre pares de divisas.
      Del básico al intermedio: Array (II) Del básico al intermedio: Array (II)
      En este artículo, veremos qué es un array dinámico y un array estático. ¿Existe diferencia entre usar uno u otro? ¿O ambos son siempre lo mismo? ¿Cuándo debo usar uno y cuándo usar el otro? ¿Y los arrays constantes? ¿Por qué existen y cuál es el riesgo que corro, cuando no inicializo todos los valores de un array? Suponiendo que serán iguales a cero. El contenido expuesto aquí tiene un propósito puramente didáctico. En ningún caso debe considerarse como una aplicación final, si el objetivo no es el estudio de los conceptos mostrados aquí.
      Creación de un modelo de restricción de tendencia de velas (Parte 8): Desarrollo de un asesor experto (II) Creación de un modelo de restricción de tendencia de velas (Parte 8): Desarrollo de un asesor experto (II)
      Piense en un asesor experto independiente. Anteriormente, analizamos un Asesor Experto basado en indicadores que también se asoció con un script independiente para dibujar la geometría de riesgo y recompensa. Hoy discutiremos la arquitectura de un Asesor Experto MQL5, que integra todas las características en un solo programa.