Русский Español
preview
Otimização de recifes de coral — Coral Reefs Optimization (CRO)

Otimização de recifes de coral — Coral Reefs Optimization (CRO)

MetaTrader 5Negociação |
122 0
Andrey Dik
Andrey Dik


Conteúdo

  1. Introdução
  2. Implementação do algoritmo
  3. Resultados dos testes


Introdução

Algoritmos bioinspirados, que se inspiram em processos e sistemas naturais, têm despertado grande interesse entre desenvolvedores recentemente. Entre eles, destaca-se o Algoritmo de Otimização de Recifes de Coral (Coral Reef Optimization, CRO), proposto originalmente por S. Salcedo-Sanz e colaboradores em 2014.

O algoritmo CRO é baseado na modelagem dos processos de formação e desenvolvimento de recifes de coral na natureza. Esses processos incluem diversos mecanismos de reprodução dos corais (reprodução sexual externa e interna, além da assexuada), competição por espaço limitado no recife e eliminação de indivíduos fracos. Assim como a evolução forma recifes de coral estáveis e adaptados na natureza, o algoritmo CRO permite explorar o espaço de busca e encontrar soluções ótimas ou próximas do ótimo para diferentes tarefas.

Neste trabalho será apresentada uma versão aprimorada do algoritmo CROm com um mecanismo modificado de eliminação, baseado no uso de uma distribuição de potência inversa para gerar novas soluções nas vizinhanças das melhores. A abordagem proposta não apenas preserva as vantagens tradicionais do CRO, como capacidade exploratória, equilíbrio natural entre diversificação e intensificação do espaço de busca, mas também adiciona um mecanismo mais eficiente, que permite localizar com maior precisão áreas promissoras e convergir mais rapidamente para soluções ótimas.

Será apresentado um teste abrangente do algoritmo proposto em um conjunto de funções clássicas de teste de otimização, demonstrando sua melhor eficiência em comparação ao algoritmo CRO original e a outras meta-heurísticas modernas. Os resultados dos experimentos mostram que a abordagem proposta é especialmente eficaz para tarefas com funções objetivo multimodais e estrutura complexa do espaço de busca.

O artigo está estruturado da seguinte forma: primeiro analisamos os conceitos básicos e os princípios de funcionamento do algoritmo CRO padrão, depois descrevemos detalhadamente as modificações propostas e sua fundamentação teórica. Em seguida, vem a avaliação experimental do algoritmo e a análise dos resultados. Nas considerações finais discutimos os resultados obtidos, as limitações do método proposto e possíveis direções para pesquisas futuras.


Implementação do algoritmo

A ideia principal do CRO consiste em modelar colônias de corais que se desenvolvem e competem por espaço no recife, formando no final uma estrutura otimizada. Cada coral no recife representa uma solução potencial para o problema de otimização analisado.

O recife é modelado como uma grade bidimensional de tamanho N×M. Cada célula da grade pode estar ocupada por um coral ou permanecer vazia. O coral representa uma solução codificada do problema de otimização. Para cada coral é definida uma função de adaptabilidade (saúde), que corresponde à função objetivo da tarefa de otimização.

O parâmetro ρ₀ ∈ (0,1) determina a fração inicial de preenchimento do recife por corais, isto é, a relação entre o número de células ocupadas e o número total de células no início do algoritmo. A inicialização do recife é realizada da seguinte forma:

  1. Define-se o tamanho do recife N×M e a fração inicial de preenchimento ρ₀.
  2. Selecionam-se aleatoriamente ⌊ρ₀ × N × M⌋ células do recife para posicionamento dos corais iniciais.
  3. Os corais iniciais são gerados aleatoriamente dentro da área de busca e colocados nas células selecionadas.

Após a inicialização do recife, inicia-se o processo iterativo de formação e desenvolvimento do recife, composto por várias etapas:

Reprodução sexual externa (Broadcast Spawning). Para esse tipo de reprodução é escolhida uma determinada fração Fₑ dos corais existentes. Os corais selecionados formam pares e, usando operadores de cruzamento, geram descendentes. Cada par produz uma larva utilizando um operador de cruzamento (normalmente uma média).

Reprodução sexual interna (Brooding). A fração restante dos corais (1-Fₑ) participa do processo de reprodução interna, em que cada coral produz descendentes por mutação. Para cada coral selecionado para reprodução interna, a larva é criada com um operador de mutação e geralmente representa uma pequena alteração aleatória da solução codificada. Fixação das larvas. Após a formação das larvas nas etapas de reprodução, cada uma tenta ocupar um espaço no recife. O processo de fixação é realizado segundo as seguintes regras:

  1. A larva escolhe aleatoriamente uma célula (i, j) no recife.
  2. Se a célula estiver livre, a larva a ocupa.
  3. Se a célula estiver ocupada, a larva pode expulsar o coral existente apenas se sua adaptabilidade for maior: f(larva) > f(Ξᵢⱼ).
  4. Se a expulsão não ocorrer, a larva pode tentar fixar-se em outro lugar (até o número máximo de tentativas k).
  5. Se após k tentativas a larva não conseguir encontrar um lugar, ela morre.

Reprodução assexuada (Budding). Os melhores corais do recife (fração Fₐ) podem reproduzir-se de forma assexuada, criando cópias exatas de si mesmos (clones). Formalmente:

  1. Os corais são ordenados pelo valor da função de adaptabilidade.
  2. Os melhores Fₐ × 100% dos corais são selecionados para a reprodução assexuada.
  3. Cada coral selecionado cria um clone, que tenta fixar-se no recife seguindo as mesmas regras utilizadas no processo de fixação das larvas.

Extinção. Ao final de cada iteração, os piores corais do recife podem morrer com probabilidade Pd, liberando espaço para novos corais nas iterações seguintes.

O processo de formação do recife se repete até que o critério de parada seja cumprido, alcançando o número máximo de iterações. Após a parada, o melhor coral do recife representa a solução encontrada para o problema de otimização.

cro-algorithm

Figura 1. Esquema do funcionamento do algoritmo CRO

Na ilustração acima estão representadas as seis etapas principais do funcionamento do algoritmo:

  1. Inicialização (ρ₀ = 0.6) mostra a grade bidimensional (recife) parcialmente preenchida por corais coloridos, representando diferentes soluções.
  2. Reprodução externa (Fb = 0.9) mostra corais formando pares e criando larvas por meio de cruzamento.
  3. Reprodução interna (1-Fb = 0.1) mostra corais individuais produzindo larvas por mutação.
  4. Fixação das larvas (k = 3 tentativas) ilustra o processo de busca das larvas por um lugar no recife, incluindo tentativas bem-sucedidas e malsucedidas.
  5. Reprodução assexuada (Fa = 0.1) mostra os melhores corais (marcados com estrelas) criando cópias exatas de si mesmos.
  6. Extinção (Fd = 0.1, Pd = 0.05) mostra a remoção dos piores corais do recife.
Agora podemos passar para a escrita do pseudocódigo do algoritmo.

Entrada: Parâmetros do recife (N, M, ρ₀, Fₑ, Fₐ, Fd, Pd, k)
Saída: Melhor solução encontrada

1. Inicialização:
   - Criar um recife de tamanho N×M
   - Preencher ⌊ρ₀ × N × M⌋ células com corais aleatórios
   - Calcular a adaptabilidade de cada coral

2. Enquanto o critério de parada não for atendido:
   3. Reprodução sexual externa:
      - Selecionar a fração Fₑ dos corais para participar
      - Formar pares e criar larvas por cruzamento
   
   4. Reprodução sexual interna:
      - Para os corais restantes (1-Fₑ), criar larvas por mutação
   
   5. Fixação das larvas:
      - Para cada larva:
        - Tentar ocupar uma célula aleatória do recife
        - Se estiver ocupada, expulsar o coral existente se tiver adaptabilidade maior
        - Se não conseguir, tentar novamente (até k tentativas)
   
   6. Reprodução assexuada:
      - Ordenar os corais por adaptabilidade
      - Os melhores Fₐ corais criam clones
      - Os clones tentam fixar-se no recife
   
   7. Extinção:
      - Com probabilidade Pd executar a eliminação
      - Remover a fração Fd dos piores corais

8. Retornar o melhor coral no recife

Agora podemos montar o código do algoritmo CRO.  Vamos escrever a classe "C_AO_CRO", que implementa o algoritmo CRO e herda da classe base "C_AO". 

Parâmetros do CRO:

Na classe são declarados parâmetros que controlam o comportamento do algoritmo:

  • popSize: tamanho da população de corais.
  • reefRows: altura do recife (quantidade de linhas na grade).
  • reefCols: largura do recife (quantidade de colunas na grade).
  • rho0: ocupação inicial do recife. É a porcentagem de células do recife ocupadas por corais no início do algoritmo.
  • Fb: fração de corais que participam da reprodução externa (Broadcast Spawning).
  • Fa: fração de corais que participam da reprodução assexuada (fragmentation).
  • Fd: fração de corais a serem removidos devido à baixa adaptabilidade.
  • Pd: probabilidade de eliminação de corais.
  • attemptsNum: quantidade de tentativas que a larva faz para fixar-se no recife.

Métodos da classe:

  • SetParams (): o método define os valores dos parâmetros do algoritmo, com base nos valores armazenados no array "params". Esse array permite alterar dinamicamente os parâmetros do algoritmo durante a execução.
  • Init (): método de inicialização, recebe os intervalos de busca das variáveis de otimização (rangeMinP, rangeMaxP, rangeStepP) e a quantidade de épocas (epochsP). O método "Init" executa os preparativos necessários para o início do trabalho do algoritmo, como a inicialização da população e do recife.
  • Moving (): função que implementa a lógica principal de movimentação dos corais no recife. 
  • Revision (): método responsável pela revisão e correção das posições dos corais no recife.
  • InitReef (): inicializa a estrutura do recife, preparando-o para ser povoado por corais.
  • LarvaSettling (): determina onde a larva do coral vai se fixar no recife, modela o processo de povoamento do recife por novos corais. O parâmetro "larva" representa a larva do coral.
  • BroadcastSpawning (): processo de reprodução externa, quando os corais liberam larvas na água.
  • Brooding (): processo alternativo de reprodução, quando as larvas se desenvolvem dentro do coral.
  • AsexualReproduction (): processo de reprodução assexuada, quando os corais se fragmentam e formam novas colônias.
  • Depredation (): processo de extinção, quando os corais são eliminados.
  • GetReefCoordIndex (): converte as coordenadas do recife (linha e coluna) em um índice em um array unidimensional.
  • SortAgentsByFitness (): ordena os corais de acordo com sua adaptabilidade (fitness).

Variáveis privadas:

  • totalReefSize: tamanho total do recife (o produto de "reefRows" e "reefCols").
  • occupied []: array de valores booleanos indicando se cada célula do recife está ocupada por um coral.
  • reefIndices []: array de índices contendo os índices dos corais (do array "a []" da classe base "C_AO") nas células ocupadas do recife.
//——————————————————————————————————————————————————————————————————————————————
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/ru/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);
};
//——————————————————————————————————————————————————————————————————————————————

O método "Init" inicializa o algoritmo CRO: chama "StandardInit" da classe base, depois calcula o tamanho total do recife "totalReefSize", determina a quantidade inicial de corais "initialPopSize", inicializa os arrays "occupied" (ocupação das células) e "reefIndices" (índices dos corais), chama "InitReef ()" para posicionar os corais no recife e, por fim, retorna "true" em caso de sucesso.

//——————————————————————————————————————————————————————————————————————————————
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;
}
//——————————————————————————————————————————————————————————————————————————————

O método "InitReef ()" da classe "C_AO_CRO" inicializa a população inicial de corais no recife. Primeiro é calculada a quantidade de corais para a inicialização, com base na densidade definida (rho0) do recife; essa quantidade é limitada pelo tamanho da população "popSize". Em seguida, para cada coral é selecionada uma posição aleatória, porém livre, no recife; após encontrá-la, ela é marcada como ocupada e o coral é associado a essa posição no array "reefIndices". Depois são geradas as coordenadas para cada coral. Cada coordenada é escolhida aleatoriamente dentro dos limites (rangeMin, rangeMax) e discretizada usando a função "u.SeInDiSp", que leva em conta o passo de discretização (rangeStep). Isso garante uma distribuição inicial uniforme e controlada das soluções (corais) no espaço de busca.

//——————————————————————————————————————————————————————————————————————————————
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]);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

O método "Moving ()" realiza a avaliação inicial da população de corais. Se "revision" tiver valor "false", o método executa uma iteração por todas as posições do recife. Para cada posição ocupada (determinada pelo valor no array "occupied"), o método obtém o índice do coral presente nessa posição a partir do array "reefIndices". Se o índice "idx" for válido, será realizada a computação da adaptabilidade desse coral.

É importante observar que o método "Moving ()" não calcula diretamente a adaptabilidade, mas apenas fornece as informações necessárias para sua computação. Após a conclusão da iteração por todas as posições do recife, o método define o valor de "revision" como "true" para impedir uma nova passagem pelo ciclo de avaliação dos corais nessa mesma iteração de otimização. Em essência, o método "Moving ()" atua como um gatilho para o cálculo da adaptabilidade dos corais, garantindo a execução única desse processo a cada iteração da otimização.

//——————————————————————————————————————————————————————————————————————————————
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;
  }
}
//——————————————————————————————————————————————————————————————————————————————

O método "Revision ()" atualiza a melhor solução global: procura corais no recife cuja adaptabilidade seja superior à da melhor solução global atual e a substitui. Cria um array, preparando espaço para as larvas obtidas durante a reprodução. Em seguida, o método inicia as reproduções sexuais: chama os métodos "BroadcastSpawning" e "Brooding" para criar larvas. No passo seguinte, o método realiza a fixação das larvas usando "LarvaSettling" para determinar os locais das novas larvas no recife. Depois executa a reprodução assexuada "AsexualReproduction", e no final ocorre o processo de eliminação. Em resumo: atualiza a solução, reproduz os corais, posiciona as larvas e modela o impacto da extinção.

//——————————————————————————————————————————————————————————————————————————————
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 ();
}
//——————————————————————————————————————————————————————————————————————————————

O método "LarvaSettling" modela o processo de ocupação do recife por uma larva. Ele tenta encontrar um local adequado e, ou a fixa em um espaço livre, ou substitui um coral existente com pior adaptabilidade. Durante a tentativa de fixação, a larva tenta ocupar o recife "attemptsNum" vezes. Em cada tentativa é escolhida uma posição aleatória no recife. Se a posição estiver livre (não ocupada por um coral), o método procura um índice livre no array de agentes "a []"; se um índice livre for encontrado, a larva copia sua solução (coordenadas "c []" e adaptabilidade "f") para a célula correspondente no array de agentes.

A informação de que a posição do recife agora está ocupada é atualizada, e o método retorna o índice da posição "pos" ocupada com sucesso. Se a posição escolhida estiver ocupada, o método compara a adaptabilidade (fitness) da larva com a do coral situado nessa posição. Se a larva for melhor, ela substitui o coral existente copiando seus parâmetros para a célula correspondente no array de agentes, e o método retorna o índice "pos" da posição onde ocorreu a substituição. Se após todas as tentativas a larva não conseguir se fixar (nem em espaço livre, nem substituindo algum coral existente), o método retorna -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;
}
//——————————————————————————————————————————————————————————————————————————————

O método "BroadcastSpawning" modela a reprodução externa dos corais por meio do processo de desova. Ele executa as seguintes etapas principais: identifica os índices de todos os locais ocupados por corais no recife, determina a quantidade de corais que participarão da desova com base no parâmetro "Fb" (Brooding Fraction) — a fração de corais que participam da reprodução externa —, embaralha os índices dos corais ocupados e então seleciona pares para o processo de desova. Criação de larvas por cruzamento: para cada par de corais é criada uma nova larva, cujas coordenadas são calculadas como o valor médio das coordenadas dos pais, com a adição de uma pequena mutação aleatória. Esse processo de cruzamento (crossover) busca unir as melhores características dos pais; as larvas produzidas são adicionadas ao array "larvae".

Pontos importantes:

  • "Fb" aqui define a fração de corais que participam da reprodução externa (desova), diferentemente de "Brooding", onde controla a fração de corais envolvidos na reprodução interna.
  • O cruzamento e a mutação aumentam a diversidade genética da população.
  • O objetivo do método é criar novas larvas obtidas pela combinação dos genes dos corais pais, para posterior colonização do recife. O método pressupõe cruzamento em pares.
//——————————————————————————————————————————————————————————————————————————————
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++;
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

