Русский Português
preview
Optimización de arrecifes de coral — Coral Reefs Optimization (CRO)

Optimización de arrecifes de coral — Coral Reefs Optimization (CRO)

MetaTrader 5Trading |
36 0
Andrey Dik
Andrey Dik


Contenido

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


Introducción

Los algoritmos bioinspirados basados en procesos y sistemas naturales han despertado últimamente un gran interés entre los desarrolladores. Entre estos algoritmos, destaca el algoritmo de optimización de arrecifes de coral (CRO), propuesto originalmente por S. Salcedo-Sanz y coautores en 2014.

El algoritmo CRO se basa en la modelización de los procesos de formación y desarrollo de los arrecifes de coral en la naturaleza. Estos procesos incluyen diversos mecanismos de reproducción de los corales (reproducción sexual externa e interna, así como reproducción asexual), la competencia por un espacio limitado en el arrecife y la muerte de los individuos débiles. Al igual que la evolución da forma a arrecifes de coral estables y adaptados en la naturaleza, el algoritmo CRO nos permite explorar el espacio de búsqueda y encontrar soluciones óptimas o casi óptimas a diferentes problemas.

Este artículo presentará una versión mejorada del algoritmo CROm con un mecanismo de aniquilación modificado basado en el uso de la distribución inversa de grados para generar nuevas soluciones en las proximidades de la mejor. El enfoque propuesto no solo conserva las ventajas tradicionales del CRO, como el poder exploratorio y el equilibrio natural entre la exploración global y la explotación local del espacio de búsqueda, sino que las complementa con un mecanismo más eficiente que permite localizar con mayor precisión las áreas de búsqueda prometedoras y converger más rápidamente a soluciones óptimas.

Realizaremos pruebas exhaustivas del algoritmo propuesto en un conjunto de funciones de prueba de optimización clásicas, demostrando su mejor rendimiento en comparación con el algoritmo CRO original y otras metaheurísticas modernas. Los resultados experimentales muestran que el enfoque propuesto resulta particularmente eficaz para problemas con funciones objetivo multimodales y una estructura compleja del paisaje de búsqueda.

El artículo se estructura de la siguiente manera: en primer lugar, repasaremos los conceptos básicos y los principios de funcionamiento del algoritmo CRO estándar; y a continuación, describiremos con detalle las modificaciones propuestas y su justificación teórica. A continuación, evaluaremos experimentalmente el algoritmo y analizaremos los resultados. Por último, discutiremos los resultados obtenidos, las limitaciones del método propuesto y los posibles rumbos para futuras investigaciones.


Implementación del algoritmo

La idea básica del CRO consiste en modelar colonias de coral que se desarrollen y compitan por el espacio en el arrecife, formando finalmente una estructura óptima. Cada coral del arrecife representa una solución potencial al problema de optimización analizado.

El arrecife se modela como una red bidimensional de tamaño N×M. Cada celda de la cuadrícula puede estar ocupada por un coral o estar vacía. El coral representa una solución codificada a un problema de optimización. Para cada coral, se define una función de aptitud (salud) que se corresponde con la función objetivo del problema de optimización.

El parámetro ρ₀ ∈ (0,1) define la fracción de ocupación coralina inicial del arrecife, es decir, la relación entre las celdas ocupadas y el número total de celdas al inicio del algoritmo. La inicialización del arrecife se realiza de la forma que sigue:

  1. Luego se especifica el tamaño del arrecife N×M y la fracción de ocupación inicial ρ₀.
  2. Después se seleccionan aleatoriamente ⌊ρ₀ × N × M⌋ celdas de arrecife para alojar a los corales iniciales.
  3. Los corales iniciales se generan de forma aleatoria dentro del área de búsqueda y se colocan en las celdas seleccionadas.

Una vez inicializado el arrecife, se inicia un proceso iterativo de formación y desarrollo del arrecife que consta de varias etapas:

Reproducción sexual externa (Broadcast Spawning). Para este tipo de propagación se selecciona una proporción determinada de los corales Fₑ existentes. Los corales seleccionados forman parejas y usan operadores de cruces para crear descendencia. Cada pareja produce una larva mediante un operador de cruce (promedio normal).

Reproducción sexual interna (Brooding). La fracción restante de corales (1-Fₑ) participa en la reproducción interna, en la que cada coral produce descendencia mediante mutación. Para cada coral seleccionado para la reproducción interna, se crea una larva usando el operador de mutación, y suele representar una pequeña variación aleatoria en la solución codificada. Asentamiento de larvas. Una vez formadas las larvas durante las fases de cría, cada una intenta fijar su residencia en el arrecife. El proceso de asentamiento se lleva a cabo según las siguientes reglas:

  1. La larva elige al azar una celda (i, j) del arrecife.
  2. Si una celda está vacía, la larva la ocupa.
  3. Si una celda está ocupada, una larva solo puede desplazar a un coral existente si su aptitud es mayor: f(larva) > f(Ξᵢⱼ).
  4. Si no se produce el desplazamiento, la larva puede tratar de asentarse en otro lugar (hasta un número máximo de intentos k).
  5. Si tras k intentos la larva no ha podido encontrar un lugar, muere.

Reproducción asexual (Budding). Los mejores corales del arrecife (fracción Fₐ) pueden reproducirse asexualmente, creando copias exactas (clones) de sí mismos. Técnicamente:

  1. Los corales se clasifican de acuerdo con la importancia de la función de adaptación.
  2. Los mejores corales Fₐ × 100% se seleccionan para la reproducción asexual.
  3. Cada coral seleccionado crea un clon que trata de asentarse en el arrecife siguiendo las mismas reglas que el proceso de asentamiento larvario.

Extinción. Al final de cada iteración, los peores corales del arrecife pueden morir con una probabilidad Pd, dejando sitio a nuevos corales en las siguientes iteraciones.

El proceso de formación de arrecifes se repite hasta que se cumple el criterio de parada indicado, alcanzando el número máximo de iteraciones. Tras detenerse, el mejor coral del arrecife representa la solución encontrada al problema de optimización.

cro-algorithm

Figura 1. Esquema del algoritmo CRO

La ilustración anterior muestra los seis pasos principales del algoritmo:

  1. Inicialización (ρ₀ = 0,6) — muestra un entramado bidimensional (arrecife) parcialmente lleno de corales multicolores que representan diferentes soluciones.
  2. Reproducción externa (Fb = 0,9)  — muestra los corales que forman parejas y crean larvas mediante el cruce.
  3. Reproducción interna (1-Fb = 0,1)  — muestra corales individuales que producen larvas por mutación.
  4. Asentamiento de larvas (k = 3 intentos)  — ilustra el proceso de búsqueda de un lugar en el arrecife por parte de las larvas, incluidos los intentos con éxito y sin éxito.
  5. Reproducción asexual (Fa = 0,1)  — muestra los mejores corales (marcados con asteriscos) haciendo copias exactas de sí mismos.
  6. Extinción (Fd = 0,1, Pd = 0,05)  — muestra la eliminación de los peores corales del arrecife.
Ahora podemos pasar a escribir el pseudocódigo del algoritmo.

Entrada: Parámetros de arrecife (N, M, ρ₀, Fₑ, Fₐ, Fd, Pd, k).
Salida: La mejor solución encontrada

1. Inicialización:
   - Creamos un arrecife de tamaño N×M
   - Llenamos ⌊ρ₀ × N × M⌋ celdas con corales al azar.
   - Calculamos la aptitud de cada coral

2. Hasta que se cumpla el criterio de parada:
   3. Reproducción sexual externa:
      - Seleccionamos una proporción de corales Fₑ para participar
      - Se forman parejas y se crean larvas mediante cruces
   
   4. Reproducción sexual interna:
      - Para que los corales restantes (1-Fₑ) creen larvas por mutación
   
   5. Asentamiento de larvas:
      - Para cada larva:
        - Se intenta ocupar una celda de arrecife aleatoria
        - Si se ocupa, desplaza al coral existente con mayor adaptación
        - Si no lo consigue, lo intenta de nuevo (hasta k intentos)
   
   6. Reproducción asexual:
      - Se clasifican los corales por adaptación
      - Los mejores corales Fₐ crean clones de ellos
      - Los clones intentan asentarse en el arrecife
   
   7. Extinción:
      - Con probabilidad Pd de realizar la destrucción
      - Se elimina la fracción Fd de los peores corales

8. Recuperamos el mejor coral del arrecife

Ahora podemos componer el código del algoritmo CRO.  Escribiremos la clase "C_AO_CRO", que implementará el algoritmo CRO y heredará de la clase básica "C_AO". 

Parámetros de CRO:

La clase declara los parámetros que controlarán el comportamiento del algoritmo:

  • popSize — tamaño de la población de corales.
  • reefRows  — altura del arrecife (número de filas en la cuadrícula).
  • reefCols  — anchura del arrecife (número de columnas de la cuadrícula).
  • rho0  — ocupación inicial del arrecife. Es el porcentaje de celdas de arrecife ocupadas por corales al inicio del algoritmo.
  • Fb — proporción de corales que participan en la reproducción externa (Broadcast Spawning).
  • Fa  — proporción de corales que participan en la reproducción asexual (fragmentación).
  • Fd  — proporción de corales que deben eliminarse debido a su baja aptitud.
  • Pd  — probabilidad de destrucción del coral.
  • attemptsNum  — número de intentos que hace una larva para asentarse en el arrecife.

Métodos de clase:

  • SetParams ()  — el método establece los valores de los parámetros del algoritmo basándose en los valores almacenados en el array "params", que es un array que permite cambiar dinámicamente los parámetros del algoritmo en tiempo de ejecución.
  • Init ()  — método de inicialización, acepta los rangos de búsqueda para las variables de optimización (rangeMinP, rangeMaxP, rangeStepP) y el número de épocas (epochsP). El método "Init" realizará los preparativos necesarios para poner en marcha el algoritmo, como inicializar la población y el arrecife.
  • Moving ()  — la función implementa la lógica básica del movimiento de los corales en el arrecife. 
  • Revision ()  — el método se encarga de revisar y corregir las posiciones de los corales en el arrecife.
  • InitReef ()  — inicializa la estructura del arrecife, preparándola para el asentamiento de los corales.
  • LarvaSettling ()  — determina dónde se asentará una larva de coral en el arrecife, modelando el proceso de repoblación del arrecife por nuevos corales. El parámetro "larva" representará la larva de un coral.
  • BroadcastSpawning ()  — proceso de reproducción externa en el que los corales liberan larvas en el agua.
  • Brooding ()  — proceso reproductivo alternativo en el que las larvas se desarrollan en el interior del coral.
  • AsexualReproduction () — proceso de reproducción asexual en el que los corales se fragmentan y forman nuevas colonias.
  • Depredation () — proceso de extinción en el que se destruyen los corales.
  • GetReefCoordIndex ()  — convierte las coordenadas del arrecife (fila y columna) en un índice de array unidimensional.
  • SortAgentsByFitness ()  — clasifica los corales según su aptitud (fitness).

Variables privadas:

  • totalReefSize  — tamaño total del arrecife (producto de "reefRows" y "reefCols").
  • ocupado []  — array de valores booleanos que indican si cada celda del arrecife está ocupada por un coral.
  • reefIndices []  — array de índices que contiene los índices de los corales (del array "a []" de la clase básica "C_AO") en celdas de arrecife ocupadas.
//——————————————————————————————————————————————————————————————————————————————
class C_AO_CRO : public C_AO
{
  public: //--------------------------------------------------------------------
  ~C_AO_CRO () { }
  C_AO_CRO ()
  {
    ao_name = "CRO";
    ao_desc = "Coral Reef Optimization";
    ao_link = "https://www.mql5.com/es/articles/17760";
    
    popSize     = 50;      // размер популяции
    reefRows    = 20;      // высота рифа
    reefCols    = 20;      // ширина рифа
    rho0        = 0.2;     // начальная занятость рифа
    Fb          = 0.99;    // доля кораллов для внешнего размножения
    Fa          = 0.01;    // доля кораллов для бесполого размножения
    Fd          = 0.8;     // доля кораллов для удаления
    Pd          = 0.9;     // вероятность уничтожения
    attemptsNum = 20;      // количество попыток для оседания личинок

    ArrayResize (params, 9);

    params [0].name = "popSize";     params [0].val = popSize;
    params [1].name = "reefRows";    params [1].val = reefRows;
    params [2].name = "reefCols";    params [2].val = reefCols;
    params [3].name = "rho0";        params [3].val = rho0;
    params [4].name = "Fb";          params [4].val = Fb;
    params [5].name = "Fa";          params [5].val = Fa;
    params [6].name = "Fd";          params [6].val = Fd;
    params [7].name = "Pd";          params [7].val = Pd;
    params [8].name = "attemptsNum"; params [8].val = attemptsNum;
  }

  void SetParams ()
  {
    popSize     = (int)params [0].val;
    reefRows    = (int)params [1].val;
    reefCols    = (int)params [2].val;
    rho0        = params      [3].val;
    Fb          = params      [4].val;
    Fa          = params      [5].val;
    Fd          = params      [6].val;
    Pd          = params      [7].val;
    attemptsNum = (int)params [8].val;
  }

  bool Init (const double &rangeMinP  [],  // минимальный диапазон поиска
             const double &rangeMaxP  [],  // максимальный диапазон поиска
             const double &rangeStepP [],  // шаг поиска
             const int     epochsP = 0);   // количество эпох

  void Moving        ();
  void Revision      ();

  //----------------------------------------------------------------------------
  int                reefRows;      // высота рифа
  int                reefCols;      // ширина рифа
  double             rho0;          // начальная занятость рифа
  double             Fb;            // доля кораллов для внешнего размножения
  double             Fa;            // доля кораллов для бесполого размножения
  double             Fd;            // доля кораллов для удаления
  double             Pd;            // вероятность уничтожения
  int                attemptsNum;   // количество попыток для оседания личинок

  private: //-------------------------------------------------------------------
  int                totalReefSize; // общий размер рифа
  bool   occupied    [];   // флаги занятости клеток рифа
  int                reefIndices []; // индексы агентов в a[], соответствующие занятым клеткам

  // Вспомогательные методы
  void   InitReef            ();
  int    LarvaSettling       (S_AO_Agent &larva);
  void   BroadcastSpawning   (S_AO_Agent &larvae [], int &larvaCount);
  void   Brooding            (S_AO_Agent &larvae [], int &larvaCount);
  void   AsexualReproduction ();
  void   Depredation         ();
  int    GetReefCoordIndex   (int row, int col);
  void   SortAgentsByFitness (int &indices [], int &count);
};
//——————————————————————————————————————————————————————————————————————————————

