Modificação do algoritmo de otimização Dingo: Dingo Optimization Algorithm M (DOAm)
Conteúdo
Introdução
Perguntemo-nos se existem algoritmos metaheurísticos populacionais capazes de alcançar um resultado de 100% com um número limitado de iterações, 10.000, como no nosso caso. Esse algoritmo teria de ser tão poderoso que, por vezes, chego a duvidar da possibilidade de encontrar um algoritmo tão excepcional para otimização. E você, o que acha?
Algoritmos capazes de chegar perto desse objetivo podem existir. Hoje conheceremos um método de otimização único, mais precisamente uma versão do Dingo Optimization Algorithm, analisado no artigo anterior, modificada por mim e que nos aproxima da solução dessa difícil tarefa.
Implementação do algoritmo
Ao trabalhar com o algoritmo Dingo Optimization Algorithm, chamou-me a atenção o princípio usado para modelar a caça dos dingos, que contempla várias estratégias. Já conhecemos esse algoritmo e analisamos detalhadamente seus princípios de funcionamento. Nos testes, entretanto, ele apresentou resultados medianos e não entrou na tabela de classificação, pois faltaram 20% para atingir a pontuação mínima de entrada. À primeira vista, seria natural deixá-lo de lado e seguir adiante. No entanto, resolvi experimentar algumas alterações nas fórmulas, e o resultado foi o seguinte:
Fórmula do ataque em grupo (Equação 2), na versão original dos autores:
x⃗ᵢ(t+1) = β₁ · [Σₖ₌₁ⁿᵃ φ⃗ₖ(t)]/nₐ - x⃗*(t)
Na versão modificada:
a[agentIdx].c[c] = cB[c] + beta1 * sumAttack;
Na fórmula, como se pode observar, foi alterado o sinal: em vez de subtrair a melhor solução, passamos a somá-la. Além disso, o método de cálculo de sumAttack tornou-se mais complexo: agora o resultado é dividido por (na * coords), em vez de apenas por na.
O limite de sobrevivência na versão original era survival[r] <= 0.3; vamos alterá-lo para survival[r] <= 0.01. Essa redução drástica significa que as estratégias de sobrevivência serão aplicadas apenas aos agentes mais "fracos".
Fórmula da busca por carniça (Equação 4), na versão original dos autores:
x⃗ᵢ(t+1) = ½ · e^β₂ · |x⃗ᵣ₁(t) - (-1)^σ · x⃗ᵢ(t)|
Nova versão:
a[agentIdx].c[c] = (MathExp(beta2) * a[r1].c[c] - sign * a[agentIdx].c[c]) / 2.0;
Nesta versão, o módulo (valor absoluto) foi removido, permitindo que as coordenadas assumam valores negativos. Além disso, o algoritmo modificado passa a calcular a média da diferença entre as posições considerando simultaneamente todas as coordenadas, em vez de fazê-lo coordenada por coordenada, como proposto pelos autores.
Por que essas alterações podem melhorar o funcionamento do algoritmo? A mudança do sinal na fórmula do ataque em grupo pode fazer com que os agentes avancem em direção à melhor solução levando em consideração as posições dos demais agentes, em vez de se afastarem dela, como ocorre na formulação original. A redução do limite de sobrevivência permite que a maior parte dos agentes utilize as principais estratégias de caça, recorrendo às estratégias de sobrevivência apenas em situações extremas. Já a remoção do módulo na fórmula da busca por carniça proporciona maior liberdade de movimentação no espaço de busca.

Figura 1. Modificação da estratégia DOAm_dingo
A ilustração acima mostra as principais diferenças entre a versão modificada e a original descrita pelos autores do algoritmo: no ataque em grupo, o movimento ocorre em direção ao líder, em vez de se afastar dele; o limite de sobrevivência foi reduzido para 0.01; o valor absoluto foi removido da fórmula da busca por carniça; e a soma passou a ser dividida por (na × coords), em vez de apenas por na. Para facilitar a visualização, as modificações estão destacadas em vermelho.
Agora vamos analisar o código do algoritmo e conferir os resultados obtidos. A classe "C_AO_DOAm_dingo" é uma versão especializada da classe base "C_AO". Ela implementa um algoritmo de otimização baseado em uma modificação do comportamento dos dingos. Por esse motivo, a classe "Dingo Optimization Algorithm M" herda de "C_AO" e aproveita suas funcionalidades básicas. Os principais parâmetros recebem os seguintes valores padrão:
- popSize - tamanho da população, ou seja, o número de dingos: 50.
- P - probabilidade de escolha entre a caça e a busca por carniça: 0.5.
- Q - probabilidade de escolha entre o ataque em grupo e a perseguição: 0.7.
- Init - inicializa o algoritmo, incluindo os agentes, ou dingos, e seus parâmetros, com base nos intervalos e passos de variação fornecidos.
- Moving - executa a principal fase do algoritmo, na qual os agentes se deslocam e interagem de acordo com as regras estabelecidas.
- Revision - reavalia o estado do algoritmo após determinada etapa ou época.
- P - probabilidade de o dingo caçar ou procurar carniça.
- Q - probabilidade de o dingo participar de um ataque em grupo ou de uma perseguição.
- beta1, beta2 - usadas para gerar vetores aleatórios ou passos durante a otimização, dentro dos intervalos de valores especificados;
- naIni, naEnd - definem o número mínimo e máximo de dingos que podem participar do ataque;
- survival - array que armazena as taxas de sobrevivência ou de aptidão de cada dingo.
- UpdateSurvivalRates - atualiza as taxas de sobrevivência dos agentes;
- GroupAttack - implementa o comportamento de ataque em grupo;
- Persecution - implementa o comportamento de perseguição;
- Scavenger - implementa o comportamento de busca por carniça;
- SurvivalProcedure - executa o procedimento relacionado à sobrevivência dos agentes;
- GetAttackVector - obtém o vetor que define a direção do ataque;
- CalculateAttackSum - calcula o "efeito" total do ataque.
//———————————————————————————————————————————————————————————————————— class C_AO_DOAm_dingo : public C_AO { public: //---------------------------------------------------------- ~C_AO_DOAm_dingo () { } C_AO_DOAm_dingo () { ao_name = "DOAm"; ao_desc = "Dingo Optimization Algorithm M"; ao_link = "https://www.mql5.com/en/articles/19187"; popSize = 50; // population size (number of dingoes) P = 0.5; // Hunting or Scavenger rate Q = 0.7; // Group attack or chase ArrayResize (params, 3); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "P"; params [1].val = P; params [2].name = "Q"; params [2].val = Q; } void SetParams () { popSize = (int)params [0].val; P = params [1].val; Q = params [2].val; } bool Init (const double &rangeMinP [], // minimum values const double &rangeMaxP [], // maximum values const double &rangeStepP [], // step change const int epochsP = 0); // number of epochs void Moving (); void Revision (); //------------------------------------------------------------------ double P; // hunting or scavenging probability double Q; // group attack or chase probability private: //--------------------------------------------------------- double beta1; // -2 < beta1 < 2 double beta2; // -1 < beta2 < 1 int naIni; // minimum number of attacking dingoes int naEnd; // maximum number of attacking dingoes double survival []; // array of survival indicators // Auxiliary methods void UpdateSurvivalRates (); void GroupAttack (int agentIdx, int na); void Persecution (int agentIdx); void Scavenger (int agentIdx); void SurvivalProcedure (int agentIdx); void GetAttackVector (int na, int &attackVector []); double CalculateAttackSum (int agentIdx, int na); }; //————————————————————————————————————————————————————————————————————
O método "Init" inicializa o algoritmo "Dingo Optimization Algorithm M" e o prepara para a execução. Para isso, define as condições iniciais da otimização, estabelece como os dingos formarão os grupos de ataque e configura o estado inicial de suas taxas de sobrevivência.
//———————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_DOAm_dingo::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ // Initialize algorithm parameters naIni = 2; // minimum number of attackers naEnd = popSize / naIni; // maximum number of attackers ArrayResize (survival, popSize); ArrayInitialize (survival, 1.0); return true; } //————————————————————————————————————————————————————————————————————
O método "Moving" é responsável por executar uma etapa, ou iteração, do algoritmo. Ele simula o comportamento da matilha de dingos durante a busca pela solução ótima. O método gerencia dinamicamente o comportamento da população, permitindo que os dingos explorem o espaço de busca por meio de diferentes estratégias, escolhidas com base em suas taxas de sobrevivência e em decisões probabilísticas. Uma descrição mais detalhada dos principais métodos foi apresentada no artigo anterior. Nesta seção, analisaremos com mais atenção os métodos específicos da versão modificada.
//———————————————————————————————————————————————————————————————————— //--- Main step of the algorithm void C_AO_DOAm_dingo::Moving () { // Starting initialization of the population if (!revision) { 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]); } } revision = true; return; } //------------------------------------------------------------------ // Update survival rates UpdateSurvivalRates (); // Update beta parameters for the current iteration //beta1 = -2.0 + 4.0 * u.RNDprobab (); //beta2 = -1.0 + 2.0 * u.RNDprobab (); beta1 = u.RNDfromCI (-2.0, 2.0); beta2 = u.RNDfromCI (-1.0, 1.0); // Main loop for all dingoes for (int r = 0; r < popSize; r++) { // Check the survival rate (Section 2.2.4) //if (survival[r] <= 0.3) if (survival [r] <= 0.01) { // Strategy 4: Survival (Eq. 6) SurvivalProcedure (r); } else { // Choose between hunting and scavenging if (u.RNDprobab () < P) // If hunting { if (u.RNDprobab () < Q) // If group attack { // Strategy 1: Group attack (Eq. 2) int na = naIni + (int)((naEnd - naIni) * u.RNDprobab ()); GroupAttack (r, na); } else // Chase { // Strategy 2: Chase (Eq. 3) Persecution (r); } } else // Scavenging { // Strategy 3: Scavenging (Eq. 4) Scavenger (r); } } } } //————————————————————————————————————————————————————————————————————
O método "GroupAttack" implementa a "Estratégia 1: Ataque em grupo". Essa estratégia é aplicada quando o dingo decide atacar a presa em conjunto com outros agentes. Primeiro, é chamado o método auxiliar "CalculateAttackSum". Ele recebe o índice do dingo atual (agentIdx) e o número de dingos que participam do ataque (na), calculando a força total ou a direção do ataque a partir da soma das contribuições de todos os participantes. O resultado dessa operação é armazenado na variável "sumAttack".
Em seguida, o método atualiza as coordenadas do dingo atual. Para isso, percorre cada coordenada "c" do espaço de busca e calcula seu novo valor. A fórmula atualizada é a seguinte:
Nova coordenada = Coordenada da melhor solução + beta1 * vetor resultante do ataque
em que:
- "cB[c]" representa a "c"-ésima coordenada da melhor solução encontrada até o momento durante a otimização;
- beta1 é o coeficiente que regula a intensidade ou a direção da influência exercida pelo ataque em grupo;
- sumAttack é o resultado calculado na etapa anterior.
O novo valor obtido para a coordenada é então ajustado aos limites permitidos. O método "u.SeInDiSp" garante que esse valor permaneça dentro dos limites mínimo (rangeMin) e máximo (rangeMax) especificados, levando também em consideração o passo de variação (rangeStep).
Assim, o método "GroupAttack" modela a busca coordenada de uma solução ótima por um grupo de dingos. Ele agrega as informações de vários agentes por meio de "sumAttack", considera a melhor solução encontrada, armazenada em "cB", e utiliza o coeficiente aleatório "beta1" para determinar uma nova posição potencialmente mais promissora no espaço de busca.
//———————————————————————————————————————————————————————————————————— //--- Strategy 1: Group attack (Eq. 2) void C_AO_DOAm_dingo::GroupAttack (int agentIdx, int na) { // Calculate the total attack vector double sumAttack = CalculateAttackSum (agentIdx, na); // Apply the group attack formula (Eq. 2) for (int c = 0; c < coords; c++) { // v = beta1 * sumatory - theBestVct a [agentIdx].c [c] = cB [c] + beta1 * sumAttack; a [agentIdx].c [c] = u.SeInDiSp (a [agentIdx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } //————————————————————————————————————————————————————————————————————
O método "CalculateAttackSum" calcula o vetor total de ataque utilizado na "Estratégia 1: Ataque em grupo". Ele determina como um dingo específico (agentIdx) interagirá com os demais agentes do ataque em grupo, cujo número é definido por "na".
Primeiro, o método cria uma lista temporária, "attackVector", que armazenará os índices dos dingos diretamente envolvidos no ataque. O tamanho dessa lista é definido pelo parâmetro "na", que corresponde ao número de atacantes. Em seguida, é chamado o método auxiliar "GetAttackVector". Com base no estado atual da população, esse método seleciona a quantidade de dingos indicada por "na" para participar do ataque e grava seus índices em "attackVector". Depois disso, a variável acumuladora "sumatory" é inicializada com o valor zero. Na sequência, o laço principal percorre todos os dingos envolvidos no ataque. Em cada iteração, o índice do dingo atacante (attackerIdx) é obtido de "attackVector".
Dentro desse laço, há outro laço que percorre todas as coordenadas (c) do espaço de busca, de "0" até "coords - 1". Para cada coordenada, calcula-se a diferença entre a coordenada do dingo atacante e a coordenada do dingo-alvo. Em seguida, essa diferença é dividida pelo número total de possíveis participantes do ataque (na) e pelo número total de coordenadas (coords), sendo então adicionada a "sumatory".
Por fim, o método retorna o valor de "sumatory". Esse valor representa a diferença média entre as posições dos dingos atacantes e a posição do dingo-alvo, ajustada pelo número de participantes e de dimensões. Posteriormente, ele será usado para atualizar a posição do dingo-alvo durante o ataque em grupo.
Assim, "CalculateAttackSum" reúne informações sobre as posições atuais dos dingos que participam do ataque conjunto e calcula uma grandeza agregada que expressa o deslocamento geral deles em relação ao dingo-alvo. Essa grandeza passa então a orientar o movimento do agente.
//———————————————————————————————————————————————————————————————————— //--- Calculate the sum for a group attack double C_AO_DOAm_dingo::CalculateAttackSum (int agentIdx, int na) { // Create a vector of attacking dingoes int attackVector []; ArrayResize (attackVector, na); GetAttackVector (na, attackVector); // Calculate the average difference in positions double sumatory = 0.0; for (int j = 0; j < na; j++) { int attackerIdx = attackVector [j]; // Calculate the average over all coordinates for (int c = 0; c < coords; c++) { sumatory += (a [attackerIdx].c [c] - a [agentIdx].c [c]) / (na * coords); } } return sumatory; } //————————————————————————————————————————————————————————————————————
O método "GetAttackVector" é responsável por formar a lista dos dingos que participarão diretamente do ataque em grupo. Ele gera um conjunto de índices aleatórios, sem duplicatas, cada um correspondente a um dingo da população. Primeiro, o método define o tamanho do array de saída "attackVector" de acordo com o valor de "na", ou seja, o número de atacantes. Também é inicializado com zero o contador "c", que acompanha quantos dingos já foram selecionados para o ataque.
Em seguida, o método entra em um laço que continua até que tenha sido escolhida a quantidade de dingos definida por "na". A cada iteração, é gerado um índice aleatório (idx) correspondente a um dingo da população, no intervalo de "0" a "popSize - 1". Antes de adicionar esse índice à lista de atacantes, o método verifica se ele já foi selecionado. Para isso, são percorridas as posições de "0" até "c - 1" do array "attackVector" para comparar os índices já armazenados. Caso o índice aleatório (idx) coincida com algum dos valores selecionados anteriormente, isto é, "attackVector[i] == idx", o sinalizador "found" recebe o valor verdadeiro. Se o sinalizador permanecer falso, o novo índice (idx), ainda não selecionado, é adicionado ao array "attackVector" na posição atual "c". Em seguida, o contador "c" é incrementado em uma unidade.
Em essência, "GetAttackVector" seleciona aleatoriamente, sem repetição, a quantidade de dingos indicada por "na" entre todos os integrantes da população para formar o grupo de ataque. Esse procedimento impede a repetição de agentes e garante diversidade entre os participantes do grupo.
//———————————————————————————————————————————————————————————————————— //--- Form the attacking dingo vector void C_AO_DOAm_dingo::GetAttackVector (int na, int &attackVector []) { int c = 0; ArrayResize (attackVector, na); while (c < na) { int idx = u.RNDintInRange (0, popSize - 1); // Check that the index is not repeated bool found = false; for (int i = 0; i < c; i++) { if (attackVector [i] == idx) { found = true; break; } } if (!found) { attackVector [c] = idx; c++; } } } //————————————————————————————————————————————————————————————————————
O método "Persecution" implementa a "Estratégia 2: Perseguição". Seu objetivo é atualizar a posição de um dingo específico (agentIdx) para iniciar a perseguição. Primeiro, o método seleciona aleatoriamente um dingo entre todos os integrantes da população, e seu índice é armazenado na variável "r1".
Em seguida, um laço percorre todas as dimensões (c) do espaço em que os dingos se encontram. Para cada dimensão, a nova coordenada do dingo (agentIdx) é calculada pela seguinte fórmula: à coordenada do melhor dingo, representada pelo valor de referência "cB[c]", soma-se o produto de três elementos: "beta1", coeficiente que controla a intensidade da perseguição; o valor retornado por "MathExp(beta2)", isto é, a exponencial do coeficiente "beta2"; e a diferença entre a coordenada do dingo selecionado aleatoriamente, "a[r1].c[c]", e a coordenada atual do dingo, "a[agentIdx].c[c]", nessa dimensão.
A nova coordenada é imediatamente ajustada pela função auxiliar "u.SeInDiSp", que restringe o valor ao intervalo permitido, de "rangeMin[c]" a "rangeMax[c]", considerando também o passo "rangeStep[c]".
Assim, o método "Persecution" faz com que o dingo especificado (agentIdx) se desloque em direção a outro dingo selecionado aleatoriamente (r1), tomando a melhor posição como referência e utilizando os parâmetros "beta1" e "beta2" para regular a intensidade e a dinâmica da perseguição. Ao mesmo tempo, a nova posição permanece estritamente dentro dos limites do espaço de busca.
//———————————————————————————————————————————————————————————————————— //--- Strategy 2: Chase (Eq. 3) void C_AO_DOAm_dingo::Persecution (int agentIdx) { // Select a random dingo int r1 = u.RNDintInRange (0, popSize - 1); // Apply the chase formula (Eq. 3) for (int c = 0; c < coords; c++) { // v = theBestVct + beta1 * exp(beta2) * (Positions[r1] - Positions[r]) a [agentIdx].c [c] = cB [c] + beta1 * MathExp (beta2) * (a [r1].c [c] - a [agentIdx].c [c]); a [agentIdx].c [c] = u.SeInDiSp (a [agentIdx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } //————————————————————————————————————————————————————————————————————
O método "Scavenger" implementa a "Estratégia 3: Busca por carniça". Seu objetivo é atualizar a posição de um dingo específico (agentIdx), deslocando-o para uma nova posição que representa uma espécie de valor intermediário entre sua posição atual e a posição de outro dingo selecionado aleatoriamente.
Primeiro, o método seleciona um dingo aleatório entre todos os integrantes da população, e seu índice é armazenado na variável "r1". Esse dingo passa a servir como ponto de referência. Também é gerado um valor aleatório de sinal (sign), que pode ser "1.0" ou "-1.0". Esse valor determina se a posição atual do dingo será subtraída ou somada durante o cálculo.
Em seguida, um laço percorre todas as dimensões do espaço de busca. Para cada dimensão, a nova coordenada do dingo (agentIdx) é calculada da seguinte forma: a coordenada do dingo selecionado aleatoriamente, "a[r1].c[c]", é multiplicada pela exponencial do coeficiente "beta2". Desse valor, subtrai-se ou soma-se, conforme o valor de "sign", a coordenada atual do dingo. O resultado é então dividido por "2.0".
Na prática, a nova coordenada assume um valor intermediário entre a coordenada atual do dingo e uma coordenada atrativa determinada pelo coeficiente "beta2" e pelo agente selecionado. Assim como nos demais métodos, após o cálculo, o novo valor é imediatamente ajustado pela função auxiliar "u.SeInDiSp". Essa função restringe a coordenada ao intervalo permitido, de "rangeMin[c]" a "rangeMax[c]", considerando também o passo definido para a dimensão.
Desse modo, o método "Scavenger" atualiza a posição do dingo, deslocando-o para um ponto intermediário entre sua posição atual e uma posição definida pela posição de outro dingo escalada pelo fator "exp(beta2)", combinada com um sinal aleatório. Esse comportamento simula a busca por carniça, que pode ser encontrada ao longo do percurso ou em alguma região intermediária entre pontos já conhecidos.
//———————————————————————————————————————————————————————————————————— //--- Strategy 3: Scavenging (Eq. 4) void C_AO_DOAm_dingo::Scavenger (int agentIdx) { // Select a random dingo int r1 = u.RNDintInRange (0, popSize - 1); double sign = u.RNDbool() ? 1.0 : -1.0; // Apply the scavenging formula (Eq. 4) for (int c = 0; c < coords; c++) { // v = (exp(beta2) * Positions[r1] - (-1)^binary * Positions[r]) / 2 a [agentIdx].c [c] = (MathExp (beta2) * a [r1].c [c] - sign * a [agentIdx].c [c]) / 2.0; a [agentIdx].c [c] = u.SeInDiSp (a [agentIdx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } //————————————————————————————————————————————————————————————————————
O método "SurvivalProcedure" implementa a "Estratégia 4: Procedimento de sobrevivência". Seu principal objetivo é atualizar a posição de um dingo específico (agentIdx), utilizando informações de outros dois dingos para encontrar uma nova posição potencialmente mais vantajosa. Primeiro, o método seleciona aleatoriamente dois dingos distintos entre todos os integrantes da população. Seus índices são armazenados nas variáveis "r1" e "r2", garantindo que esses índices sejam diferentes.
Também é gerado um valor aleatório de sinal (sign), que pode ser "1.0" ou "-1.0". Esse valor determina se a coordenada do segundo dingo será subtraída ou somada durante o cálculo. Em seguida, um laço percorre todas as dimensões do espaço de busca. Para cada dimensão, a nova coordenada do dingo é calculada da seguinte forma: à coordenada da melhor solução encontrada, "cB[c]", soma-se metade da diferença entre a coordenada do primeiro dingo aleatório, "a[r1].c[c]", e a coordenada do segundo dingo aleatório, "a[r2].c[c]", cujo sinal pode ser invertido de acordo com o valor de "sign". Após o cálculo, a nova coordenada é obrigatoriamente ajustada pela função auxiliar "SeInDiSp".
De modo geral, o método "SurvivalProcedure" implementa uma estratégia em que o dingo atualiza sua posição tomando como referência a melhor solução encontrada e acrescentando metade da diferença entre as posições de outros dois agentes selecionados aleatoriamente. O sinal aleatório introduz um elemento de imprevisibilidade na escolha da direção desse deslocamento, enquanto o ajuste aos limites garante que o dingo permaneça dentro da área permitida. Esse mecanismo pode ser interpretado como uma tentativa de encontrar uma posição intermediária entre dois pontos potencialmente promissores, sem deixar de considerar a melhor posição conhecida.
//———————————————————————————————————————————————————————————————————— //--- Strategy 4: Survival procedure (Eq. 6) void C_AO_DOAm_dingo::SurvivalProcedure (int agentIdx) { // Select two different random dingoes int r1, r2; do { r1 = u.RNDintInRange (0, popSize - 1); r2 = u.RNDintInRange (0, popSize - 1); } while (r1 == r2); double sign = u.RNDbool() ? 1.0 : -1.0; // Apply the survival formula (Eq. 6) for (int c = 0; c < coords; c++) { // v = theBestVct + (Positions[r1] - (-1)^binary * Positions[r2]) / 2 a [agentIdx].c [c] = cB [c] + (a [r1].c [c] - sign * a [r2].c [c]) / 2.0; a [agentIdx].c [c] = u.SeInDiSp (a [agentIdx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } //————————————————————————————————————————————————————————————————————
O método "UpdateSurvivalRates" é responsável por calcular e atualizar as taxas de sobrevivência de cada dingo da população. Esses valores determinam a probabilidade de o agente ser selecionado para participar da próxima iteração evolutiva.
Primeiro, o método inicializa duas variáveis: "minFitness" e "maxFitness". Em seguida, percorre todos os dingos da população. Para cada agente, seu valor atual de aptidão, "a[i].f", é comparado com os valores atuais de "minFitness" e "maxFitness". Se a aptidão atual for menor que "minFitness", essa variável será atualizada. Se for maior que "maxFitness", o valor de "maxFitness" será atualizado. Ao final do laço, "minFitness" armazenará o menor valor de aptidão de toda a população, enquanto "maxFitness" conterá o maior.
Caso a diferença entre "maxFitness" e "minFitness" seja muito pequena, praticamente igual a zero, o que significa que todos os dingos apresentam a mesma aptidão, será atribuída a todos eles uma taxa de sobrevivência igual a "0.5". Isso evita uma divisão por zero e garante as mesmas chances para todos os agentes.
Se os valores de aptidão forem diferentes, o método percorrerá novamente todos os dingos. Para cada agente, será calculada sua taxa de sobrevivência, "survival[i]". A fórmula utilizada é a seguinte: "(maxFitness - a[i].f) / (maxFitness - minFitness)". Essa expressão normaliza a aptidão do dingo em relação ao intervalo entre os valores mínimo e máximo de aptidão da população.
O dingo com a menor aptidão, próxima de "minFitness", receberá uma taxa de sobrevivência próxima de "1.0", pois a diferença "maxFitness - a[i].f" será grande. Já o dingo com a maior aptidão, próxima de "maxFitness", receberá uma taxa de sobrevivência próxima de "0.0", uma vez que "maxFitness - a[i].f" estará próximo de zero.
Assim, o método "UpdateSurvivalRates" converte os valores absolutos de aptidão dos dingos em taxas de sobrevivência relativas, nas quais valores de aptidão mais elevados correspondem a taxas de sobrevivência menores, e vice-versa. Isso pode ser interpretado como uma estratégia em que os agentes em situação menos favorável, ou seja, com baixa aptidão, têm maior probabilidade de "sobreviver" ou alterar sua posição, enquanto os agentes mais "bem-sucedidos", com alta aptidão, têm menos estímulo para mudar.
//———————————————————————————————————————————————————————————————————— //--- Update survival rates void C_AO_DOAm_dingo::UpdateSurvivalRates () { // Find the minimum and maximum 'fitness' double minFitness = DBL_MAX; double maxFitness = -DBL_MAX; for (int i = 0; i < popSize; i++) { if (a [i].f < minFitness) minFitness = a [i].f; if (a [i].f > maxFitness) maxFitness = a [i].f; } // Calculate survival rates if (MathAbs (maxFitness - minFitness) < DBL_EPSILON) { ArrayInitialize (survival, 0.5); } else { for (int i = 0; i < popSize; i++) { // survival_rate = (max - fit) / (max - min) survival [i] = (maxFitness - a [i].f) / (maxFitness - minFitness); } } } //————————————————————————————————————————————————————————————————————
O método "Revision" concentra-se em identificar o melhor dingo da população atual após a ordenação. Caso esse agente supere o melhor resultado conhecido até então, o método atualiza os indicadores globais da melhor solução. Trata-se de um procedimento padrão em algoritmos de otimização, destinado a preservar e acompanhar a melhor solução encontrada ao longo de todo o processo de busca.
//———————————————————————————————————————————————————————————————————— //--- Update the best and worst solutions void C_AO_DOAm_dingo::Revision () { // Sort the population to find the best and worst static S_AO_Agent aT []; ArrayResize (aT, popSize); u.Sorting (a, aT, popSize); // Update the global best solution if (a [0].f > fB) { fB = a [0].f; ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY); } } //————————————————————————————————————————————————————————————————————
Resultados dos testes
Resultados de 50 execuções, em vez das 10 habituais, para reduzir a influência de resultados excelentes obtidos ao acaso.
DOA|Dingo Optimization Algorithm|50.0|0.5|0.7|
=============================
5 Hilly's; Func runs: 10000; result: 0.4796838225601811
25 Hilly's; Func runs: 10000; result: 0.4536754883347326
500 Hilly's; Func runs: 10000; result: 0.4636906699500103
=============================
5 Forest's; Func runs: 10000; result: 0.9414536230352631
25 Forest's; Func runs: 10000; result: 0.8790978288567921
500 Forest's; Func runs: 10000; result: 0.9145376343407303
=============================
5 Megacity's; Func runs: 10000; result: 0.7861538461538461
25 Megacity's; Func runs: 10000; result: 0.8606153846153848
500 Megacity's; Func runs: 10000; result: 0.8480461538461537
=============================
All score: 6.62695 (73.63%)
Na visualização, observa-se um padrão complexo e variável na área de busca, decorrente da aplicação das diferentes estratégias de caça. Ainda é possível perceber os traços de diagonalidade herdados do algoritmo original. No entanto, apesar dos resultados gerais elevados, há uma variabilidade muito alta em todas as funções de teste.

DOA_dingo na função de teste Hilly

DOA_dingo na função de teste Forest

DOA_dingo na função de teste Megacity
Com base nos resultados dos testes, o algoritmo DOAm_dingo ocupa o primeiro lugar em nossa tabela de classificação dos métodos populacionais de otimização.
| № | AO | Description | Hilly | Hilly Final | Forest | Forest Final | Megacity (discrete) | Megacity Final | Final Result | % of MAX | ||||||
| 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | 10 p (5 F) | 50 p (25 F) | 1000 p (500 F) | ||||||||
| 1 | DOAm_dingo | 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 |
| 1 | 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 |
| 2 | 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 |
| 3 | 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 |
| 4 | (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 |
| 5 | 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 |
| 6 | 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 |
| 7 | 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 |
| 8 | 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 |
| 9 | 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 |
| 10 | 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 |
| 11 | 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 |
| 12 | 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 |
| 13 | 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 |
| 14 | 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 |
| 15 | 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 |
| 16 | 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 |
| 17 | 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 |
| 18 | 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 |
| 19 | 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 |
| 20 | 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 |
| 21 | 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 |
| 22 | 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 |
| 23 | 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 |
| 24 | 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 |
| 25 | 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 |
| 26 | 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 |
| 27 | 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 |
| 28 | 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 |
| 29 | 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 |
| 30 | 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 |
| 31 | 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 |
| 32 | 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 |
| 33 | (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 |
| 34 | 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 |
| 35 | 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 |
| 36 | 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 |
| 37 | 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 |
| 38 | 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 |
| 39 | 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 |
| 40 | 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 |
| 41 | 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 |
| 42 | 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 |
| 43 | 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 |
| 44 | 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 |
| 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
A modificação analisada do algoritmo DOA_dingo destaca-se por sua elevada capacidade de busca. No entanto, é importante considerar que esses resultados expressivos correspondem a valores médios obtidos em uma série de testes. Isso significa que, para garantir diversificação suficiente no espaço de busca, será necessário executar a otimização várias vezes.
Em contrapartida, o algoritmo apresenta uma velocidade de convergência bastante modesta em funções suaves. Ainda assim, o DOA_dingo é um método muito útil e promissor. Eu recomendaria seu uso na etapa inicial da busca, seguido pela aplicação de algoritmos mais precisos. Também é extremamente importante normalizar previamente todos os parâmetros para um mesmo intervalo de valores.

Figura 2. Graduação de cores dos algoritmos pelos testes correspondentes

Figura 3. Histograma dos resultados dos testes dos algoritmos (em uma escala de 0 a 100, quanto maior, melhor, onde 100 é o resultado teórico máximo possível; no arquivo há um script para calcular a tabela de classificação)
Pontos fortes e desvantagens do algoritmo DOAm_dingo:
Pontos fortes:
- Rápido.
- Resultados médios muito elevados.
Desvantagens:
- Alta variabilidade nos resultados.
- Tendência a estagnar.
Está anexado ao artigo um arquivo compactado 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_DOAm_dingo.mq5 | Script | Bancada de testes para o DOAm_dingo |
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/19187
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: sinais de negociação robustos em qualquer regime de mercado (módulos de atenção)
Desenvolvendo uma Estratégia de Pullback: Construção do Modelo Base
Está chegando o novo MetaTrader 5 e MQL5
Desenvolvendo uma estratégia de pullback: Fundamentos
- 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
Foi publicado o artigo “Modificação do Algoritmo de Otimização Dingo — Dingo Optimization Algorithm M (DOAm)”:
Autor: Andrey Dik