Algoritmo dos Macacos-Azuis — Blue Monkey (BM) Algorithm
Conteúdo
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.

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:
- 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
- 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
- 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)
- 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):
- 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
- 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
- Para cada grupo de adultos:
- 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
- Para cada macaco do grupo:
- 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
- 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
- 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
- Se for encontrada uma solução melhor que o recorde atual:
- 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:
- 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.
- numGroups - número de grupos em que a população é dividida.
- childrenRatio - percentual da população representado pelos "filhotes" (novos indivíduos).
- 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).
- 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:
- 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).
- 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.
- Se o filhote for melhor:
- 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.
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.
- 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.
//———————————————————————————————————————————————————————————————————— 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.

BM na função de teste Hilly

BM na função de teste Forest

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.

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

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:
- Pequena quantidade de parâmetros externos.
Desvantagens:
- 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
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Redes neurais em trading: Treinamento de modelos spiking profundos (Integração de spikes)
Desenvolvendo um EA Dinâmico para Múltiplos Pares (Parte 2): Diversificação e Otimização de Portfólio
Está chegando o novo MetaTrader 5 e MQL5
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
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso