English Русский
preview
Algoritmo dos Macacos-Azuis — Blue Monkey (BM) Algorithm

Algoritmo dos Macacos-Azuis — Blue Monkey (BM) Algorithm

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

Conteúdo

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


Introdução

Na busca pelo algoritmo de otimização mais adequado para problemas discretos envolvendo robôs de negociação, será analisado mais um método e suas possibilidades. No coração das selvas africanas vivem criaturas cuja existência, marcada por interações complexas e uma hierarquia bem definida, serviu de inspiração para a criação de um dos métodos de otimização. Trata-se dos macacos-azuis, ou Cercopithecus mitis, pequenos e ágeis habitantes das copas das árvores cujo comportamento, aparentemente caótico, na realidade obedece a profundos princípios biológicos. Foi esse comportamento que deu origem à ideia de desenvolver um algoritmo batizado em sua homenagem: Blue Monkey.

O algoritmo foi desenvolvido por M. Mahmood e B. Al-Khateeb e publicado em 2019 na revista Periodicals of Engineering and Natural Sciences (PEN), 7(3):1054-1066, DOI:10.21533/pen.v7i3.621.

Esse ecossistema complexo, porém harmonioso, inspirou a abstração matemática do algoritmo Blue Monkey, estruturada da seguinte forma:

  • Grupos como equipes. A população de agentes é dividida em equipes independentes, cada uma com seu próprio líder, à semelhança de um macho dominante.
  • Líder como referência de excelência. O melhor indivíduo de cada grupo, com o maior valor de "aptidão" (fitness), representa o macho dominante e serve de referência para os demais.
  • Filhotes como nova geração. Os indivíduos jovens, os "filhotes", representam uma nova geração em desenvolvimento, que aprende e está pronta para substituir os mais velhos, trazendo novas ideias e soluções.
  • Migração como renovação do sistema. A migração ocorre quando os melhores "filhotes" substituem os "adultos" menos aptos, promovendo a renovação contínua da população e aumentando a eficiência do algoritmo.
  • Aprendizado social como caminho até o líder. O deslocamento dos "filhotes" em direção ao líder do grupo, determinado por equações matemáticas, simula o aprendizado social, em que os mais jovens aprendem com os indivíduos experientes.


Implementação do algoritmo

Imagine que você esteja coordenando uma expedição de pesquisa na selva em busca de uma cidade perdida. Você conta com 50 exploradores experientes (macacos adultos) e 15 estudantes estagiários (filhotes, correspondentes a 30% do número de adultos).

Estrutura da expedição:

  • os exploradores experientes são divididos em 5 equipes de 10 pessoas;
  • cada equipe explora de forma independente uma determinada região da selva;
  • 5 equipes = 5 zonas de busca diferentes: a equipe A segue para o norte, a equipe B vai para o sul, a equipe C faz buscas a leste, a equipe D explora a oeste e a equipe E permanece na região central. 

Se a cidade perdida estiver ao norte, a equipe A a encontrará mais rapidamente, enquanto as demais irão se aproximando gradualmente dessa região. Os estagiários trabalham todos juntos como um único grupo de treinamento. A ideia central é: 5 equipes = 5 zonas de busca diferentes. "Siga quem obtém os melhores resultados". Em cada equipe, é definido um líder, ou seja, aquele que encontrou a direção mais promissora. Os demais ajustam sua rota em direção ao líder, mas não seguem diretamente até ele! Mantêm 70% da direção atual e redirecionam os outros 30% em direção ao líder. Assim, não abandonam imediatamente sua rota atual, pois ela também pode levar a algo interessante, mas se deslocam gradualmente para as regiões mais promissoras.

Treinamento dos jovens: os estudantes em treinamento fazem o mesmo, mas todos seguem o melhor estagiário. O pior indivíduo de cada equipe é identificado e comparado com os melhores estudantes em treinamento. Se um estagiário for melhor, ele é efetivado, enquanto o indivíduo com pior desempenho é dispensado. Os estudantes em treinamento trazem novas ideias: um explorador experiente pode ter ficado preso em uma região pouco promissora, enquanto um jovem pode ter encontrado por acaso uma verdadeira mina de ouro.

blue_monkey

Figura 1. Esquema de funcionamento do algoritmo BM

O diagrama mostra o funcionamento do algoritmo BM, separando a lógica em dois fluxos independentes, um para os indivíduos adultos e outro para os filhotes, juntamente com as principais fórmulas. Em seguida, passa-se à elaboração do pseudocódigo do algoritmo BM.

Inicialização:

  1. Criar uma população de macacos adultos (tamanho N)
    • Atribuir a cada macaco uma posição aleatória no espaço de busca
    • Inicializar a velocidade de deslocamento em zero
    • Atribuir um peso inicial entre 4 e 6
  2. Criar uma população de filhotes (30% do tamanho da população adulta)
    • Atribuir a cada filhote uma posição aleatória
    • Inicializar a velocidade de deslocamento em zero
    • Atribuir um peso inicial entre 4 e 6
  3. Distribuir os adultos entre os grupos
    • Dividir uniformemente a população em T grupos
    • O macaco de índice i é atribuído ao grupo (i módulo T)
  4. Calcular o fitness inicial
    • Avaliar a qualidade da posição de cada adulto
    • Avaliar a qualidade da posição de cada filhote

Laço principal (enquanto o critério de parada não for atingido):

  1. Atualizar os pesos com base no fitness
    • Encontrar os valores mínimo e máximo de fitness entre os adultos
    • Normalizar os pesos: peso = 4 + 2 × (fitness - mín)/(máx - mín)
    • Repetir o procedimento para os filhotes
  2. Substituir os piores pelos melhores (a partir da segunda iteração)
    • Para cada grupo de adultos:
      • Encontrar o macaco com o pior fitness do grupo
      • Selecionar o próximo melhor filhote ainda não utilizado
      • Se o fitness do filhote for melhor que o do adulto:
        • Substituir o adulto pelo filhote
        • Criar um novo filhote para ocupar a vaga deixada
  3. Atualizar as posições dos macacos adultos
    • Para cada macaco do grupo:
      • Encontrar o líder do grupo (o macaco com o melhor fitness)
      • Atualizar a velocidade: nova velocidade = 0.7 × velocidade anterior + (peso do líder - peso individual) × valor aleatório × (posição do líder - posição individual)
      • Atualizar a posição: nova posição = posição anterior + velocidade × valor aleatório
      • Restringir a posição aos limites do espaço de busca
  4. Atualizar as posições dos filhotes
    • Encontrar o melhor filhote entre todos
    • Para cada um dos demais filhotes:
      • Atualizar a velocidade: nova velocidade = 0.7 × velocidade anterior + (peso do melhor filhote - peso individual) × valor aleatório × (posição do melhor filhote - posição individual)
      • Atualizar a posição: nova posição = posição anterior + velocidade × valor aleatório
      • Restringir a posição aos limites
  5. Calcular o novo fitness
    • Avaliar a qualidade das novas posições de todos os macacos
    • Avaliar a qualidade das novas posições de todos os filhotes
  6. Atualizar a melhor solução global
    • Se for encontrada uma solução melhor que o recorde atual:
      • Salvá-la como o novo recorde
    • Incrementar o contador de iterações
  7. Verificar o critério de parada
    • Se o número máximo de iterações tiver sido atingido OU
    • Se a precisão exigida tiver sido alcançada:
      • Sair do laço
    • Caso contrário: retornar à etapa 5