O método "Brooding" imita o processo de desenvolvimento interno das larvas nos corais dentro do CRO e executa as seguintes etapas:

  • determina quais posições do recife estão ocupadas por corais e armazena os índices dessas posições. 
  • calcula quantos corais participarão do desenvolvimento interno das larvas usando o parâmetro "Fb" (Brooding Fraction). Para selecionar aleatoriamente os corais participantes, os índices das posições ocupadas são embaralhados. 
  • cria e muta larvas: para cada coral selecionado é criada uma larva. A larva é inicializada e mutada: suas coordenadas são ligeiramente alteradas de forma aleatória em relação às do coral pai. As larvas mutadas são adicionadas ao array "larvae".

Pontos importantes:

  • "Fb" define a fração de corais que NÃO participam da reprodução assexuada (ou seja, os que participam do desenvolvimento interno).
  • A mutação garante diversidade genética na população de larvas.
  • O objetivo é criar novos indivíduos potencialmente melhores, que irão competir por vagas no recife.
//——————————————————————————————————————————————————————————————————————————————
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++;
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

O método "AsexualReproduction" modela a reprodução assexuada dos corais por brotamento. O método realiza as seguintes etapas: identifica os índices de todas as posições do recife ocupadas por corais, ordena esses índices em ordem decrescente de adaptabilidade, ou seja, primeiro aparecem as posições com os corais mais adaptados. Com base no parâmetro "Fa" (Fraction for Asexual reproduction), que define a fração de corais participantes da reprodução assexuada, determina-se quantos dos melhores corais irão produzir clones. Criação e fixação dos clones: para cada coral selecionado é criada uma cópia exata (clone), isto é, uma larva com as mesmas coordenadas e adaptabilidade. O método "LarvaSettling" é chamado para tentar fixar o clone no recife.

Pontos importantes:

  • A reprodução assexuada permite que os corais mais adaptados espalhem rapidamente seus genes.
  • O parâmetro "Fa" controla a intensidade da reprodução assexuada.
  • O uso de "LarvaSettling" para a fixação dos clones significa que os clones devem competir por espaço no recife da mesma forma que as larvas obtidas por reprodução sexual. 
  • O método de clonagem, neste caso, é uma cópia exata do pai SEM mutações. As mutações ocorrem apenas na reprodução 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);
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

O método "Depredation" modela o processo de extinção dos corais devido à predação ou outros fatores negativos (por exemplo, poluição). Neste caso, a eliminação é aplicada com probabilidade "Pd". Se um número aleatório for menor que "Pd", então o processo de eliminação é realizado. A busca por posições ocupadas identifica os índices de todas as posições do recife que estão ocupadas por corais. Os índices dessas posições são ordenados pela adaptabilidade (fitness) dos corais.

O array ordenado de índices é invertido, de modo que no início do array fiquem os índices dos corais menos adaptados. É determinado o número de corais que serão removidos com base no parâmetro "Fd" (Fraction for Depredation). Esse valor é arredondado para um número inteiro. O número especificado de corais menos adaptados é então removido. Em seguida, ocorre a limpeza do recife: para cada posição marcada para remoção: a célula correspondente no array "occupied" é definida como "false", indicando que agora a posição está livre. A célula correspondente no array "reefIndices" é definida como -1, indicando a ausência de um coral nessa posição.

//——————————————————————————————————————————————————————————————————————————————
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;
        }
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

Método "GetReefCoordIndex" converte as coordenadas do recife (linha e coluna) em um índice unidimensional e recebe "row" e "col" como entradas, retornando o índice correspondente no array que representa o recife. Primeiro, o método verifica se as coordenadas fornecidas não ultrapassam os limites do recife. Caso estejam dentro dos limites, o índice é calculado usando a fórmula (row * reefCols + col), onde "reefCols" é o número de colunas no recife. Esse método é usado para acessar os elementos nos arrays que representam o recife.

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

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

Método "SortAgentsByFitness" ordena o array de índices "indices" contendo "count" elementos correspondentes a corais, em ordem decrescente de adaptabilidade (fitness), usando o algoritmo de ordenação por bolha, A ordenação é realizada com base nos valores de adaptabilidade armazenados no array "a", acessado por meio do array "reefIndices". Dessa forma, após a execução do método, "indices" contém os índices dos corais organizados do mais adaptado ao menos adaptado. Verificações adicionais são incluídas para evitar acessos fora dos limites dos arrays.

//——————————————————————————————————————————————————————————————————————————————
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;
          }
        }
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

