English Русский
preview
Modificação do algoritmo de otimização Dingo: Dingo Optimization Algorithm M (DOAm)

Modificação do algoritmo de otimização Dingo: Dingo Optimization Algorithm M (DOAm)

MetaTrader 5Sistemas de negociação |
25 2
Andrey Dik
Andrey Dik

Conteúdo

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


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.

DOA_M

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.
O array "params" é preparado para armazenar os parâmetros e seus respectivos valores, com valores iniciais definidos de acordo com "popSize", "P" e "Q". O método "SetParams" permite atualizar os valores de "popSize", "P" e "Q" a partir de uma fonte externa, por meio do array "params". Os métodos "Init", "Moving" e "Revision" são definidos na classe base "C_AO" e sobrescritos nesta classe para implementar a lógica específica do algoritmo.
  • 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.
Variáveis-membro públicas:
  • 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.
Variáveis-membro privadas:
  • 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.
Métodos auxiliares privados: estes métodos implementam a lógica interna do algoritmo 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.

Hilly

DOA_dingo na função de teste Hilly

Forest

DOA_dingo na função de teste Forest

Megacity

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.

tab

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

chart

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:

  1. Rápido.
  2. Resultados médios muito elevados.

Desvantagens:

  1. Alta variabilidade nos resultados.
  2. 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

Arquivos anexados |
DOAm_Dingo.ZIP (340.25 KB)
Últimos Comentários | Ir para discussão (2)
Yevgeniy Koshtenko
Yevgeniy Koshtenko | 23 set. 2025 em 18:39
Excelente algoritmo! Incrível!
cemal
cemal | 25 jul. 2026 em 10:17
Como esse algoritmo pode ser utilizado para negociação? Você poderia dar alguns exemplos de regressão ou classificação?
Redes neurais em trading: sinais de negociação robustos em qualquer regime de mercado (módulos de atenção) Redes neurais em trading: sinais de negociação robustos em qualquer regime de mercado (módulos de atenção)
Neste artigo, damos continuidade à implementação das abordagens do framework ST-Expert, concentrando-nos nos aspectos práticos de sua aplicação com os recursos do MQL5. Anteriormente, analisamos os fundamentos teóricos e os principais componentes do modelo. Agora, passamos a trabalhar diretamente com os algoritmos de atenção em grafos e de distribuição local e global da atenção. O principal objetivo desta etapa é mostrar como as ideias conceituais do ST-Expert se transformam em soluções funcionais para a análise e a previsão de séries financeiras.
Desenvolvendo uma Estratégia de Pullback: Construção do Modelo Base Desenvolvendo uma Estratégia de Pullback: Construção do Modelo Base
Este artigo apresenta a evolução de um EA de pullback em MQL5, desde a definição do regime por EMA até a identificação da retração e a confirmação do gatilho. São abordados a otimização integrada ao MetaTrader 5, o stop ajustado ao tick size, o filtro de distância por ATR e a gestão com breakeven e trailing, priorizando platôs paramétricos, controle dos graus de liberdade e redução do risco de overfitting.
Está chegando o novo MetaTrader 5 e MQL5 Está chegando o novo MetaTrader 5 e MQL5
Esta é apenas uma breve resenha do MetaTrader 5. Eu não posso descrever todos os novos recursos do sistema por um período tão curto de tempo - os testes começaram em 09.09.2009. Esta é uma data simbólica, e tenho certeza que será um número de sorte. Alguns dias passaram-se desde que eu obtive a versão beta do terminal MetaTrader 5 e MQL5. Eu ainda não consegui testar todos os seus recursos, mas já estou impressionado.
Desenvolvendo uma estratégia de pullback: Fundamentos Desenvolvendo uma estratégia de pullback: Fundamentos
O artigo apresenta os fundamentos de uma estratégia de pullback a partir de quatro pilares: regime, retração, gatilho e gestão. A proposta é mostrar como essa lógica pode sair da leitura visual do mercado e começar a ser transformada em regras objetivas no MetaTrader 5, por meio de um EA simples e inicial. A partir desse primeiro passo, o leitor acompanha como uma ideia de trading começa a ganhar estrutura, permitindo testar hipóteses, observar resultados e preparar a evolução da estratégia de forma progressiva.