Conclusão:

  1. Retornar o resultado
    • Retornar a melhor posição encontrada
    • Retornar o valor de fitness nessa posição

A seguir, o pseudocódigo é transformado em uma implementação. Para isso, é criada a classe "C_AO_BM". Ela herda de "C_AO", que é a classe base. A classe "C_AO_BM" herda todos os campos públicos e protegidos da classe "C_AO". 

Campos públicos:

O construtor "C_AO_BM" inicializa as propriedades básicas do algoritmo e define seus parâmetros iniciais: "popSize" (tamanho da população), "numGroups" (número de grupos) e "childrenRatio" (proporção de filhotes). Também configura o array "params", usado para armazenar os parâmetros configuráveis do algoritmo. Para cada parâmetro, são especificados seu nome e seu valor atual. Além disso, define "minW" e "maxW" como os valores mínimo e máximo dos pesos utilizados pelo algoritmo.
  • SetParams() - método que define os parâmetros do algoritmo a partir do array "params". Ele lê os valores "val" de "params", converte-os para os tipos de dados correspondentes (int ou double) e os atribui aos membros da classe.
  • Init() - método de inicialização que prepara o algoritmo para execução usando os intervalos de entrada (rangeMinP, rangeMaxP, rangeStepP) e o número de épocas (epochsP).
  • Moving() - método que implementa a lógica principal de deslocamento ou atualização dos indivíduos da população de acordo com o algoritmo.
  • Revision() - método usado para reavaliar o estado atual da população e, quando necessário, selecionar e atualizar os indivíduos.
Variáveis públicas:
  • numGroups - número de grupos em que a população é dividida.
  • childrenRatio - percentual da população representado pelos "filhotes" (novos indivíduos).
Campos privados:
  • numChildren - número de "filhotes" na população, calculado com base em "popSize" e "childrenRatio".
  • minW, maxW - valores mínimo e máximo dos pesos.
  • Arrays dos macacos adultos:
    • monkeyRate - armazena a "velocidade" de cada indivíduo da população. O tamanho do array depende de "popSize" e do número de dimensões (coordenadas) do espaço de busca.
    • monkeyWeight - armazena os "pesos" (W) de cada indivíduo adulto.
    • groupId - array que indica a qual grupo pertence cada indivíduo adulto.
  • Os arrays dos filhotes armazenam:
    • childPos - posições dos "filhotes" no espaço de busca.
    • childRate - "velocidade" dos "filhotes".
    • childWeight - "pesos" dos "filhotes".
    • childFitness - valor de aptidão (fitness) dos "filhotes".
  • Métodos privados:
    • SwapWorstWithBest() - implementa a etapa 8 do algoritmo, que consiste em substituir o pior indivíduo do grupo pelo melhor indivíduo disponível.
    • UpdatePositions() - implementa as etapas 9 e 10 do algoritmo, responsáveis pela atualização das posições dos indivíduos.
    • UpdateWeights() - implementa a etapa 2 do algoritmo e a atualização geral dos pesos.
    • FindGroupBest() - encontra o melhor indivíduo do grupo especificado.
    • FindGroupWorst() - encontra o pior indivíduo do grupo especificado.

O algoritmo divide a população em grupos, utiliza os conceitos de "velocidade" (rate) e "peso" (weight) para o deslocamento dos indivíduos e também inclui mecanismos para criar "filhotes" e substituir os piores indivíduos pelos melhores dentro dos grupos. Os membros privados são responsáveis pelo gerenciamento interno do estado do algoritmo, enquanto os membros públicos fornecem a interface para configurar e executar a otimização.

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

    popSize        = 50;    // population size
    numGroups      = 5;     // number of groups T
    childrenRatio  = 0.3;   // offspring ratio in the population

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

    minW           = 4.0;
    maxW           = 6.0;
  }

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

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

  void Moving   ();
  void Revision ();

  //------------------------------------------------------------------
  int    numGroups;      // number of groups T
  double childrenRatio;  // offspring ratio

  private: //---------------------------------------------------------
  int    numChildren;         // number of offspring
  double minW;
  double maxW;

  // Arrays for adult monkeys
  double monkeyRate   [];     // Rate - velocity of change [popSize * coords]
  double monkeyWeight [];     // W - the monkeys' weights
  int    groupId      [];     // group membership

  // Arrays for offspring
  double childPos     [];      // offspring positions [numChildren * coords]
  double childRate    [];      // Rate for offspring
  double childWeight  [];      // W for offspring
  double childFitness [];      // offspring fitness

  void SwapWorstWithBest ();   // Step 8
  void UpdatePositions   ();   // Steps 9–10
  void UpdateWeights     ();   // Step 2 and update
  int  FindGroupBest     (int groupId);
  int  FindGroupWorst    (int groupId);
};
//————————————————————————————————————————————————————————————————————

A função "Init" realiza as seguintes operações:

Inicialização padrão. Primeiro, é chamada a função de inicialização da classe base "StandardInit", à qual são passados os parâmetros dos intervalos (rangeMinP, rangeMaxP, rangeStepP). Se essa inicialização básica falhar, a função "Init" também é encerrada, retornando "false".

Ajuste dos parâmetros:

  • É verificado o número de grupos (numGroups).
  • É calculado o número de "filhotes" (numChildren) com base no tamanho da população e na proporção de filhotes (childrenRatio). 
Redimensionamento dos arrays:
  • São redimensionados os arrays destinados a armazenar os dados dos indivíduos "adultos". Isso inclui "monkeyRate" (que armazena a velocidade em cada dimensão), "monkeyWeight" (o peso de cada indivíduo) e "groupId" (o grupo ao qual cada indivíduo pertence).
  • Também são redimensionados os arrays dos "filhotes", incluindo suas posições (childPos), velocidades (childRate), pesos (childWeight) e valores de aptidão (childFitness).

Inicialização das velocidades. Os arrays "monkeyRate" e "childRate", usados para determinar a direção do deslocamento, são inicializados com valores iguais a zero.

Inicialização dos pesos. Os pesos (monkeyWeight e childWeight) dos "adultos" e dos "filhotes" são inicializados com valores aleatórios no intervalo entre "minW" e "maxW". Os pesos efetivos serão recalculados após o primeiro cálculo do fitness.

Distribuição entre os grupos:

  • Os indivíduos adultos são distribuídos entre os grupos (groupId) usando o operador módulo. Assim, o indivíduo de índice 0 é atribuído ao grupo 0, o de índice 1 ao grupo 1 e assim por diante; o indivíduo de índice numGroups - 1 pertence ao grupo numGroups - 1, enquanto o de índice numGroups volta ao grupo 0.
  • Todos os filhotes ficam inicialmente em uma única equipe.

Inicialização do fitness dos filhotes. O array "childFitness" é inicializado com o menor valor possível. Isso garante que qualquer valor de fitness calculado posteriormente seja maior que esse valor inicial.