Concluímos a preparação do algoritmo, descrevemos no código todos os seus numerosos métodos de funcionamento, e agora podemos passar diretamente para os testes.


Resultados dos testes

Após os testes realizados, pode-se observar que o algoritmo CRO apresenta desempenho bastante fraco e é necessário revisar alguns métodos do procedimento 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%)

Antes de tudo, o que me chamou atenção no algoritmo CRO foi o método de eliminação. Ele precisa ser modificado para melhorar a busca; iremos substituir os piores corais do recife por novos, que serão gerados na vizinhança dos melhores corais (soluções), promovendo assim a busca em direção a regiões mais promissoras. Definimos a quantidade de melhores corais "eliteCount" e a quantidade de piores corais "removeCount". 

Substituição das piores soluções: para cada "vítima" é escolhido um coral "elite", e é gerado um novo coral na vizinhança desse coral "elite". No mecanismo aprimorado de eliminação aplicamos uma distribuição de potência inversa com expoente 10 (onde power = 0.1) para gerar desvios em relação ao melhor coral. Essa distribuição é caracterizada por gerar a maioria das novas soluções muito próximas do melhor coral, mas com pequena probabilidade de produzir desvios significativos, o que assegura o equilíbrio entre a busca local (intensificação) e a exploração global do espaço de soluções. As novas coordenadas são limitadas ao intervalo permitido. A posição liberada no recife é então ocupada pelo novo coral.

//——————————————————————————————————————————————————————————————————————————————
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;
      }
    }
  }
}

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

Agora, após as modificações realizadas, testamos o algoritmo CROm. Como pode ser observado nos resultados abaixo, o algoritmo melhorou significativamente seu desempenho.

CROm|Coral Reef Optimization 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%)

A visualização do algoritmo não é especial, mas é um pouco mais difícil para o algoritmo lidar com problemas discretos na função de teste "Megacity".

Hilly

CROm na função de teste Hilly

Forest

CROm na função de teste Forest

Megacity

CROm na função de teste Megacity

De acordo com os resultados do teste, o algoritmo CROm está na 42ª posição da tabela de classificação dos algoritmos de otimização baseados em população.

AO Description Hilly Hilly
Final
Forest Forest
Final
Megacity (discrete) Megacity
Final
Final
Result
% of
MAX
10 p (5 F)50 p (25 F)1000 p (500 F)10 p (5 F)50 p (25 F)1000 p (500 F)10 p (5 F)50 p (25 F)1000 p (500 F)
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 ptimization 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
17AOSmatomic orbital search 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
37ABHAartificial 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
RWneuroboids optimization algorithm 2(joo)0,487540,321590,257811,066940,375540,219440,158770,753750,279690,149170,098470,527342,34826,09


Considerações finais

Os recifes de coral vivos representam um sistema delicadamente equilibrado, onde é continuamente mantido um balanço entre estabilidade e variabilidade, entre preservar estratégias de sobrevivência comprovadas e experimentar novas. O mecanismo modificado de extinção, com a geração de novas soluções na vizinhança das melhores, imita o processo natural de sucesso ecológico nos recifes de coral.

Na natureza, após a morte de algumas colônias, as áreas liberadas do recife não são ocupadas por espécies aleatórias, mas por aquelas que apresentam melhor adaptação às condições locais. Além disso, a sobrevivência de novas colônias é maior nas zonas próximas às colônias bem-sucedidas já existentes, o que é diretamente refletido no algoritmo por meio da geração de novas soluções ao redor das elites. Isso garante a exploração contínua de regiões promissoras do espaço de busca e mantém inalterado o tamanho da população.

A implementação da distribuição de potência inversa (com expoente 10, onde power = 0.1) permite gerar desvios em relação ao melhor coral. Esse tipo de distribuição concentra a maioria das novas soluções próximas do melhor coral, com raros desvios significativos, garantindo um equilíbrio ideal entre intensificação local e diversificação global.

A ampliação do raio de busca para 70% do intervalo permitido possibilita ao algoritmo explorar áreas mais amplas do espaço de soluções.

O uso apenas das melhores soluções (a elite) como base para gerar novos candidatos acelera a convergência em direção às regiões ótimas e melhora a qualidade das soluções obtidas, enquanto os diversos operadores que modelam aspectos-chave da evolução dos corais (reprodução externa e interna, fixação de larvas e reprodução assexuada) podem ser aplicados no desenvolvimento de outros métodos populacionais de otimização.