El método "Init" inicializa el algoritmo CRO: llama a "StandardInit" de la clase básica, luego calcula el tamaño total del arrecife "totalReefSize", determina el número inicial de corales "initialPopSize", inicializa los arrays "occupied" (ocupación de celdas) y "reefIndices"  (índices de corales), llama a "InitReef ()" para colocar los corales en el arrecife y finalmente devolverá "true" en caso de éxito.

//——————————————————————————————————————————————————————————————————————————————
bool C_AO_CRO::Init (const double &rangeMinP  [],  // минимальный диапазон поиска
                     const double &rangeMaxP  [],  // максимальный диапазон поиска
                     const double &rangeStepP [],  // шаг поиска
                     const int     epochsP = 0)    // количество эпох
{
  // Стандартная инициализация родительского класса
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //----------------------------------------------------------------------------
  // Рассчитываем общий размер рифа
  totalReefSize = reefRows * reefCols;

  // Количество начальных кораллов не должно превышать popSize
  int initialPopSize = (int)MathRound (rho0 * totalReefSize);
  if (initialPopSize > popSize) initialPopSize = popSize;

  // Инициализация массива занятости и индексов
  ArrayResize (occupied, totalReefSize);
  ArrayResize (reefIndices, totalReefSize);

  // Заполняем массивы начальными значениями
  for (int i = 0; i < totalReefSize; i++)
  {
    occupied    [i] = false;
    reefIndices [i] = -1;
  }

  // Инициализация рифа
  InitReef ();

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

El método "InitReef ()" de la clase "C_AO_CRO" inicializa la población inicial de corales en el arrecife. Primero se calcula el número de corales a inicializar basándose en una densidad dada del arrecife (rho0), este número está limitado por el tamaño de la población "popSize". A continuación, para cada coral, se asigna una posición aleatoria pero libre en el arrecife; una vez encontrada, se marca como ocupada y el coral se vincula a esa posición en el array "reefIndices". Después se generan las coordenadas para cada coral. Cada coordenada se selecciona aleatoriamente dentro de los límites especificados (rangeMin, rangeMax) y se discretiza usando la función "u.SeInDiSp" que tiene en cuenta el paso de muestreo (rangeStep). Esto garantiza una distribución uniforme y controlada de las soluciones (corales) iniciales en el espacio de búsqueda.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_CRO::InitReef ()
{
  // Количество начальных кораллов в рифе (исходя из rho0)
  int initialCorals = (int)MathRound (rho0 * totalReefSize);

  // Количество начальных кораллов не должно превышать размер популяции
  if (initialCorals > popSize) initialCorals = popSize;

  // Инициализируем initialCorals случайных позиций в рифе
  for (int i = 0; i < initialCorals; i++)
  {
    int pos;
    // Ищем свободную позицию
    do
    {
      pos = (int)MathFloor (u.RNDfromCI (0, totalReefSize));
      // Защита от выхода за пределы массива
      if (pos < 0) pos = 0;
      if (pos >= totalReefSize) pos = totalReefSize - 1;
    }
    while (occupied [pos]);

    // Создаем новый коралл на найденной позиции
    occupied [pos] = true;
    reefIndices [pos] = i;

    // Генерируем случайные координаты для нового коралла
    for (int c = 0; c < coords; c++)
    {
      double coordinate = u.RNDfromCI (rangeMin [c], rangeMax [c]);
      a [i].c [c] = u.SeInDiSp (coordinate, rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método "Moving ()" realiza una estimación inicial de la población de corales. Si "revision" se establece en "false", el método iterará a través de todas las posiciones del arrecife. Para cada posición ocupada (determinada por el valor del elemento del array "occupied"), el método recupera el índice del coral en esa posición del array "reefIndices". Si el índice "idx" resultante es válido, se realizará un cálculo de la aptitud de ese coral.

Debemos señalar que el método "Moving ()" no calcula directamente el alojamiento en sí, sino que solo proporciona la información necesaria para calcularlo. Una vez completada la iteración para todas las posiciones del arrecife, el método establecerá el valor "revision" en "true" para evitar que el ciclo de evaluación del coral vuelva a pasar por la misma iteración de optimización. Esencialmente, el método "Moving ()" actúa como disparador para calcular la aptitud de los corales, garantizando que este proceso se realice una vez en cada iteración de la optimización.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_CRO::Moving ()
{
  if (!revision)
  {
    // Первичная оценка всех кораллов в рифе
    for (int i = 0; i < totalReefSize; i++)
    {
      if (occupied [i])
      {
        int idx = reefIndices [i];
        if (idx >= 0 && idx < popSize)
        {
          // Расчет приспособленности не требует использования GetFitness()
          // так как он будет вычислен во внешнем коде (в FuncTests)
        }
      }
    }

    revision = true;
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método "Revision ()" actualiza la mejor solución global: busca los corales en el arrecife con una aptitud mejor que la mejor solución global actual y la sustituye. Luego crea un array preparando espacio para las larvas producidas por la reproducción. A continuación, el método activa la reproducción sexual: llama a los métodos "BroadcastSpawning" y "Brooding" para crear larvas. El siguiente paso del método implementa el asentamiento de larvas, usando el método "LarvaSettling" para identificar las ubicaciones de las nuevas larvas en el arrecife. Después comienza la reproducción asexual "AsexualReproduction", al final tiene lugar la destrucción. En resumen: actualiza la solución, reproduce corales, coloca larvas y modela el impacto de la extinción.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_CRO::Revision ()
{

  // Обновление глобального наилучшего решения
  for (int i = 0; i < totalReefSize; i++)
  {
    if (occupied [i] && a [reefIndices [i]].f > fB)
    {
      fB = a [reefIndices [i]].f;
      ArrayCopy (cB, a [reefIndices [i]].c, 0, 0, WHOLE_ARRAY);
    }
  }

  // Формируем массив для хранения личинок
  S_AO_Agent larvae [];
  ArrayResize (larvae, totalReefSize * 2); // Выделяем с запасом
  int larvaCount = 0;

  // Этап 1: Broadcast Spawning (внешнее половое размножение)
  BroadcastSpawning (larvae, larvaCount);

  // Этап 2: Brooding (внутреннее половое размножение)
  Brooding (larvae, larvaCount);

  // Вычисляем функцию приспособленности для каждой личинки
  // (будет выполнено в внешнем коде в FuncTests)

  // Этап 3: Оседание личинок
  for (int i = 0; i < larvaCount; i++)
  {
    LarvaSettling (larvae [i]);
  }

  // Этап 4: Бесполое размножение
  AsexualReproduction ();

  // Этап 5: Вымирание
  Depredation ();
}
//——————————————————————————————————————————————————————————————————————————————

El método LarvaSettling simula el proceso de asentamiento de las larvas en el arrecife. Intenta encontrar un lugar adecuado para la larva y, o bien la coloca en el espacio vacante, o bien sustituye a un coral ya existente pero menos adaptable. Al intentar ubicarse, la larva intenta ubicarse "attemptsNum" veces. En cada intento se selecciona una posición aleatoria en el arrecife. Si la posición seleccionada está libre (no ocupada por un coral), el método buscará un índice libre en el array de agentes "a []", si se encuentra un índice libre, la larva copiará su solución (coordenadas "c []" y aptitud "f") en la celda correspondiente del array de agentes.

Luego la información de que la posición en el arrecife está ahora ocupada se actualizará, y el método retornará el índice de la posición ocupada con éxito "pos". Si la posición seleccionada está ocupada, el método comparará la aptitud de la larva con la aptitud del coral actual en esa posición. Si la larva es mejor, sustituirá al coral existente copiando sus parámetros en su celda del array de agentes, y el método devolverá el índice "pos" de la posición en la que se ha producido la sustitución. Si, después de todos los intentos, la larva no ha logrado ubicarse (ya sea en un espacio vacante o sustituyendo a un coral existente), el método devolverá -1.

//——————————————————————————————————————————————————————————————————————————————
int C_AO_CRO::LarvaSettling (S_AO_Agent &larva)
{
  // Пытаемся заселить личинку attemptsNum раз
  for (int attempt = 0; attempt < attemptsNum; attempt++)
  {
    // Выбираем случайную позицию в рифе
    int pos = (int)MathFloor (u.RNDfromCI (0, totalReefSize));

    // Проверяем, что позиция находится в пределах массива
    if (pos < 0 || pos >= totalReefSize) continue;

    // Если позиция свободна, заселяем личинку
    if (!occupied [pos])
    {
      // Ищем свободный индекс в массиве агентов
      int newIndex = -1;
      for (int i = 0; i < popSize; i++)
      {
        bool used = false;
        for (int j = 0; j < totalReefSize; j++)
        {
          if (reefIndices [j] == i)
          {
            used = true;
            break;
          }
        }

        if (!used)
        {
          newIndex = i;
          break;
        }
      }

      if (newIndex != -1)
      {
        // Копируем решение личинки
        ArrayCopy (a [newIndex].c, larva.c, 0, 0, WHOLE_ARRAY);
        a [newIndex].f = larva.f;

        // Обновляем информацию о рифе
        occupied [pos] = true;
        reefIndices [pos] = newIndex;

        return pos;
      }
    }
    // Если позиция занята, проверяем, лучше ли личинка текущего коралла
    else
      if (occupied [pos] && reefIndices [pos] >= 0 && reefIndices [pos] < popSize && larva.f > a [reefIndices [pos]].f)
      {
        // Личинка вытесняет существующий коралл
        ArrayCopy (a [reefIndices [pos]].c, larva.c, 0, 0, WHOLE_ARRAY);
        a [reefIndices [pos]].f = larva.f;

        return pos;
      }
  }

  // Если личинке не удалось заселиться, возвращаем -1
  return -1;
}
//——————————————————————————————————————————————————————————————————————————————

El método BroadcastSpawning simula la reproducción externa de los corales por desove. Realiza los siguientes pasos básicos: encuentra los índices de todos los lugares ocupados por el coral en el arrecife, determina el número de corales que participan en el desove basándose en el parámetro "Fb" (Brooding Fraction), la proporción de corales que participan en la reproducción externa, mezcla los índices de los corales ocupados y, a continuación, selecciona las parejas para el desove. Creación de larvas por cruce: se crea una nueva larva para cada pareja de corales, las coordenadas de la larva se calculan como la media de las coordenadas de los progenitores con una pequeña mutación aleatoria añadida. Este proceso de cruce busca combinar los mejores rasgos de los progenitores, las larvas creadas se añaden al array "larvae".

Puntos clave:

  • "Fb" aquí es responsable de la proporción de corales en la reproducción externa, en contraposición a "Brooding", que se refiere a la proporción de corales dedicados a la reproducción interna.
  • El cruce y la mutación aumentan la diversidad genética de una población.
  • El objetivo del método es crear nuevas larvas derivadas de una combinación de genes de corales progenitores para que sigan colonizando el arrecife. El método consiste en el cruce por parejas.
//——————————————————————————————————————————————————————————————————————————————
void C_AO_CRO::BroadcastSpawning (S_AO_Agent &larvae [], int &larvaCount)
{
  // Находим все занятые позиции
  int occupiedIndices [];
  int occupiedCount = 0;

  for (int i = 0; i < totalReefSize; i++)
  {
    if (occupied [i])
    {
      ArrayResize (occupiedIndices, occupiedCount + 1);
      occupiedIndices [occupiedCount] = i;
      occupiedCount++;
    }
  }

  // Проверка на случай, если нет занятых позиций
  if (occupiedCount == 0) return;

  // Выбираем долю Fb для внешнего размножения
  int broadcastCount = (int)MathRound (Fb * occupiedCount);
  if (broadcastCount <= 0) broadcastCount = 1; // Минимум один коралл
  if (broadcastCount > occupiedCount) broadcastCount = occupiedCount;

  // Перемешиваем индексы
  for (int i = 0; i < occupiedCount; i++)
  {
    // Фиксируем проблему выхода за пределы массива
    int j = (int)MathFloor (u.RNDfromCI (0, occupiedCount));

    // Гарантируем, что j в пределах массива
    if (j >= 0 && j < occupiedCount && i < occupiedCount)
    {
      int temp = occupiedIndices [i];
      occupiedIndices [i] = occupiedIndices [j];
      occupiedIndices [j] = temp;
    }
  }

  // Образуем пары и создаем потомство
  for (int i = 0; i < broadcastCount - 1; i += 2)
  {
    if (i + 1 < broadcastCount) // Проверяем, что есть второй родитель
    {
      int idx1 = reefIndices [occupiedIndices [i]];
      int idx2 = reefIndices [occupiedIndices [i + 1]];

      if (idx1 >= 0 && idx1 < popSize && idx2 >= 0 && idx2 < popSize)
      {
        // Инициализируем личинку
        larvae [larvaCount].Init (coords);

        // Создаем новую личинку как результат скрещивания
        for (int c = 0; c < coords; c++)
        {
          // Простой метод скрещивания: среднее значение координат родителей с небольшой мутацией
          double value = (a [idx1].c [c] + a [idx2].c [c]) / 2.0 + u.RNDfromCI (-0.1, 0.1) * (rangeMax [c] - rangeMin [c]);
          larvae [larvaCount].c [c] = u.SeInDiSp (value, rangeMin [c], rangeMax [c], rangeStep [c]);
        }

        // Увеличиваем счетчик личинок
        larvaCount++;
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método Brooding imita el proceso de incubación interna de larvas en corales en el CRO y sigue los pasos siguientes:

  • determina qué posiciones del arrecife están ocupadas por corales y almacena los índices de estas posiciones. 
  • calcula cuántos corales participarán en la incubación de larvas usando el parámetro "Fb" (Brooding Fraction). Los índices de las posiciones ocupadas se barajan para seleccionar aleatoriamente los corales que participan en la reproducción. 
  • crea y muta las larvas: se crea una larva para cada coral seleccionado. La larva se inicializa y muta: sus coordenadas se desvían ligeramente de las del coral progenitor de forma aleatoria. Las larvas mutadas se añaden al array "larvae".

Puntos clave:

  • "Fb" define la proporción de corales NO implicados en la reproducción asexual (es decir, implicados en la cría interna).
  • La mutación aporta diversidad genética a la población larvaria.
  • El objetivo es crear nuevos individuos, potencialmente mejores, que compitan por un lugar en el arrecife.
//——————————————————————————————————————————————————————————————————————————————
void C_AO_CRO::Brooding (S_AO_Agent &larvae [], int &larvaCount)
{
  // Находим все занятые позиции
  int occupiedIndices [];
  int occupiedCount = 0;

  for (int i = 0; i < totalReefSize; i++)
  {
    if (occupied [i])
    {
      ArrayResize (occupiedIndices, occupiedCount + 1);
      occupiedIndices [occupiedCount] = i;
      occupiedCount++;
    }
  }

  // Проверка на случай, если нет занятых позиций
  if (occupiedCount == 0) return;

  // Количество кораллов для внутреннего размножения
  int broodingCount = (int)MathRound ((1.0 - Fb) * occupiedCount);
  if (broodingCount <= 0) broodingCount = 1; // Минимум один коралл
  if (broodingCount > occupiedCount) broodingCount = occupiedCount;

  // Перемешиваем индексы
  for (int i = 0; i < occupiedCount; i++)
  {
    // Фиксируем проблему выхода за пределы массива
    int j = (int)MathFloor (u.RNDfromCI (0, occupiedCount));

    // Гарантируем, что j в пределах массива
    if (j >= 0 && j < occupiedCount && i < occupiedCount)
    {
      int temp = occupiedIndices [i];
      occupiedIndices [i] = occupiedIndices [j];
      occupiedIndices [j] = temp;
    }
  }

  // Для каждого выбранного коралла создаем мутированную копию
  for (int i = 0; i < broodingCount; i++)
  {
    if (i < occupiedCount) // Проверка на выход за границы
    {
      int idx = reefIndices [occupiedIndices [i]];

      if (idx >= 0 && idx < popSize)
      {
        // Инициализируем личинку
        larvae [larvaCount].Init (coords);

        // Создаем новую личинку как мутацию исходного коралла
        for (int c = 0; c < coords; c++)
        {
          // Мутация: добавляем небольшое случайное отклонение
          double value = a [idx].c [c] + u.RNDfromCI (-0.2, 0.2) * (rangeMax [c] - rangeMin [c]);
          larvae [larvaCount].c [c] = u.SeInDiSp (value, rangeMin [c], rangeMax [c], rangeStep [c]);
        }

        // Увеличиваем счетчик личинок
        larvaCount++;
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método de reproducción asexual modela la reproducción asexual de los corales por gemación. El método realiza los siguientes pasos: encuentra los índices de todas las posiciones ocupadas por corales en el arrecife, clasifica los índices de las posiciones ocupadas en orden descendente de aptitud, es decir, las posiciones con los corales más adaptables aparecen en primer lugar. Se basa en el parámetro "Fa" (Fraction for Asexual reproduction), que establece la proporción de corales que participan en la reproducción asexual. Se determina el número de mejores corales que producirán clones. Creación y asentamiento de clones: para cada coral seleccionado, se crea una copia exacta (clon), es decir, una larva con las mismas coordenadas y aptitud. Se llama al método "LarvaSettling" para intentar ubicar un clon en el arrecife.

Puntos clave:

  • La reproducción asexual permite a los corales mejor adaptados propagar rápidamente sus genes.
  • El parámetro "Fa" controla la intensidad de la reproducción asexual.
  • Usar "LarvaSettling" para ubicar los clones implica que éstos deben competir por el espacio del arrecife del mismo modo que las larvas producidas sexualmente. 
  • El método de clonación, en este caso, supone una copia exacta del progenitor SIN mutaciones. Las mutaciones solo se producen durante la reproducción sexual.
//——————————————————————————————————————————————————————————————————————————————
void C_AO_CRO::AsexualReproduction ()
{
  // Находим все занятые позиции и их индексы
  int occupiedIndices [];
  int occupiedCount = 0;

  for (int i = 0; i < totalReefSize; i++)
  {
    if (occupied [i])
    {
      ArrayResize (occupiedIndices, occupiedCount + 1);
      occupiedIndices [occupiedCount] = i;
      occupiedCount++;
    }
  }

  // Если нет занятых позиций, выходим
  if (occupiedCount == 0) return;

  // Сортируем индексы по приспособленности
  SortAgentsByFitness (occupiedIndices, occupiedCount);

  // Выбираем лучшие Fa% кораллов для бесполого размножения
  int budCount = (int)MathRound (Fa * occupiedCount);
  if (budCount <= 0) budCount = 1; // Минимум один коралл
  if (budCount > occupiedCount) budCount = occupiedCount;

  // Для каждого выбранного коралла создаем клон и пытаемся заселить его
  for (int i = 0; i < budCount; i++)
  {
    if (i < occupiedCount) // Проверка на выход за границы
    {
      int idx = reefIndices [occupiedIndices [i]];

      if (idx >= 0 && idx < popSize)
      {
        // Создаем новую личинку как точную копию исходного коралла
        S_AO_Agent clone;
        clone.Init (coords);
        ArrayCopy (clone.c, a [idx].c, 0, 0, WHOLE_ARRAY);
        clone.f = a [idx].f;

        // Пытаемся заселить клон
        LarvaSettling (clone);
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método "Depredation" modela el proceso de extinción de los corales debido a la depredación o a otros factores negativos (por ejemplo, la contaminación). En este caso, la destrucción se aplica con una probabilidad "Pd". Si el número aleatorio es inferior a "Pd", se ejecutará el proceso de destrucción. La búsqueda de posiciones ocupadas determinará los índices de todas las posiciones del arrecife ocupadas por corales. Los índices de ocupación se clasifican según la aptitud (fitness) de los corales.

El array de índices ordenados se invierte para que los índices de los corales menos adaptados estén al principio del array. Luego se determina la cantidad de coral a eliminar en función del parámetro "Fd" (Fraction for Depredation). Este valor se redondea a un número entero. Después se elimina un número determinado de los corales menos adaptados. A continuación, se limpia el arrecife: para cada posición programada para eliminarse, la celda correspondiente del array "ocupado" se pone en "false", lo que indica que la posición está ahora libre. La celda correspondiente del array "reefIndices" se pone a -1, lo cual indica que no hay coral en esa posición.

//——————————————————————————————————————————————————————————————————————————————
oid C_AO_CRO::Depredation()
{
  // Применяем уничтожение с вероятностью Pd
  if (u.RNDfromCI(0, 1) < Pd)
  {
    // Находим все занятые позиции и их индексы
    int occupiedIndices[];
    int occupiedCount = 0;

    for (int i = 0; i < totalReefSize; i++)
    {
      if (occupied[i])
      {
        ArrayResize(occupiedIndices, occupiedCount + 1);
        occupiedIndices[occupiedCount] = i;
        occupiedCount++;
      }
    }

    // Если нет занятых позиций, выходим
    if (occupiedCount == 0) return;

    // Сортируем индексы по приспособленности
    SortAgentsByFitness(occupiedIndices, occupiedCount);

    // Переворачиваем массив, чтобы худшие были первыми
    for (int i = 0; i < occupiedCount / 2; i++)
    {
      if(i < occupiedCount && (occupiedCount - 1 - i) < occupiedCount) // Проверка на выход за границы
      {
        int temp = occupiedIndices[i];
        occupiedIndices[i] = occupiedIndices[occupiedCount - 1 - i];
        occupiedIndices[occupiedCount - 1 - i] = temp;
      }
    }

    // Удаляем худшие Fd% кораллов
    int removeCount = (int)MathRound(Fd * occupiedCount);
    if (removeCount <= 0) removeCount = 1; // Минимум один коралл
    if (removeCount > occupiedCount) removeCount = occupiedCount; // Защита от переполнения

    for (int i = 0; i < removeCount; i++)
    {
      if(i < occupiedCount) // Проверка на выход за границы
      {
        int pos = occupiedIndices[i];
        if(pos >= 0 && pos < totalReefSize) // Дополнительная проверка
        {
          occupied[pos] = false;
          reefIndices[pos] = -1;
        }
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

El método "GetReefCoordIndex" convierte las coordenadas del arrecife (fila y columna) en un índice unidimensional y toma la fila "row" y la columna "col" como entrada, y devolverá el índice correspondiente en un array que representa el arrecife. El método comprueba primero si las coordenadas transmitidas están fuera de los límites del arrecife. En caso contrario, se calcula el índice mediante la fórmula (row * reefCols + col, donde "reefCols" es el número de columnas del arrecife). Este método se usa para acceder a los elementos de los arrays que representan el arrecife.

//——————————————————————————————————————————————————————————————————————————————
int C_AO_CRO::GetReefCoordIndex (int row, int col)
{
  // Проверка на выход за границы
  if (row < 0 || row >= reefRows || col < 0 || col >= reefCols) return -1;

  // Переводим двухмерную позицию в одномерный индекс
  return row * reefCols + col;
}
//——————————————————————————————————————————————————————————————————————————————

El método "SortAgentsByFitness" clasifica el array de índices "indices" de los corales de "count" elementos en orden descendente según su aptitud utilizando un algoritmo de clasificación de burbujas que se realiza basándose en los valores de aptitud almacenados en el array "a" al que se accede a través del array "reefIndices". Así, tras la ejecución del método, "indices" contendrá los índices de los corales clasificados del más adaptado al menos adaptado. Hemos incluido comprobaciones adicionales para evitar que se sobrepasen los límites del array.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_CRO::SortAgentsByFitness (int &indices [], int &count)
{
  // Проверка на пустой массив
  if (count <= 0) return;

  // Сортировка пузырьком по убыванию приспособленности
  for (int i = 0; i < count - 1; i++)
  {
    for (int j = 0; j < count - i - 1; j++)
    {
      if (j + 1 < count) // Проверка, чтобы j+1 не выходил за пределы
      {
        int idx1 = reefIndices [indices [j]];
        int idx2 = reefIndices [indices [j + 1]];

        if (idx1 >= 0 && idx1 < popSize && idx2 >= 0 && idx2 < popSize) // Проверка индексов
        {
          if (a [idx1].f < a [idx2].f)
          {
            int temp = indices [j];
            indices [j] = indices [j + 1];
            indices [j + 1] = temp;
          }
        }
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

Ya hemos terminado de preparar el algoritmo, y también hemos descrito en el código todos los numerosos métodos para su funcionamiento; ahora podemos proceder directamente a la prueba.


Resultados de las pruebas

Tras las pruebas, observamos que el algoritmo CRO funciona bastante mal y que es necesario revisar algunos métodos del enfoque original.

CRO|Coral Reef Optimization|50.0|5.0|5.0|0.4|0.9|0.1|0.1|0.01|3.0|
=============================
5 Hilly's; Func runs: 10000; result: 0.365266682511984
25 Hilly's; Func runs: 10000; result: 0.270828009448956
500 Hilly's; Func runs: 10000; result: 0.2504192846772352
=============================
5 Forest's; Func runs: 10000; result: 0.23618879234608753
25 Forest's; Func runs: 10000; result: 0.19453106526100442
500 Forest's; Func runs: 10000; result: 0.1679109693993047
=============================
5 Megacity's; Func runs: 10000; result: 0.13076923076923075
25 Megacity's; Func runs: 10000; result: 0.11138461538461542
500 Megacity's; Func runs: 10000; result: 0.09366153846153921
=============================
All score: 1.82096 (20.23%)

En primer lugar, hemos analizado en el algoritmo CRO el método de destrucción. Para mejorar la búsqueda, sustituiremos los peores corales del arrecife por otros nuevos que se generen en las proximidades de los mejores corales (soluciones), desplazando así la búsqueda hacia zonas más prometedoras. Luego determinaremos el número de corales mejores "eliteCount" y peores "removeCount". 

Sustitución de las peores soluciones: para cada "víctima", se selecciona un coral "de élite" y se genera un nuevo coral en las proximidades de ese coral "de élite". En el mecanismo de destrucción mejorado, aplicaremos una distribución de potencia inversa con exponente 10 (donde la potencia = 0,1) para generar desviaciones de la mejor solución. Esta distribución se caracteriza por el hecho de que la mayoría de las nuevas soluciones se generan muy cerca de la mejor solución, pero con una pequeña probabilidad de que se produzcan desviaciones significativas, lo cual proporciona un equilibrio entre la búsqueda (explotación) local y la exploración global del espacio de soluciones. Las nuevas coordenadas se limitan al rango permitido. El lugar desocupado del arrecife se repoblará con nuevos corales.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_CRO::Depredation ()
{
  // Применяем уничтожение с вероятностью Pd
  if (u.RNDfromCI (0, 1) < Pd)
  {
    // Находим все занятые позиции и их индексы
    int occupiedIndices [];
    int occupiedCount = 0;

    for (int i = 0; i < totalReefSize; i++)
    {
      if (occupied [i])
      {
        ArrayResize (occupiedIndices, occupiedCount + 1);
        occupiedIndices [occupiedCount] = i;
        occupiedCount++;
      }
    }

    // Если нет занятых позиций, выходим
    if (occupiedCount == 0) return;

    // Сортируем индексы по приспособленности (от лучших к худшим)
    SortAgentsByFitness (occupiedIndices, occupiedCount);

    // Определяем количество лучших решений, используемых для генерации новых
    int eliteCount = (int)MathRound (0.1 * occupiedCount); // Используем 10% лучших
    if (eliteCount <= 0) eliteCount = 1; // Минимум одно элитное решение
    if (eliteCount > occupiedCount) eliteCount = occupiedCount;

    // Определяем количество худших решений для замены
    int removeCount = (int)MathRound (Fd * occupiedCount);
    if (removeCount <= 0) removeCount = 1; // Минимум одно решение заменяем
    if (removeCount > occupiedCount - eliteCount) removeCount = occupiedCount - eliteCount; // Не удаляем элитные решения

    // Удаляем худшие решения и заменяем их новыми в окрестности лучших
    for (int i = 0; i < removeCount; i++)
    {
      // Индекс удаляемого решения (с конца отсортированного массива)
      int removeIndex = occupiedCount - 1 - i;
      if (removeIndex < 0 || removeIndex >= occupiedCount) continue;

      int posToRemove = occupiedIndices [removeIndex];
      if (posToRemove < 0 || posToRemove >= totalReefSize) continue;

      // Выбираем одно из элитных решений
      double power = 0.1; // Параметр степенного распределения
      double r = u.RNDfromCI (0, 1);
      int eliteIdx = (int)(eliteCount);
      if (eliteIdx >= eliteCount) eliteIdx = eliteCount - 1;

      int posBest = occupiedIndices [eliteIdx];
      if (posBest < 0 || posBest >= totalReefSize) continue;

      int bestAgentIdx = reefIndices [posBest];
      if (bestAgentIdx < 0 || bestAgentIdx >= popSize) continue;

      // Освобождаем позицию для нового решения
      occupied [posToRemove] = false;

      // Генерируем новое решение в окрестности выбранного элитного решения
      int newAgentIdx = reefIndices [posToRemove]; // Используем тот же индекс агента

      if (newAgentIdx >= 0 && newAgentIdx < popSize)
      {
        // Генерация через степенное распределение вокруг лучшего решения
        for (int c = 0; c < coords; c++)
        {
          // Определяем радиус поиска (можно адаптировать в зависимости от прогресса)
          double radius = 0.7 * (rangeMax [c] - rangeMin [c]); // 10% от диапазона

          // Генерируем значение по степенному закону
          double sign = u.RNDfromCI (0, 1) < 0.5 ? -1.0 : 1.0; // Случайный знак
          double deviation = sign * radius * MathPow (u.RNDfromCI (0, 1), 1.0 / power);

          // Новое значение в окрестности лучшего
          double newValue = a [bestAgentIdx].c [c] + deviation;

          // Ограничиваем значение в допустимом диапазоне
          a [newAgentIdx].c [c] = u.SeInDiSp (newValue, rangeMin [c], rangeMax [c], rangeStep [c]);
        }

        // Заселяем новое решение в риф
        occupied [posToRemove] = true;
        reefIndices [posToRemove] = newAgentIdx;
      }
    }
  }
}

//——————————————————————————————————————————————————————————————————————————————

Ahora, tras los cambios realizados, probaremos el algoritmo CROm. Como podemos ver en los resultados de más abajo, el algoritmo ha mejorado mucho el rendimiento.

CROm|Optimización de Arrecifes de Coral M|50,0|20,0|20,0|0,2|0,99|0,01|0,8|0,9|20,0|
=============================
5 Hilly's; Func runs: 10000; result: 0.7851210159578113
25 Hilly's; Func runs: 10000; result: 0.4603296933002806
500 Hilly's; Func runs: 10000; result: 0.25958379129490083
=============================
5 Forest's; Func runs: 10000; result: 0.8668751980437325
25 Forest's; Func runs: 10000; result: 0.3529695710837671
500 Forest's; Func runs: 10000; result: 0.16267582083006701
=============================
5 Megacity's; Func runs: 10000; result: 0.6323076923076923
25 Megacity's; Func runs: 10000; result: 0.2673846153846154
500 Megacity's; Func runs: 10000; result: 0.10733846153846247
=============================
All score: 3.89459 (43.27%)

La visualización del algoritmo no destaca especialmente: para el algoritmo resulta un poco más difícil hacer frente a problemas discretos en la función de prueba "Megacity".

Hilly

CROm en la función de prueba Hilly

Forest

CROm en la función de prueba Forest

Megacity

CROm en la función de prueba Megacity

Según los resultados de la prueba, el algoritmo CROm ocupa el puesto 42 en la tabla de clasificación de algoritmos de optimización basados en la población.

AO Description Hilly Hilly
Final
Forest Forest
Final
Megacity (discrete) Megacity
Final
Final
Resultado
% de
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)
1ANSacross neighbourhood search0,949480,847760,438572,235811,000000,923340,399882,323230,709230,634770,230911,574916,13468,15
2CLAcode lock algorithm (joo)0,953450,871070,375902,200420,989420,917090,316422,222940,796920,693850,193031,683806,10767,86
3AMOmanimal migration optimization M0,903580,843170,462842,209590,990010,924360,465982,380340,567690,591320,237731,396755,98766,52
4(P+O)ES(P+O) evolution strategies0,922560,881010,400212,203790,977500,874900,319452,171850,673850,629850,186341,490035,86665,17
5CTAcomet tail algorithm (joo)0,953460,863190,277702,094350,997940,857400,339492,194840,887690,564310,105121,557125,84664,96
6TETAtime evolution travel algorithm (joo)0,913620,823490,319902,057010,970960,895320,293242,159520,734620,685690,160211,580525,79764,41
7SDSmstochastic diffusion search M0,930660,854450,394762,179880,999830,892440,196192,088460,723330,611000,106701,441035,70963,44
8BOAmbilliards optimization algorithm M0,957570,825990,252352,035901,000000,900360,305022,205380,735380,525230,095631,356255,59862,19
9AAmarchery algorithm M0,917440,708760,421602,047800,925270,758020,353282,036570,673850,552000,237381,463235,54861,64
10ESGevolution of social groups (joo)0,999060,796540,350562,146161,000000,828630,131021,959650,823330,553000,047251,423585,52961,44
11SIAsimulated isotropic annealing (joo)0,957840,842640,414652,215130,982390,795860,205071,983320,686670,493000,090531,270205,46960,76
12ACSartificial cooperative search0,755470,747440,304071,806981,000000,888610,224132,112740,690770,481850,133221,305835,22658,06
13DAdialectical algorithm0,861830,700330,337241,899400,981630,727720,287181,996530,703080,452920,163671,319675,21657,95
14BHAmblack hole algorithm M0,752360,766750.345831,864930.935930.801520,271772,009230.650770.516460,154721,321955.19657.73
15ASOanarchy society optimization0,848720,746460,314651,909830,961480,791500,238031,991010,570770,540620,166141,277525,17857,54
16RFOroyal flush optimization (joo)0,833610,737420,346291,917330,894240,738240,240981,873460,631540,502920,164211,298675,08956,55
17AOSmbúsqueda de orbitales atómicos M0,802320,704490,310211,817020,856600,694510,219961,771070,746150,528620,143581,418355,00655,63
18TSEAturtle shell evolution algorithm (joo)0,967980,644800,296721,909490,994490,619810,227081,841390,690770,426460,135981,253225,00455,60
19DEdifferential evolution0,950440,616740,303081,870260,953170,788960,166521,908650,786670,360330,029531,176534,95555,06
20SRAsuccessful restaurateur algorithm (joo)0,968830,634550,292171,895550,946370,555060,191241,692670,749230,440310,125261,314804,90354,48
21CROchemical reaction optimisation0,946290,661120,298531,905930,879060,584220,211461,674730,758460,426460,126861,311784,89254,36
22BIOblood inheritance optimization (joo)0,815680,653360,308771,777810,899370,653190,217601,770160,678460,476310,139021,293784,84253,80
23BSAbird swarm algorithm0,893060,649000,262501,804550,924200,711210,249391,884790,693850,326150,100121,120124,80953,44
24HSharmony search0,865090,687820,325271,878180,999990,680020,095901,775920,620000,422670,054581,097254,75152,79
25SSGsaplings sowing and growing0,778390,649250,395431,823080,859730,624670,174291,658690,646670,441330,105981,193984,67651,95
26BCOmbacterial chemotaxis optimization M0,759530,622680,314831,697040,893780,613390,225421,732590,653850,420920,144351,219124,64951,65
27ABOafrican buffalo optimization0,833370,622470,299641,755480,921700,586180,197231,705110,610000,431540,132251,173784,63451,49
28(PO)ES(PO) evolution strategies0,790250,626470,429351,846060,876160,609430,195911,681510,590000,379330,113221,082554,61051,22
29TSmtabu search M0,877950,614310,291041,783300,928850,518440,190541,637830,610770,382150,121571,114494,53650,40
30BSObrain storm optimization0,937360,576160,296881,810410,931310,558660,235371,725340,552310,290770,119140,962224,49849,98
31WOAmwale optimization algorithm M0,845210,562980,262631,670810,931000,522780,163651,617430,663080,411380,113571,188034,47649,74
32AEFAartificial electric field algorithm0,877000,617530,252351,746880,927290,726980,180641,834900,666150,116310,095080,877544,45949,55
33AEOartificial ecosystem-based optimization algorithm0,913800,467130,264701,645630,902230,437050,214001,553270,661540,308000,285631,255174,45449,49
34ACOmant colony optimization M0,881900,661270,303771,846930,858730,586800,150511,596040,596670,373330,024720,994724,43849,31
35BFO-GAbacterial foraging optimization  - ga0,891500,551110,315291,757900,969820,396120,063051,428990,726670,275000,035251,036924,22446,93
36SOAsimple optimization algorithm0.915200.469760.270891,655850.896750.374010.169841,440600.695380.280310.108521,084224.18146,45
37ABHartificial bee hive algorithm0,841310,542270,263041,646630,878580,477790,171811,528180,509230,338770,103970,951974.12745,85
38ACMOatmospheric cloud model optimization0,903210,485460,304031,692700,802680,378570,191781,373030,623080,244000,107950,975034,04144,90
39ADAMmadaptive moment estimation M0.886350.447660,266131.600140.844970.384930.168891,398800,661540.270460.105941,037944.03744.85
40CGOchaos game optimization0,572560,371580,320181,264320,611760,619310,621611,852670,375380,219230,190280,784903,90243,35
41ATAmartificial tribe algorithm M0.717710.553040,252351,523100.824910.559040,204731,588670,440000,186150.094110.720263.83242.58
42CROmcoral reefs optimization M0,785120,460320,259581,505020,866880,352970,162671,382520,632310,267380,107341,007033,89543,27
43CFOcentral force optimization0,609610,549580,278311,437500,634180,468330,225411,327920,572310,234770,095860,902943,66840,76
44ASHAartificial showering algorithm0,896860,404330,256171,557370,803600,355260,191601,350460,476920,181230,097740,755893,66440,71
45ASBOadaptive social behavior optimization0,763310,492530,326191,582020,795460,400350,260971,456770,264620,171690,182000,618313.65740,63
R.W.neuroboids optimization algorithm 2(joo)0.487540.321590.257811,066940.375540,219440,158770,753750.279690,149170.098470.527342.34826.09


Conclusiones

Los arrecifes de coral vivos son un sistema sutil en el que se mantiene constantemente un equilibrio entre estabilidad y variabilidad, entre el mantenimiento de estrategias de supervivencia probadas y la experimentación de otras nuevas. El mecanismo de extinción modificado con generación de nuevas soluciones en las proximidades de la mejor imita el proceso natural de éxito ecológico en los arrecifes de coral.

En la naturaleza, tras la muerte de algunas colonias, en las zonas desalojadas del arrecife no aparecen especies al azar, sino aquellas que se han adaptado con mayor éxito a las condiciones ambientales locales. Además, la tasa de supervivencia de las nuevas colonias resulta mayor en las zonas que rodean a las colonias que existen "con éxito", lo cual se refleja directamente en el algoritmo a través de la generación de nuevas soluciones alrededor de las soluciones "de élite". Esto garantiza que se exploren continuamente las áreas prometedoras del espacio de búsqueda y mantiene constante el tamaño de la población.

La aplicación de una distribución de potencia inversa (con exponente 10, donde la potencia = 0,1) permite generar desviaciones de la mejor solución. Esta distribución concentra la mayoría de las nuevas soluciones cerca de la mejor, con escasas desviaciones significativas, lo cual proporciona un equilibrio óptimo entre explotación local y exploración global.

Ampliar el radio de búsqueda al 70% del rango permitido permite al algoritmo explorar regiones más amplias del espacio de soluciones.

El uso exclusivo de las mejores soluciones como base para generar nuevas soluciones acelera la convergencia hacia regiones óptimas y mejora la calidad de las soluciones resultantes, mientras que una amplia variedad de operadores modelan aspectos clave de la evolución de los corales: la reproducción externa e interna, el asentamiento de larvas y la reproducción asexual pueden utilizarse en el desarrollo de otros métodos de optimización basados en poblaciones.

Tab

Figura 2. Clasificación por colores de los algoritmos según las pruebas respectivas

chart

Figura 3. Histograma de los resultados de las pruebas de algoritmos (en una escala de 0 a 100, cuanto más mejor, donde 100 es el máximo resultado teórico posible, el script para calcular la tabla de puntuación está en el archivo)

Ventajas e inconvenientes del algoritmo CROm:

Ventajas:

  1. Es una idea interesante.
  2. Desarrollo prometedor.
  3. Resultados estables.

Desventajas:

  1. Resultados más débiles en funciones discretas.
  2. Gran número de parámetros externos.

Adjuntamos al artículo un archivo con las versiones actuales de los códigos de los algoritmos. El autor de este artículo no se responsabiliza de la exactitud absoluta de la descripción de los algoritmos canónicos, muchos de ellos han sido modificados para mejorar las capacidades de búsqueda. Las conclusiones y juicios presentados en los artículos se basan en los resultados de los experimentos realizados.


Programas usados en el artículo

#NombreTipoDescripción
1#C_AO.mqh
Archivo de inclusión
Clase padre de algoritmos de optimización basados en la población
2#C_AO_enum.mqh
Archivo de inclusión
Enumeración de los algoritmos de optimización basados en la población
3TestFunctions.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 en una tabla comparativa
7
Testing AOs.mq5
ScriptBanco de pruebas único para todos los algoritmos de optimización basados en la población
8
Simple use of population optimization algorithms.mq5
Script
Ejemplo sencillo de utilización de algoritmos de optimización basados en la población sin visualización
9
Test_AO_CROm.mq5
ScriptBanco de pruebas para CROm

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

Archivos adjuntos |
CROm.ZIP (273.9 KB)
Pruebas retrospectivas manuales simplificadas: herramientas personalizadas en MQL5 para el Probador de Estrategias Pruebas retrospectivas manuales simplificadas: herramientas personalizadas en MQL5 para el Probador de Estrategias
En este artículo diseñamos un conjunto de herramientas MQL5 personalizadas para facilitar las pruebas retrospectivas manuales en el Probador de Estrategias. Explicamos su diseño e implementación, centrándonos en los controles comerciales interactivos. A continuación mostramos cómo utilizarlo para probar estrategias de forma eficaz.
Trading con algoritmos: La IA y su camino hacia las alturas doradas Trading con algoritmos: La IA y su camino hacia las alturas doradas
En este artículo veremos un método para crear estrategias comerciales para el oro utilizando el aprendizaje automático. Considerando el enfoque propuesto para el análisis y la previsión de series temporales desde distintos ángulos, podemos determinar sus ventajas e inconvenientes en comparación con otras formas de crear sistemas comerciales basados únicamente en el análisis y la previsión de series temporales financieras.
Redes neuronales en el trading: Actor—Director—Crítico (Actor—Director—Critic) Redes neuronales en el trading: Actor—Director—Crítico (Actor—Director—Critic)
Hoy le presentamos el framework Actor-Director-Critic, que combina el aprendizaje jerárquico y la arquitectura multicomponente para crear estrategias comerciales adaptativas. En este artículo, detallaremos cómo el uso del Director para clasificar las acciones del Actor ayuda a optimizar eficazmente las decisiones comerciales y a aumentar la solidez de los modelos en el entorno de los mercados financieros.
Automatización de estrategias de trading en MQL5 (Parte 14): Estrategia Trade Layering con técnicas estadísticas basadas en MACD y RSI Automatización de estrategias de trading en MQL5 (Parte 14): Estrategia Trade Layering con técnicas estadísticas basadas en MACD y RSI
En este artículo se presenta una estrategia de trade layering que combina los indicadores MACD y RSI con métodos estadísticos para automatizar un trading dinámico en MQL5. Se analiza la arquitectura de este enfoque en cascada, se detalla su implementación mediante segmentos clave de código y se orienta al lector sobre cómo realizar pruebas retrospectivas para optimizar el rendimiento. Finalmente, concluimos destacando el potencial de la estrategia y preparando el escenario para futuras mejoras en el trading automatizado.