A função retorna "true" quando a inicialização é concluída com êxito. Dessa forma, "Init" realiza toda a preparação necessária: define os tamanhos das estruturas de dados, atribui os valores iniciais de velocidade e peso, distribui os indivíduos entre os grupos e os prepara para o primeiro ciclo de cálculos.

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

  //------------------------------------------------------------------
  // Parameter adjustment
  if (numGroups < 1) numGroups = 1;
  if (numGroups > popSize) numGroups = popSize;

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

  // Initializing arrays for adult individuals
  ArrayResize (monkeyRate, popSize * coords);
  ArrayResize (monkeyWeight, popSize);
  ArrayResize (groupId, popSize);

  // Initializing arrays for offspring
  ArrayResize (childPos, numChildren * coords);
  ArrayResize (childRate, numChildren * coords);
  ArrayResize (childWeight, numChildren);
  ArrayResize (childFitness, numChildren);

  // Step 2: Initialize velocity (Rate) and weight W
  // Rate ∈ [0, 1], W ∈ [4, 6]
  ArrayInitialize (monkeyRate, 0.0);
  ArrayInitialize (childRate, 0.0);

  // The weights will be updated after the first fitness calculation
  for (int i = 0; i < popSize; i++)
  {
    monkeyWeight [i] = u.RNDfromCI (minW, maxW);
  }

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

  // Step 3: Distribution into groups
  // Adult individuals are divided into T groups
  for (int i = 0; i < popSize; i++)
  {
    groupId [i] = i % numGroups;
  }
  // "while all offspring are in one team" — all offspring are initially in one team

  ArrayInitialize (childFitness, -DBL_MAX);

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

O método "Moving" realiza as seguintes operações. Primeiro, verifica o sinalizador "revision" e, se "revision" for igual a "false" (primeira execução):

Inicialização da população de adultos (Etapa 1). Para cada indivíduo adulto "i", de 0 a popSize - 1, e para cada dimensão "j", de 0 a coords - 1, a posição (a [i].c [j]) é inicializada com um número aleatório dentro do intervalo especificado (rangeMin [j], rangeMax [j]). Em seguida, o valor da posição é ajustado pela função "u.SeInDiSp", que garante que ele permaneça dentro dos limites definidos e respeite o passo especificado (rangeStep [j]).

Inicialização da população de filhotes (Etapa 1). Para cada filhote "i", de 0 a numChildren - 1, e para cada dimensão "j", de 0 a coords - 1, a posição (childPos [i * coords + j]) é inicializada com um número aleatório dentro do intervalo especificado (rangeMin, rangeMax). O valor da posição também é ajustado pela função "u.SeInDiSp".

O sinalizador "revision" é definido como "true", para que, nas chamadas seguintes do método "Moving", seja executada outra parte da lógica. O método conclui a execução. Se "revision" for igual a "true" (execuções subsequentes), as posições são atualizadas por meio da chamada ao método "UpdatePositions ()". Esse método é responsável pelo deslocamento dos indivíduos de acordo com a lógica do algoritmo Blue Monkey.

Assim, o método "Moving" tem duas finalidades principais:

  • Na primeira chamada, ele inicializa completamente as posições de todos os indivíduos adultos e filhotes dentro dos intervalos especificados.
  • Nas chamadas seguintes, ele delega a atualização das posições ao método "UpdatePositions ()", que implementa o núcleo da lógica de deslocamento do algoritmo.
//————————————————————————————————————————————————————————————————————
void C_AO_BM::Moving ()
{
  if (!revision)
  {
    // Step 1: Initialization of the adult population
    for (int i = 0; i < popSize; i++)
    {
      for (int j = 0; j < coords; j++)
      {
        a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
        a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]);
      }
    }

    // Step 1: Initialization of the offspring population
    for (int i = 0; i < numChildren; i++)
    {
      for (int j = 0; j < coords; j++)
      {
        childPos [i * coords + j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
        childPos [i * coords + j] = u.SeInDiSp (childPos [i * coords + j], rangeMin [j], rangeMax [j], rangeStep [j]);
      }
    }

    revision = true;
    return;
  }

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

O método "Revision" realiza as seguintes operações:

Atualização dos pesos. Primeiro, é chamado o método "UpdateWeights ()". Isso significa que os pesos de cada indivíduo são recalculados com base em seus valores atuais de fitness.
Substituição do pior pelo melhor. Em seguida, é chamado o método "SwapWorstWithBest ()". Esse método encontra o adulto com o pior valor de fitness e o substitui por um filhote com fitness superior, quando houver um candidato adequado. 

    Atualização do melhor indivíduo.Para os adultos, são percorridos todos os indivíduos "i", de 0 a popSize - 1. Se o valor atual de fitness de um adulto (a [i].f) for maior que o melhor valor de fitness já alcançado por esse indivíduo (a [i].fB), seu melhor valor de fitness é atualizado. A posição correspondente ao melhor resultado individual (a [i].cB) é copiada da posição atual do indivíduo (a [i].c).

    Da mesma forma, se o valor atual de fitness de um adulto (a [i].f) for maior que o melhor valor de fitness de toda a população (fB), o melhor valor global de fitness (fB) é atualizado. A posição da melhor solução global (cB) é copiada da posição atual do indivíduo (a [i].c).

    Para os filhotes, são percorridos todos os indivíduos "i", de 0 a numChildren - 1. Se o fitness do filhote (childFitness [i]) for maior que o melhor valor global de fitness (fB), o valor de fB é atualizado. A posição da melhor solução global (cB) também é atualizada, copiando-se as coordenadas do filhote armazenadas em childPos.

      De modo geral, o método "Revision" tem como objetivo melhorar a qualidade das soluções, acompanhando e preservando tanto o melhor resultado já encontrado por cada indivíduo (recorde individual) quanto o melhor resultado de toda a população (recorde global), considerando tanto os adultos quanto os filhotes.

      //————————————————————————————————————————————————————————————————————
      void C_AO_BM::Revision ()
      {
        // Update weights based on current fitness
        UpdateWeights ();
      
        // Step 8: Swapping
        SwapWorstWithBest ();
      
        // Step 12: Update Current Best
        for (int i = 0; i < popSize; i++)
        {
          if (a [i].f > a [i].fB)
          {
            a [i].fB = a [i].f;
            ArrayCopy (a [i].cB, a [i].c, 0, 0, coords);
          }
      
          if (a [i].f > fB)
          {
            fB = a [i].f;
            ArrayCopy (cB, a [i].c, 0, 0, coords);
          }
        }
      
        // Checking offspring for the global best solution
        for (int i = 0; i < numChildren; i++)
        {
          if (childFitness [i] > fB)
          {
            fB = childFitness [i];
            for (int j = 0; j < coords; j++)
            {
              cB [j] = childPos [i * coords + j];
            }
          }
        }
      }
      //————————————————————————————————————————————————————————————————————

      O método "UpdatePositions" da classe "C_AO_BM" implementa a lógica principal de deslocamento dos indivíduos da população com base nas equações matemáticas definidas pelo algoritmo. O método é dividido em duas partes: atualização das posições dos indivíduos adultos, os chamados "macacos-azuis", e atualização das posições dos filhotes.

      Atualização das posições dos indivíduos adultos (macacos-azuis):

      O método percorre cada indivíduo adulto "i", de 0 a popSize - 1. Busca do líder do grupo: para cada indivíduo atual, é determinado o grupo ao qual ele pertence (groupId [i]) e encontrado o índice do melhor indivíduo desse grupo (bestIdx = FindGroupBest). Se o indivíduo atual for o próprio líder do grupo ou se nenhum líder for encontrado, passa-se para o próximo indivíduo. Em seguida, são aplicadas as Equações 1 e 2 a cada dimensão.

      Atualização da velocidade (Equação 1):

      • São determinados o peso do líder do grupo "Wleader" e o peso do indivíduo atual "Wi".
      • São obtidas as posições atuais do líder "Xbest" e do indivíduo atual "Xi" na dimensão considerada.
      • É gerado um número aleatório "rand1" entre 0 e 1.
      • A nova velocidade "monkeyRate" do indivíduo atual é calculada. Ela combina a velocidade anterior, multiplicada pelo coeficiente 0.9, com um termo calculado pela diferença entre o peso do líder e o peso do indivíduo atual, multiplicada por um número aleatório e pela diferença entre suas posições.

      Atualização da posição (Equação 2):

      É gerado outro número aleatório "rand2" entre 0 e 1. A nova posição do indivíduo é calculada somando à posição atual o produto da nova velocidade "monkeyRate" pelo número aleatório "rand2". A nova posição obtida é então ajustada aos intervalos especificados por meio de "u.SeInDiSp", garantindo que permaneça dentro da região de busca permitida.

        Atualização das posições dos filhotes:

        1. Busca do melhor filhote: primeiro, o método percorre todos os filhotes "i", de 0 a numChildren - 1, para determinar o maior valor de fitness (bestChildFitness) e o índice correspondente (bestChildIdx).
        2. Atualização pelas Equações 3 e 4, caso o melhor filhote tenha sido encontrado:
          • Se houver um melhor filhote (bestChildIdx >= 0), o método percorre todos os demais filhotes "i", de 0 a numChildren - 1, exceto o próprio melhor indivíduo.
          • Atualização da velocidade do filhote (Equação 3):
            • Assim como no caso dos adultos, são determinados o peso do melhor filhote "Wchleader" e o peso do filhote atual "Wchi".
            • São obtidas as posições atuais do melhor filhote "Xchbest" e do filhote atual "Xchi" na dimensão considerada.
            • É gerado um número aleatório "rand1" entre 0 e 1.
            • A nova velocidade (childRate) do filhote atual é calculada por uma fórmula análoga à da Equação 1.
          • Atualização da posição do filhote (Equação 4):
            • É gerado um número aleatório "rand2" entre 0 e 1.
            • A nova posição do filhote é calculada somando à posição atual o produto de sua nova velocidade (childRate) pelo número aleatório "rand2".
          • Limitação da posição: a nova posição obtida para o filhote também é ajustada aos intervalos especificados.

        Assim, o método "UpdatePositions" implementa:

        • O modelo de comportamento dos "macacos-azuis": os indivíduos adultos se deslocam tomando como referência o líder do próprio grupo, enquanto a velocidade depende da diferença entre os pesos e entre as posições.
        • O modelo de comportamento dos filhotes: eles se deslocam de forma semelhante, tomando como referência o melhor indivíduo do grupo.
        • A manutenção dos indivíduos dentro da região de busca permitida por meio da função "SeInDiSp".
        //————————————————————————————————————————————————————————————————————
        void C_AO_BM::UpdatePositions ()
        {
          // Step 9: Update the positions of the blue monkeys using Equations 1 and 2
          for (int i = 0; i < popSize; i++)
          {
            int bestIdx = FindGroupBest (groupId [i]);
            if (bestIdx < 0 || bestIdx == i) continue;
        
            for (int j = 0; j < coords; j++)
            {
              // Equation (1): Velocity (Rate) update
              double Wleader = monkeyWeight [bestIdx];
              double Wi      = monkeyWeight [i];
              double Xbest   = a [bestIdx].c [j];
              double Xi      = a [i].c [j];
              double rand1   = u.RNDfromCI (0, 1);
        
              // Rate_{i,t} = (0.7 * Rate) + (W_leader - W_i) * rand * (X_best - X_i)
              monkeyRate [i * coords + j] = 0.9 * monkeyRate [i * coords + j] +
                                            (Wleader - Wi) * rand1 * (Xbest - Xi);
        
              // Equation (2): Position update
              // X_{i,t} = X_i + Rate_{i,t} * rand
              double rand2 = u.RNDfromCI (0, 1);
              a [i].c [j] = a [i].c [j] + monkeyRate [i * coords + j] * rand2;
        
              // Constraining positions within the range
              a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]);
            }
          }
        
          // Step 10: Update the positions of the offspring using Equations 3 and 4
          // Find the best offspring
          int bestChildIdx = -1;
          double bestChildFitness = -DBL_MAX;
          for (int i = 0; i < numChildren; i++)
          {
            if (childFitness [i] > bestChildFitness)
            {
              bestChildFitness = childFitness [i];
              bestChildIdx = i;
            }
          }
        
          if (bestChildIdx >= 0)
          {
            for (int i = 0; i < numChildren; i++)
            {
              if (i == bestChildIdx) continue;
        
              for (int j = 0; j < coords; j++)
              {
                // Equation (3): Offspring velocity (Rate) update
                double Wchleader = childWeight [bestChildIdx];
                double Wchi      = childWeight [i];
                double Xchbest   = childPos [bestChildIdx * coords + j];
                double Xchi      = childPos [i * coords + j];
                double rand1     = u.RNDfromCI (0, 1);
        
                childRate [i * coords + j] = 0.9 * childRate [i * coords + j] +
                                             (Wchleader - Wchi) * rand1 * (Xchbest - Xchi);
        
                // Equation (4): Offspring position update
                double rand2 = u.RNDfromCI (0, 1);
                childPos [i * coords + j] = childPos [i * coords + j] + childRate [i * coords + j] * rand2;
        
                childPos [i * coords + j] = u.SeInDiSp (childPos [i * coords + j], rangeMin [j], rangeMax [j], rangeStep [j]);
              }
            }
          }
        }
        //————————————————————————————————————————————————————————————————————

        O método "SwapWorstWithBest" realiza as seguintes operações:

        Preparação dos filhotes. É criado um array "childIndices" para armazenar os índices de todos os filhotes. Esse array é preenchido com índices de 0 até o número total de filhotes. Em seguida, os índices dos filhotes são ordenados em ordem decrescente de acordo com seus valores de fitness (childFitness). Para isso, é usado um algoritmo simples de ordenação por bolha. Como resultado, "childIndices [0]" conterá o índice do melhor filhote, "childIndices [1]" o índice do segundo melhor, e assim por diante. Também é criado um array booleano "childUsed" para registrar se cada filhote já foi utilizado em uma substituição.

        Iteração pelos grupos de indivíduos adultos. O método percorre cada grupo de adultos, de g = 0 até numGroups - 1. Para cada grupo, se todos os filhotes já tiverem sido utilizados, o processamento é encerrado. Dentro de cada grupo, é identificado o pior indivíduo adulto (worstAdultIdx). Em seguida, é selecionado o melhor filhote ainda disponível, usando o array ordenado "childIndices" e o contador "childIndex". Depois, verifica-se se o fitness do filhote selecionado (childFitness [currentChildIdx]) é melhor que o fitness do pior adulto do grupo (a [worstAdultIdx].f).

        • Substituição, caso haja melhoria:
          • Se o filhote for melhor:
            • A posição e a velocidade do pior adulto são substituídas pela posição e pela velocidade do filhote selecionado.
            • O valor de fitness do adulto é atualizado com o valor de fitness do filhote.
            • O peso do adulto é atualizado com o peso do filhote.
            • O filhote é marcado como utilizado (childUsed[currentChildIdx] = true), e o contador "childIndex" é incrementado para passar ao próximo melhor filhote.
        • Interrupção das substituições, caso não haja melhoria:
          • Se nem mesmo o melhor filhote ainda disponível for melhor que o pior adulto do grupo atual, as tentativas de substituição neste grupo e nos grupos seguintes são encerradas. Isso ocorre porque os filhotes estão ordenados por fitness e, se o melhor filhote disponível não supera o pior indivíduo adulto, os filhotes seguintes também não proporcionarão melhoria.
          Criação de novos filhotes. Depois de concluídas todas as substituições possíveis, o método percorre todos os filhotes. Se um filhote tiver sido utilizado em uma substituição, isto é, se childUsed [i] for igual a true, sua posição é regenerada dentro dos intervalos especificados, usando u.RNDfromCI e u.SeInDiSp. Sua velocidade é redefinida para "0", e o valor de fitness é redefinido para o menor valor possível, indicando que ele ainda não foi avaliado após a reinicialização. Também é atribuído a ele um novo peso aleatório dentro dos limites iniciais minW e maxW.

            Assim, o método "SwapWorstWithBest" tem a seguinte função: ele utiliza ativamente os melhores filhotes para substituir os piores indivíduos adultos em seus respectivos grupos sempre que isso representa uma melhoria, incorporando à população adulta as melhores soluções encontradas pelos filhotes. Os filhotes usados nessas substituições são então "renascidos" com novos parâmetros aleatórios.

            //————————————————————————————————————————————————————————————————————
            void C_AO_BM::SwapWorstWithBest ()
            {
              // Step 8: Replacing the individual with the worst fitness in each group with the individual with the best fitness from the offspring group
            
              // Create an array of offspring indices and sort them by fitness
              int childIndices [];
              ArrayResize (childIndices, numChildren);
              for (int i = 0; i < numChildren; i++)
              {
                childIndices [i] = i;
              }
            
              // Sort the offspring indices in descending order of fitness
              for (int i = 0; i < numChildren - 1; i++)
              {
                for (int j = 0; j < numChildren - i - 1; j++)
                {
                  if (childFitness [childIndices [j]] < childFitness [childIndices [j + 1]])
                  {
                    int temp = childIndices [j];
                    childIndices [j] = childIndices [j + 1];
                    childIndices [j + 1] = temp;
                  }
                }
              }
            
              // To track the offspring that have been used
              bool childUsed [];
              ArrayResize (childUsed, numChildren);
              ArrayInitialize (childUsed, false);
            
              // For each group, try to replace the worst individual
              int childIndex = 0;
            
              for (int g = 0; g < numGroups; g++)
              {
                if (childIndex >= numChildren) break;
            
                // Find the worst individual in the group
                int worstAdultIdx = FindGroupWorst (g);
                if (worstAdultIdx < 0) continue;
            
                // Use the best available offspring
                int currentChildIdx = childIndices [childIndex];
            
                // Check whether the offspring is better
                if (childFitness [currentChildIdx] > a [worstAdultIdx].f)
                {
                  // Perform the replacement
                  for (int j = 0; j < coords; j++)
                  {
                    a [worstAdultIdx].c [j] = childPos [currentChildIdx * coords + j];
                    monkeyRate [worstAdultIdx * coords + j] = childRate [currentChildIdx * coords + j];
                  }
                  a [worstAdultIdx].f = childFitness [currentChildIdx];
                  monkeyWeight [worstAdultIdx] = childWeight [currentChildIdx];
            
                  childUsed [currentChildIdx] = true;
                  childIndex++;
                }
                else
                {
                  // If the best offspring is no better than the worst adult,
                  // there is no point in checking further (they are sorted)
                  break;
                }
              }
            
              // Generate new offspring to replace the ones that have been used
              for (int i = 0; i < numChildren; i++)
              {
                if (childUsed [i])
                {
                  for (int j = 0; j < coords; j++)
                  {
                    childPos [i * coords + j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
                    childPos [i * coords + j] = u.SeInDiSp (childPos [i * coords + j],
                                                            rangeMin [j], rangeMax [j], rangeStep [j]);
                    childRate [i * coords + j] = 0.0;
                  }
                  childFitness [i] = -DBL_MAX;
                  childWeight [i] = u.RNDfromCI (minW, maxW); // New weight according to the initial conditions
                }
              }
            }
            //————————————————————————————————————————————————————————————————————

            A função "UpdateWeights" é responsável por ajustar os pesos de dois grupos de indivíduos: os "macacos" (adultos) e os "filhotes". O ajuste dos pesos baseia-se na normalização e no escalonamento do fitness de cada indivíduo.

            Determinação do intervalo de fitness. Primeiro, a função encontra os valores mínimo (minFit) e máximo (maxFit) de fitness entre todos os "macacos" da população.

            Tratamento do caso de valores de fitness iguais. Se a diferença entre os valores máximo e mínimo de fitness for muito pequena (inferior a 1e-10, o que na prática significa que todos os valores de fitness são aproximadamente iguais), todos os "macacos" recebem um peso fixo igual a 5.0, correspondente ao ponto médio do intervalo definido para os pesos.

            Normalização e escalonamento dos pesos dos "macacos". Se o intervalo de fitness for suficientemente amplo (maior que 1e-10), para cada "macaco" são realizadas as seguintes operações:

            • Seu fitness (a [i].f) é normalizado para o intervalo [0, 1]. Para isso, subtrai-se o valor mínimo de fitness (minFit) e divide-se o resultado pela diferença entre os valores máximo e mínimo de fitness (maxFit - minFit).
            • Em seguida, o valor normalizado obtido é escalonado para gerar o peso final no intervalo [4, 6]. Isso é feito multiplicando o valor normalizado por 2.0 e somando o peso mínimo (minW). Assim, os indivíduos com o menor fitness recebem um peso próximo de 4.0, enquanto aqueles com o maior fitness recebem um peso próximo de 6.0.

              Processamento dos pesos dos "filhotes". Depois do processamento dos "macacos", o mesmo procedimento é repetido para os "filhotes". Primeiro, são determinados os valores mínimo (minChildFit) e máximo (maxChildFit) de fitness entre todos os "filhotes". Nesse cálculo, são ignorados os "filhotes" cujo fitness ainda não foi inicializado, considerando-se que um fitness não inicializado possui um valor extremamente baixo, próximo de -DBL_MAX. Da mesma forma que para os "macacos":

              • se a diferença entre os valores máximo e mínimo de fitness dos "filhotes" for pequena, todos os "filhotes" já inicializados recebem peso 5.0;
              • se o intervalo de fitness dos "filhotes" for suficientemente amplo, seus valores de fitness são normalizados para o intervalo [0, 1] e, em seguida, escalonados para o intervalo [4, 6], da mesma forma que para os "macacos".

                De modo geral, a função "UpdateWeights" transforma os valores de fitness dos indivíduos, tanto dos "macacos" quanto dos "filhotes", em pesos que variam de 4 a 6. Isso permite aumentar ou reduzir a influência de indivíduos com diferentes níveis de fitness nas etapas seguintes do algoritmo. Indivíduos com fitness mais alto recebem pesos maiores, o que pode indicar maior influência nas etapas seguintes do algoritmo.

                //————————————————————————————————————————————————————————————————————
                void C_AO_BM::UpdateWeights ()
                {
                  double minFit = DBL_MAX, maxFit = -DBL_MAX;
                
                  for (int i = 0; i < popSize; i++)
                  {
                    if (a [i].f > maxFit) maxFit = a [i].f;
                    if (a [i].f < minFit) minFit = a [i].f;
                  }
                
                  if (maxFit - minFit > 1e-10)
                  {
                    for (int i = 0; i < popSize; i++)
                    {
                      // Normalize to [0, 1] and scale to [4, 6]
                      double normalized = (a [i].f - minFit) / (maxFit - minFit);
                      monkeyWeight [i] = minW + normalized * 2.0; // Obtain a value in [4, 6]
                    }
                  }
                  else
                  {
                    for (int i = 0; i < popSize; i++)
                    {
                      monkeyWeight [i] = 5.0; // Average value
                    }
                  }
                
                  // Updating offspring weights
                  double minChildFit = DBL_MAX, maxChildFit = -DBL_MAX;
                
                  for (int i = 0; i < numChildren; i++)
                  {
                    if (childFitness [i] > -DBL_MAX + 1) // Initialization check
                    {
                      if (childFitness [i] > maxChildFit) maxChildFit = childFitness [i];
                      if (childFitness [i] < minChildFit) minChildFit = childFitness [i];
                    }
                  }
                
                  if (maxChildFit - minChildFit > 1e-10)
                  {
                    for (int i = 0; i < numChildren; i++)
                    {
                      if (childFitness [i] > -DBL_MAX + 1)
                      {
                        double normalized = (childFitness [i] - minChildFit) /
                                            (maxChildFit - minChildFit);
                        childWeight [i] = minW + normalized * 2.0; // In the range [4, 6]
                      }
                    }
                  }
                  else
                  {
                    for (int i = 0; i < numChildren; i++)
                    {
                      if (childFitness [i] > -DBL_MAX + 1)
                      {
                        childWeight [i] = 5.0;
                      }
                    }
                  }
                }
                //————————————————————————————————————————————————————————————————————

                A função "FindGroupBest" serve para encontrar o melhor indivíduo de um determinado grupo. A função percorre todos os indivíduos da população, com índices de 0 a popSize - 1.

                  Verificação da associação ao grupo e comparação do fitness. Para cada indivíduo, verifica-se se ele pertence ao grupo de interesse. Se pertencer, seu fitness (a [i].f) é comparado com o melhor valor registrado até o momento (bestFitness). Se o fitness atual (a [i].f) for maior que o melhor valor de fitness registrado até o momento (bestFitness), então:
                  • bestFitness é atualizado com o valor de fitness do indivíduo atual.
                  • bestIdx é atualizado com o índice do indivíduo atual "i".

                  Após percorrer toda a população, a função retorna o valor "bestIdx". Esse será o índice do indivíduo com o maior fitness no grupo especificado. Se o grupo estiver vazio ou não contiver indivíduos com fitness superior ao valor mínimo inicial, a função retornará -1.

                  Assim, a função "FindGroupBest" permite identificar o líder, isto é, o melhor indivíduo de um determinado grupo da população.

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

                  A função "FindGroupWorst" percorre todos os indivíduos da população, com índices de 0 a popSize - 1.

                  Verificação da associação ao grupo e comparação do fitness. Para cada indivíduo no laço, primeiro é verificado se ele pertence ao grupo de interesse, definido pelo parâmetro "grpId". Se o indivíduo pertencer ao grupo especificado, seu valor de fitness (a [i].f) é comparado com o pior valor registrado até o momento (worstFitness). Se o fitness do indivíduo atual for menor que worstFitness, isso significa que foi encontrado um novo indivíduo com o menor fitness até o momento. Nesse caso:

                  • worstFitness é atualizado com o valor de fitness desse novo pior indivíduo;
                  • worstIdx é atualizado para armazenar o índice "i" desse novo pior indivíduo.
                  Depois que o laço for concluído, ou seja, após a verificação de todos os indivíduos da população, a função retornará o valor "worstIdx". Esse será o índice do pior indivíduo do grupo especificado. Se o grupo estiver vazio, será retornado o valor inicial -1. Em outras palavras, a função "FindGroupWorst" encontra e retorna o índice do indivíduo com o menor valor de fitness no grupo identificado pelo parâmetro "grpId".

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


                  Resultados dos testes

                  A análise dos resultados mostra que, infelizmente, eles não são suficientes para que o algoritmo entre na tabela de classificação. 

                  BM|Blue Monkey Algorithm|50.0|3.0|0.7|
                  =============================
                  5 Hilly's; Func runs: 10000; result: 0.6730156841791012
                  25 Hilly's; Func runs: 10000; result: 0.3811383925125479
                  500 Hilly's; Func runs: 10000; result: 0.26883473381494066
                  =============================
                  5 Forest's; Func runs: 10000; result: 0.6116553050534701
                  25 Forest's; Func runs: 10000; result: 0.3051920014986885
                  500 Forest's; Func runs: 10000; result: 0.18691769556662757
                  =============================
                  5 Megacity's; Func runs: 10000; result: 0.4384615384615385
                  25 Megacity's; Func runs: 10000; result: 0.19353846153846152
                  500 Megacity's; Func runs: 10000; result: 0.1037384615384623
                  =============================
                  All score: 3.16249 (35.14%)

                  A visualização mostra claramente a divisão em grupos e as regiões de concentração dos indivíduos.

                  Hilly

                  BM na função de teste Hilly

                  Forest

                  BM na função de teste Forest

                  Megacity

                  BM na função de teste Megacity

                  Na tabela de classificação, o algoritmo BM é apresentado apenas para fins informativos.

                  AO Description Hilly Hilly
                  Final
                  Forest Forest
                  Final
                  Megacity (discrete) Megacity
                  Final
                  Final
                  Result
                  % of
                  MAX
                  10 p (5 F) 50 p (25 F) 1000 p (500 F) 10 p (5 F) 50 p (25 F) 1000 p (500 F) 10 p (5 F) 50 p (25 F) 1000 p (500 F)
                  DOAdingom dingo_optimization_algorithm_M 0,47968 0,45367 0,46369 1,39704 0,94145 0,87909 0,91454 2,73508 0,78615 0,86061 0,84805 2,49481 6,627 73,63
                  ANS across neighbourhood search 0,94948 0,84776 0,43857 2,23581 1,00000 0,92334 0,39988 2,32323 0,70923 0,63477 0,23091 1,57491 6,134 68,15
                  CLA code lock algorithm (joo) 0,95345 0,87107 0,37590 2,20042 0,98942 0,91709 0,31642 2,22294 0,79692 0,69385 0,19303 1,68380 6,107 67,86
                  AMOm animal migration ptimization M 0,90358 0,84317 0,46284 2,20959 0,99001 0,92436 0,46598 2,38034 0,56769 0,59132 0,23773 1,39675 5,987 66,52
                  (P+O)ES (P+O) evolution strategies 0,92256 0,88101 0,40021 2,20379 0,97750 0,87490 0,31945 2,17185 0,67385 0,62985 0,18634 1,49003 5,866 65,17
                  CTA comet tail algorithm (joo) 0,95346 0,86319 0,27770 2,09435 0,99794 0,85740 0,33949 2,19484 0,88769 0,56431 0,10512 1,55712 5,846 64,96
                  TETA time evolution travel algorithm (joo) 0,91362 0,82349 0,31990 2,05701 0,97096 0,89532 0,29324 2,15952 0,73462 0,68569 0,16021 1,58052 5,797 64,41
                  SDSm stochastic diffusion search M 0,93066 0,85445 0,39476 2,17988 0,99983 0,89244 0,19619 2,08846 0,72333 0,61100 0,10670 1,44103 5,709 63,44
                  BOAm billiards optimization algorithm M 0,95757 0,82599 0,25235 2,03590 1,00000 0,90036 0,30502 2,20538 0,73538 0,52523 0,09563 1,35625 5,598 62,19
                  AAm archery algorithm M 0,91744 0,70876 0,42160 2,04780 0,92527 0,75802 0,35328 2,03657 0,67385 0,55200 0,23738 1,46323 5,548 61,64
                  ESG evolution of social groups (joo) 0,99906 0,79654 0,35056 2,14616 1,00000 0,82863 0,13102 1,95965 0,82333 0,55300 0,04725 1,42358 5,529 61,44
                  SIA simulated isotropic annealing (joo) 0,95784 0,84264 0,41465 2,21513 0,98239 0,79586 0,20507 1,98332 0,68667 0,49300 0,09053 1,27020 5,469 60,76
                  EOm extremal_optimization_M 0,76166 0,77242 0,31747 1,85155 0,99999 0,76751 0,23527 2,00277 0,74769 0,53969 0,14249 1,42987 5,284 58,71
                  BBO biogeography based optimization 0,94912 0,69456 0,35031 1,99399 0,93820 0,67365 0,25682 1,86867 0,74615 0,48277 0,17369 1,40261 5,265 58,50
                  ACS artificial cooperative search 0,75547 0,74744 0,30407 1,80698 1,00000 0,88861 0,22413 2,11274 0,69077 0,48185 0,13322 1,30583 5,226 58,06
                  DA dialectical algorithm 0,86183 0,70033 0,33724 1,89940 0,98163 0,72772 0,28718 1,99653 0,70308 0,45292 0,16367 1,31967 5,216 57,95
                  BHAm black hole algorithm M 0,75236 0,76675 0,34583 1,86493 0,93593 0,80152 0,27177 2,00923 0,65077 0,51646 0,15472 1,32195 5,196 57,73
                  ASO anarchy society optimization 0,84872 0,74646 0,31465 1,90983 0,96148 0,79150 0,23803 1,99101 0,57077 0,54062 0,16614 1,27752 5,178 57,54
                  RFO royal flush optimization (joo) 0,83361 0,73742 0,34629 1,91733 0,89424 0,73824 0,24098 1,87346 0,63154 0,50292 0,16421 1,29867 5,089 56,55
                  AOSm atomic orbital search M 0,80232 0,70449 0,31021 1,81702 0,85660 0,69451 0,21996 1,77107 0,74615 0,52862 0,14358 1,41835 5,006 55,63
                  TSEA turtle shell evolution algorithm (joo) 0,96798 0,64480 0,29672 1,90949 0,99449 0,61981 0,22708 1,84139 0,69077 0,42646 0,13598 1,25322 5,004 55,60
                  BSA backtracking_search_algorithm 0,97309 0,54534 0,29098 1,80941 0,99999 0,58543 0,21747 1,80289 0,84769 0,36953 0,12978 1,34700 4,959 55,10
                  DE differential evolution 0,95044 0,61674 0,30308 1,87026 0,95317 0,78896 0,16652 1,90865 0,78667 0,36033 0,02953 1,17653 4,955 55,06
                  SRA successful restaurateur algorithm (joo) 0,96883 0,63455 0,29217 1,89555 0,94637 0,55506 0,19124 1,69267 0,74923 0,44031 0,12526 1,31480 4,903 54,48
                  CRO chemical reaction optimisation 0,94629 0,66112 0,29853 1,90593 0,87906 0,58422 0,21146 1,67473 0,75846 0,42646 0,12686 1,31178 4,892 54,36
                  BIO blood inheritance optimization (joo) 0,81568 0,65336 0,30877 1,77781 0,89937 0,65319 0,21760 1,77016 0,67846 0,47631 0,13902 1,29378 4,842 53,80
                  DOA dream_optimization_algorithm 0,85556 0,70085 0,37280 1,92921 0,73421 0,48905 0,24147 1,46473 0,77231 0,47354 0,18561 1,43146 4,825 53,62
                  BSA bird swarm algorithm 0,89306 0,64900 0,26250 1,80455 0,92420 0,71121 0,24939 1,88479 0,69385 0,32615 0,10012 1,12012 4,809 53,44
                  DEA dolphin_echolocation_algorithm 0,75995 0,67572 0,34171 1,77738 0,89582 0,64223 0,23941 1,77746 0,61538 0,44031 0,15115 1,20684 4,762 52,91
                  HS harmony search 0,86509 0,68782 0,32527 1,87818 0,99999 0,68002 0,09590 1,77592 0,62000 0,42267 0,05458 1,09725 4,751 52,79
                  SSG saplings sowing and growing 0,77839 0,64925 0,39543 1,82308 0,85973 0,62467 0,17429 1,65869 0,64667 0,44133 0,10598 1,19398 4,676 51,95
                  BCOm bacterial chemotaxis optimization M 0,75953 0,62268 0,31483 1,69704 0,89378 0,61339 0,22542 1,73259 0,65385 0,42092 0,14435 1,21912 4,649 51,65
                  ABO african buffalo optimization 0,83337 0,62247 0,29964 1,75548 0,92170 0,58618 0,19723 1,70511 0,61000 0,43154 0,13225 1,17378 4,634 51,49
                  (PO)ES (PO) evolution strategies 0,79025 0,62647 0,42935 1,84606 0,87616 0,60943 0,19591 1,68151 0,59000 0,37933 0,11322 1,08255 4,610 51,22
                  FBA fractal-based Algorithm 0,79000 0,65134 0,28965 1,73099 0,87158 0,56823 0,18877 1,62858 0,61077 0,46062 0,12398 1,19537 4,555 50,61
                  TSm tabu search M 0,87795 0,61431 0,29104 1,78330 0,92885 0,51844 0,19054 1,63783 0,61077 0,38215 0,12157 1,11449 4,536 50,40
                  BSO brain storm optimization 0,93736 0,57616 0,29688 1,81041 0,93131 0,55866 0,23537 1,72534 0,55231 0,29077 0,11914 0,96222 4,498 49,98
                  WOAm wale optimization algorithm M 0,84521 0,56298 0,26263 1,67081 0,93100 0,52278 0,16365 1,61743 0,66308 0,41138 0,11357 1,18803 4,476 49,74
                  AEFA artificial electric field algorithm 0,87700 0,61753 0,25235 1,74688 0,92729 0,72698 0,18064 1,83490 0,66615 0,11631 0,09508 0,87754 4,459 49,55
                  AEO artificial ecosystem-based optimization algorithm 0,91380 0,46713 0,26470 1,64563 0,90223 0,43705 0,21400 1,55327 0,66154 0,30800 0,28563 1,25517 4,454 49,49
                  CAm camel algorithm M 0,78684 0,56042 0,35133 1,69859 0,82772 0,56041 0,24336 1,63149 0,64846 0,33092 0,13418 1,11356 4,444 49,37
                  ACOm ant colony optimization M 0,88190 0,66127 0,30377 1,84693 0,85873 0,58680 0,15051 1,59604 0,59667 0,37333 0,02472 0,99472 4,438 49,31
                  CMAES covariance_matrix_adaptation_evolution_strategy 0,76258 0,72089 0,00000 1,48347 0,82056 0,79616 0,00000 1,61672 0,75846 0,49077 0,00000 1,24923 4,349 48,33
                  DA_duelist duelist_algorithm 0,92782 0,53778 0,27792 1,74352 0,86957 0,47536 0,18193 1,52686 0,62153 0,33569 0,11715 1,07437 4,345 48,28
                  BFO-GA bacterial foraging optimization - ga 0,89150 0,55111 0,31529 1,75790 0,96982 0,39612 0,06305 1,42899 0,72667 0,27500 0,03525 1,03692 4,224 46,93
                  BM blue_monkey _algorithm 0,67301 0,38113 0,26883 1,32297 0,61165 0,30519 0,18691 1,10375 0,43846 0,19354 0,10373 0,73573 3,162 35,14
                  RW random walk 0,48754 0,32159 0,25781 1,06694 0,37554 0,21944 0,15877 0,75375 0,27969 0,14917 0,09847 0,52734 2,348 26,09


                  Conclusões

                  O algoritmo demonstrou ser funcional nas funções de teste, mas seus resultados não foram competitivos o suficiente para justificar sua inclusão na tabela de classificação. Os resultados indicam a necessidade de otimizar e aperfeiçoar ainda mais seus principais mecanismos. A estrutura em grupos permite a diversificação paralela no espaço de busca, enquanto o mecanismo de renovação geracional contribui para atualizar a população. No entanto, a velocidade de convergência para a solução ótima ficou abaixo do esperado. 

                  O algoritmo Blue Monkey apresenta uma abordagem interessante para a resolução de problemas de otimização, baseada na modelagem de processos naturais. Apesar das limitações atuais de desempenho, o algoritmo ainda apresenta potencial para aperfeiçoamento. A versão atual pode ser recomendada para fins educacionais e como base para pesquisas futuras na área de algoritmos meta-heurísticos de otimização.

                  tab

                  Figura 2. Gradação de cores dos algoritmos nos testes correspondentes

                  chart

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

                  Prós e contras do algoritmo BM:

                  Prós:

                  1. Pequena quantidade de parâmetros externos.

                  Desvantagens:

                  1. Baixa eficiência em problemas de otimização, especialmente em funções discretas.

                  Foi anexado ao artigo um arquivo com as versões atualizadas dos códigos dos algoritmos. O autor do artigo não se responsabiliza pela precisão absoluta na descrição dos algoritmos canônicos, pois muitos deles foram modificados para melhorar sua capacidade de busca. As conclusões e avaliações apresentadas nos artigos baseiam-se nos resultados dos experimentos realizados.


                  Programas utilizados no artigo

                  # Nome Tipo Descrição
                  1 #C_AO.mqh
                  Arquivo de inclusão
                  Classe base dos algoritmos populacionais de otimização
                  2 #C_AO_enum.mqh
                  Arquivo de inclusão
                  Enumeração dos algoritmos populacionais de otimização
                  3 TestFunctions.mqh
                  Arquivo de inclusão
                  Biblioteca de funções de teste
                  4
                  TestStandFunctions.mqh
                  Arquivo de inclusão
                  Biblioteca de funções da bancada de testes
                  5
                  Utilities.mqh
                  Arquivo de inclusão
                  Biblioteca de funções auxiliares
                  6
                  CalculationTestResults.mqh
                  Arquivo de inclusão
                  Script para calcular os resultados da tabela comparativa
                  7
                  Testing AOs.mq5
                  Script Bancada de testes unificada 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_BM.mq5
                  Script Bancada de testes para o BM

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

                  Arquivos anexados |
                  BM.zip (279.93 KB)
                  Redes neurais em trading: Treinamento de modelos spiking profundos (Integração de spikes) Redes neurais em trading: Treinamento de modelos spiking profundos (Integração de spikes)
                  O artigo apresenta uma implementação prática, em MQL5, dos principais componentes do framework SEW-ResNet. O uso de arrays dinâmicos e mecanismos spiking permite construir a arquitetura do modelo de forma flexível e processar séries temporais financeiras com eficiência. As soluções propostas mostram como o SEW-ResNet pode otimizar os cálculos e melhorar a extração de atributos relevantes.
                  Desenvolvendo um EA Dinâmico para Múltiplos Pares (Parte 2): Diversificação e Otimização de Portfólio Desenvolvendo um EA Dinâmico para Múltiplos Pares (Parte 2): Diversificação e Otimização de Portfólio
                  A Diversificação e Otimização de Portfólio distribui estrategicamente os investimentos entre múltiplos ativos para minimizar o risco, ao mesmo tempo em que seleciona a combinação ideal de ativos para maximizar os retornos com base em métricas de desempenho ajustadas ao risco.
                  Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
                  Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
                  Como criamos a plataforma de trading mais poderosa com Machine Learning: a crônica da evolução do MQL e do MetaTrader com base em arquivos, fóruns e releases Como criamos a plataforma de trading mais poderosa com Machine Learning: a crônica da evolução do MQL e do MetaTrader com base em arquivos, fóruns e releases
                  Crônica técnica da evolução do MQL: do MQL/MQL II limitado, passando pelo MQL4 procedural, até o MQL5 orientado a objetos, com compilação nativa, APIs avançadas e um ambiente completo de engenharia. São apresentadas as principais funcionalidades da linguagem e as integrações com Python, OpenCL, ONNX, OpenBLAS, bancos de dados e DirectX, além do AI Assistant baseado em agentes e do Model Context Protocol (MCP), que conecta a AI ao terminal, ao MetaEditor, aos dados de mercado, ao trading e às ferramentas de desenvolvimento. Também são analisadas fontes de arquivo sobre a formação da MetaQuotes/MetaTrader, o lançamento do MQL4.COM e do MQL5.COM, os campeonatos, o Algo Forge e sua influência sobre o ecossistema.