Tab

Figura 2. Classificação colorida dos algoritmos de acordo com os testes correspondentes

chart

Figura 3. Histograma dos resultados de testes dos algoritmos (em escala de 0 a 100, quanto maior melhor, onde 100 é o resultado teórico máximo possível, no arquivo compactado há um script para calcular a tabela de classificação)

Prós e contras do algoritmo CROm:

Prós:

  1. Ideia interessante.
  2. Desenvolvimento promissor.
  3. Resultados estáveis.

Contras:

  1. Resultados mais fracos em funções discretas.
  2. Grande quantidade de parâmetros externos.

Um arquivo com as versões atuais dos códigos dos algoritmos está anexado ao artigo. O autor do artigo não se responsabiliza pela precisão absoluta na descrição dos algoritmos canônicos, já que muitos deles foram modificados para melhorar suas capacidades de busca. As conclusões e julgamentos apresentados no artigo baseiam-se nos resultados dos experimentos realizados.


Programas utilizados no artigo

#NomeTipoDescrição
1#C_AO.mqh
Arquivo incluído
Classe pai dos algoritmos populacionais de otimização
2#C_AO_enum.mqh
Arquivo incluído
Enumeração dos algoritmos populacionais de otimização
3TestFunctions.mqh
Arquivo incluído
Biblioteca de funções de teste
4
TestStandFunctions.mqh
Arquivo incluído
Biblioteca de funções do test stand
5
Utilities.mqh
Arquivo incluído
Biblioteca de funções auxiliares
6
CalculationTestResults.mqh
Arquivo incluído
Script para calcular resultados na tabela comparativa
7
Testing AOs.mq5
ScriptTest stand unificado para todos os algoritmos populacionais de otimização
8
Simple use of population optimization algorithms.mq5
Script
Exemplo simples de uso de algoritmos populacionais de otimização sem visualização
9
Test_AO_CROm.mq5
ScriptTest stand para CROm

Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/17760

Arquivos anexados |
CROm.ZIP (273.9 KB)
Redes neurais em trading: Ator–Diretor–Crítico (Actor–Director–Critic) Redes neurais em trading: Ator–Diretor–Crítico (Actor–Director–Critic)
Propomos conhecer o framework Actor-Director-Critic, que combina aprendizado hierárquico e uma arquitetura com múltiplos componentes para criar estratégias de trading adaptativas. Neste artigo, analisamos em detalhe como o uso do Diretor para classificar as ações do Ator ajuda a otimizar decisões de trading de forma eficiente e a aumentar a robustez dos modelos nas condições dos mercados financeiros.
Trading por algoritmo: IA e seu caminho para os topos dourados Trading por algoritmo: IA e seu caminho para os topos dourados
Neste artigo, é demonstrado um método de criação de estratégias de trading para o ouro usando aprendizado de máquina. Ao analisar o método proposto para a previsão de séries temporais sob diferentes ângulos, é possível identificar suas vantagens e desvantagens em comparação com outras formas de criação de sistemas de trading baseadas somente na análise e previsão de séries temporais financeiras.
Construa Expert Advisors Auto-Otimizáveis em MQL5 (Parte 2): Estratégia de Scalping USDJPY Construa Expert Advisors Auto-Otimizáveis em MQL5 (Parte 2): Estratégia de Scalping USDJPY
Junte-se a nós hoje enquanto nos desafiamos a construir uma estratégia de trading para o par USDJPY. Vamos negociar padrões de candles que são formados no gráfico diário, pois eles potencialmente têm mais força por trás deles. Nossa estratégia inicial foi lucrativa, o que nos encorajou a continuar refinando a estratégia e adicionando camadas extras de segurança, para proteger o capital obtido.
Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 5): Volatility Navigator EA Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 5): Volatility Navigator EA
Determinar a direção do mercado pode ser simples, mas saber quando entrar pode ser desafiador. Como parte da série intitulada "Desenvolvimento do Kit de Ferramentas de Análise de Price Action", tenho o prazer de apresentar mais uma ferramenta que fornece pontos de entrada, níveis de take profit e definições de stop loss. Para isso, utilizamos a linguagem de programação MQL5. Vamos nos aprofundar em cada etapa neste artigo.