Русский
preview
Otimização em estilo Battle Royale — Battle Royale Optimizer (BRO)

Otimização em estilo Battle Royale — Battle Royale Optimizer (BRO)

MetaTrader 5Testador |
20 0
Andrey Dik
Andrey Dik

Conteúdo

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


Introdução

Na otimização meta-heurística, onde os algoritmos frequentemente se inspiram em processos naturais, fenômenos físicos e mecanismos evolutivos, surgiu uma nova e surpreendente fonte de inspiração, os jogos eletrônicos. O Battle Royale Optimizer (BRO), desenvolvido por Taymaz Rahkar Farshi, representa um algoritmo de otimização inovador baseado na mecânica dos populares jogos do gênero "Battle Royale", como o PlayerUnknown's Battlegrounds (PUBG).

O algoritmo BRO inaugura uma nova categoria de métodos de otimização — os "game-based" (baseados em jogos) — complementando o já consolidado trio de abordagens de otimização, que inclui os algoritmos evolucionários, os algoritmos de inteligência de enxame e os algoritmos baseados em fenômenos físicos, todos pertencentes ao vasto grupo dos algoritmos populacionais de otimização. Diferente dos algoritmos de inteligência de enxame, onde os agentes cooperam para alcançar um objetivo comum, no BRO os indivíduos competem entre si, buscando sobreviver e ocupar a melhor posição no espaço de busca.

A principal característica do BRO é o seu mecanismo singular de competição e “dano” entre soluções. Cada solução é comparada com seu vizinho mais próximo, e a que perde recebe um “dano”, enquanto a vencedora recomeça do zero. As soluções que acumulam dano excessivo são removidas da população e substituídas por novas soluções geradas aleatoriamente, de forma análoga à eliminação de jogadores no PUBG após sofrerem dano crítico. Esse processo garante o mecanismo de exploração do espaço de busca.


Implementação do algoritmo

O algoritmo Battle Royale Optimizer (BRO) pode ser visualizado como um mundo virtual, onde vários jogadores são lançados em um campo de batalha e apenas um deve permanecer vivo, este é o princípio essencial do jogo que serviu de inspiração. Agora, vamos transferir esse conceito para a resolução de problemas de otimização.

No início da execução do algoritmo, é criada uma população de soluções distribuídas aleatoriamente pelo espaço de busca. Cada solução representa um tipo de “jogador” que possui uma posição específica e uma qualidade associada (fitness) a essa posição. Em seguida, tem início o ciclo principal de competições, no qual cada solução é comparada com seu vizinho mais próximo, de forma semelhante a como os jogadores em uma batalha se enfrentam diretamente.

Quando duas soluções “se encontram”, elas são comparadas de acordo com sua qualidade. A melhor solução é declarada vencedora e recebe zero de dano, enquanto a pior torna-se a perdedora e acumula um ponto de dano. Esse contador de dano é um elemento fundamental do algoritmo. A solução perdedora não apenas sofre dano, mas também tenta melhorar sua posição, movendo-se na direção da melhor solução conhecida na população. Esse movimento simula o instinto de sobrevivência, buscando um local mais seguro e vantajoso.

Se uma solução acumular dano em excesso (ultrapassando um limite predefinido), ela é “eliminada do jogo”, ou seja, removida da população e substituída por uma nova solução gerada aleatoriamente. Isso se assemelha à eliminação de um jogador em uma partida battle royale e à entrada de um novo competidor no próximo confronto. Esse mecanismo garante a renovação constante da população e mantém a diversidade entre as soluções.

Periodicamente, o algoritmo realiza o estreitamento do espaço de busca, um análogo à zona de jogo que se reduz nas partidas battle royale, forçando os jogadores a se aproximarem. As fronteiras do espaço de busca se contraem em torno da melhor solução encontrada, o que leva a população a se concentrar nas regiões mais promissoras.

Graças a essa abordagem, o algoritmo BRO mantém um equilíbrio entre a exploração de novas áreas e o aproveitamento das soluções já descobertas. As soluções perdedoras se movem em direção às melhores, preservando a tendência de melhoria, enquanto as que são completamente eliminadas são substituídas por novas, proporcionando uma nova perspectiva sobre o espaço de busca. Ao mesmo tempo, o estreitamento periódico das fronteiras intensifica a busca local ao redor das soluções mais promissoras.

bro-algorithm

Figura 1. Ilustração do funcionamento do algoritmo BRO.

Esta ilustração mostra os principais componentes do funcionamento do algoritmo Battle Royale Optimizer (BRO). O espaço de busca é representado como uma área 2D com contornos que simbolizam a função de otimização (as regiões mais brilhantes representam as melhores soluções). A melhor solução global é marcada por uma estrela vermelha no centro do pico mais alto. As soluções vencedoras são marcadas por pontos verdes — representam soluções com zero dano (aquelas que venceram suas comparações com os vizinhos). As soluções perdedoras são indicadas por pontos amarelos (com 1 dano) e laranja (com 2 danos). As novas soluções geradas aleatoriamente são mostradas como pontos azuis, surgindo quando uma solução acumula dano em excesso. As soluções perdedoras movem-se em direção à melhor solução (representadas por setas tracejadas). O estreitamento do espaço de busca é ilustrado por uma moldura tracejada laranja, centralizada na melhor solução.

As etapas fundamentais do algoritmo são: inicialização, comparação com os vizinhos, movimento em direção à melhor solução e estreitamento do espaço de busca.

    As soluções no algoritmo BRO competem entre si, e as perdedoras recebem “dano”. As soluções que acumulam dano em excesso são substituídas por novas soluções geradas aleatoriamente. Agora que o princípio de funcionamento do algoritmo está claro, podemos passar à escrita do pseudocódigo.

    Inicialização:

    1. Criar uma população de soluções de tamanho popSize
    2. Para cada solução, definir o contador de dano como 0
    3. Definir o limite máximo de dano maxDamage
    4. Definir o número de épocas epochs
    5. Calcular o valor inicial de delta para o estreitamento periódico do espaço de busca

    Algoritmo principal:

    1. Criação da população inicial:
      • Para cada solução na população:
        • Gerar coordenadas aleatórias em um determinado espaço de busca
    2. Para cada época, executar:
      • Atualizar a melhor solução global, se for encontrada uma melhor
      • Realização de “disputas” entre decisões:
        • Para cada solução na população:
          • Encontrar o vizinho mais próximo (a solução com a menor distância)
          • Comparar a qualidade da solução atual com a de seu vizinho:
            • Se a solução atual for melhor:
              • Zerar o contador de dano da solução atual
              • Aumentar o contador de dano do vizinho
              • A solução perdedora (o vizinho) move-se em direção à melhor solução
            • Caso contrário:
              • Aumentar o contador de dano da solução atual
              • Zerar o contador de dano do vizinho
              • A solução perdedora (a atual) move-se em direção à melhor solução
      • Tratamento das soluções excessivamente danificadas:
        • Para cada solução na população:
          • Se o contador de dano ≥ maxDamage:
            • Zerar o contador de dano
            • Substituir a solução por uma nova gerada aleatoriamente
      • Estreitamento periódico do espaço de busca:
        • Se o número da época atual for divisível por delta:
          • Calcular o desvio padrão das coordenadas de toda a população
          • Reduzir o espaço de busca em torno da melhor solução
          • Atualizar o valor de delta
    3. Retornar a melhor solução encontrada

    O algoritmo utiliza as seguintes fórmulas:

    • Cálculo do valor inicial de delta para o estreitamento do espaço de busca: delta = ⌊epochs / log₁₀(epochs)⌋
    • Cálculo da distância euclidiana entre soluções: distance = √(∑(a[idx1][c] - a[idx2][c])²)
    • Movimento da solução perdedora em direção à melhor solução global: a[i][c] = a[i][c] + r × (cB[c] - a[i][c])
    • Cálculo do valor médio para cada coordenada: mean[c] = (∑a[i][c]) / popSize
    • Cálculo do desvio padrão para cada coordenada: sdValues[c] = √(∑(a[i][c] - mean[c])² / popSize)
    • Fórmulas para o estreitamento do espaço de busca: newMin[c] = cB[c] - sdValues[c] newMax[c] = cB[c] + sdValues[c]
    • Atualização do parâmetro delta após o estreitamento do espaço de busca: delta = delta + ⌊delta / 2⌉

    Para o estreitamento periódico do espaço de busca, o autor propõe a seguinte fórmula: Δ (delta) = maxEpochs / log₁₀(maxEpochs), cujo gráfico é apresentado abaixo:

    func

    Figura 2. Gráfico da função de dependência do parâmetro delta em relação ao número de épocas

    O gráfico da função delta = epochs/log₁₀(epochs) tem grande importância no funcionamento do algoritmo BRO, pois determina a quantidade de iterações após as quais ocorrerá o estreitamento do espaço de busca. Como se pode observar no gráfico, o valor de delta aumenta com o número de épocas, mas não tão rapidamente quanto as próprias épocas, devido à divisão pelo logaritmo. Isso cria uma dependência não linear, que oferece as seguintes vantagens: nas etapas iniciais da otimização (quando o número de épocas é pequeno), o estreitamento ocorre com maior frequência, ajudando o algoritmo a se concentrar mais rapidamente nas regiões promissoras, já nas etapas posteriores (com número elevado de épocas), o estreitamento ocorre com menor frequência, permitindo uma exploração mais detalhada das regiões já identificadas como favoráveis.

    Durante meus experimentos, modifiquei a fórmula de dependência do parâmetro delta, aplicando o logaritmo duas vezes, e o algoritmo apresentou desempenho superior.

    // Вычисление начального delta для сужения пространства поиска
      delta = (int)MathFloor(epochs / MathLog(MathLog(epochs)));

    Vamos agora à implementação do código, criando a classe "C_AO_BRO", que herda da classe base "C_AO", ou seja, herda todos os membros públicos e protegidos da classe "C_AO" e pode redefinir seu comportamento. Essa classe representará a implementação do algoritmo de otimização baseado no conceito de "Battle Royale".

    1. Membros públicos da classe:

    • popSize — define o tamanho da população.
    • maxDamage — define o limite máximo de dano, ou seja, quantas “derrotas” uma solução pode sofrer antes de ser eliminada.
    • SetParams() — método que atualiza os valores de "popSize" e "maxDamage" com base nos valores armazenados no array "params", permitindo ajustar os parâmetros do algoritmo durante a execução.
    • Init() — método de inicialização do algoritmo. Recebe os seguintes parâmetros:
      • rangeMinP[] — valores mínimos do intervalo de busca para cada variável.
      • rangeMaxP[] — valores máximos do intervalo de busca.
      • rangeStepP[] — passo de busca para cada variável.
      • epochsP — número de épocas (iterações) do algoritmo. O valor padrão é 0.
    • Moving() — método que implementa a lógica principal de movimento ou atualização das soluções no espaço de busca.
    • Revision() — método que implementa a lógica de revisão das soluções atuais, realizando a avaliação dos “danos” recebidos por cada solução.
    • maxDamage — membro público que armazena o limite máximo de dano.

    2. Campos privados da classe:

    • delta — intervalo para o estreitamento (shrink) do espaço de busca. É usado para adaptar o tamanho do passo de busca durante o processo de otimização.
    • damages[] — array que armazena a quantidade de “danos” recebidos por cada solução na população. 
    • epoch — época atual (número da iteração) do algoritmo.
    • epochs — número máximo de épocas (iterações) do algoritmo.

    3. Métodos auxiliares:

    • FindNearestNeighbor() — encontra o vizinho mais próximo de uma solução com base em seu índice, sendo usado para a interação entre soluções.
    • CalculateDistance() — calcula a distância entre duas soluções identificadas por seus respectivos índices.
    • CalculateStandardDeviations() — calcula o desvio padrão dos valores das soluções da população, usado para avaliar a diversidade da população e adaptar os parâmetros de busca.
    • ShrinkSearchSpace() — método que realiza o estreitamento do espaço de busca. Trata-se de uma técnica padrão para auxiliar a convergência do algoritmo em direção à solução ótima.

    Visão geral:

    C_AO_BRO representa a classe do algoritmo de otimização Battle Royale, cuja ideia central, em resumo, é a seguinte:

    1. Inicialização: cria-se uma população de soluções aleatórias dentro de um espaço de busca definido.
    2. Avaliação: cada solução é avaliada por meio de uma função objetivo (fitness function).
    3. Battle Royale: as soluções “batalham” entre si (são comparadas com base nos valores da função objetivo).
    4. Dano: algumas soluções recebem “danos”, de acordo com os resultados das “batalhas”.
    5. Eliminação: soluções que acumulam um número de “damage” superior a “maxDamage” são removidas da população.
    6. Reprodução/substituição: as soluções removidas são substituídas por novas soluções aleatórias.
    7. Estreitamento do espaço de busca: o espaço de busca é reduzido para concentrar a busca nas regiões mais promissoras.
    8. Repetição: os passos 2 a 7 se repetem durante o número de épocas definido.
    //——————————————————————————————————————————————————————————————————————————————
    class C_AO_BRO : public C_AO
    {
      public: //--------------------------------------------------------------------
      ~C_AO_BRO () { }
      C_AO_BRO ()
      {
        ao_name = "BRO";
        ao_desc = "Battle Royale Optimizer";
        ao_link = "https://www.mql5.com/ru/articles/17688";
    
        popSize   = 100;    // размер популяции
        maxDamage = 3;      // максимальный порог повреждений
    
        ArrayResize (params, 2);
    
        params [0].name = "popSize";   params [0].val = popSize;
        params [1].name = "maxDamage"; params [1].val = maxDamage;
      }
    
      void SetParams ()
      {
        popSize   = (int)params [0].val;
        maxDamage = (int)params [1].val;
      }
    
      bool Init (const double &rangeMinP [],  // минимальный диапазон поиска
                 const double &rangeMaxP [],  // максимальный диапазон поиска
                 const double &rangeStepP [], // шаг поиска
                 const int     epochsP = 0);  // количество эпох
    
      void Moving ();
      void Revision ();
    
      //----------------------------------------------------------------------------
      int maxDamage;    // максимальный порог повреждений
    
      private: //-------------------------------------------------------------------
      int    delta;      // интервал для сужения пространства поиска
      int    damages []; // количество повреждений для каждого решения
      int    epoch;      // текущая эпоха
      int    epochs;     // максимальное количество эпох
    
      // Вспомогательные методы
      int    FindNearestNeighbor (int index);
      double CalculateDistance (int idx1, int idx2);
      void   CalculateStandardDeviations (double &sdValues []);
      void   ShrinkSearchSpace ();
    };
    //——————————————————————————————————————————————————————————————————————————————

    O método Init() inicializa o algoritmo BRO, chamando StandardInit() para realizar a inicialização padrão, utilizando os intervalos e passos de busca fornecidos. Se StandardInit retornar “false”, o método Init() também retorna “false”, indicando uma falha na inicialização. Ele inicializa o array damages, alocando memória para cada solução da população (popSize) e define o número inicial de “danos” de todas as soluções como 0. Define o número total de épocas (epochs) e zera a época atual (epoch = 0).

    O valor de delta é calculado com base no número total de épocas, de modo que o espaço de busca seja estreitado gradualmente. Caso delta seja menor ou igual a 0, seu valor é ajustado para 1. Em resumo, esse método prepara o algoritmo para execução, inicializando seus principais parâmetros e estruturas de dados.

    //——————————————————————————————————————————————————————————————————————————————
    bool C_AO_BRO::Init (const double &rangeMinP  [],  // минимальный диапазон поиска
                         const double &rangeMaxP  [],  // максимальный диапазон поиска
                         const double &rangeStepP [],  // шаг поиска
                         const int     epochsP = 0)    // количество эпох
    {
      if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
    
      //----------------------------------------------------------------------------
      // Инициализация счетчиков повреждений для каждого решения
      ArrayResize (damages, popSize);
      ArrayInitialize (damages, 0);
    
      // Установка эпох
      epochs = epochsP;
      epoch  = 0;
    
      // Вычисление начального delta для сужения пространства поиска
      delta = (int)MathFloor (epochs / MathLog10 (epochs));
      if (delta <= 0) delta = 1;
    
      return true;
    }
    //——————————————————————————————————————————————————————————————————————————————

    O método Moving() implementa a lógica de inicialização da população de soluções, em que cada coordenada de cada solução é gerada aleatoriamente entre os limites mínimo e máximo definidos por rangeMin e rangeMax, sendo discretizada de acordo com o passo rangeStep. Esse método garante que a população seja inicializada apenas uma vez. 

    /——————————————————————————————————————————————————————————————————————————————
    void C_AO_BRO::Moving ()
    {
      if (!revision)
      {
        // Инициализация популяции случайными решениями
        for (int i = 0; i < popSize; i++)
        {
          for (int c = 0; c < coords; c++)
          {
            double coordinate = u.RNDfromCI (rangeMin [c], rangeMax [c]);
            a [i].c [c] = u.SeInDiSp (coordinate, rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
    
        revision = true;
      }
    }
    //——————————————————————————————————————————————————————————————————————————————

    O método Revision() é uma etapa fundamental no algoritmo de otimização BRO. A cada iteração, ele atualiza a melhor solução: se alguma solução da população atual for melhor que a solução global até então, essa melhor solução global é atualizada.

    O método compara as soluções com seus vizinhos: para cada solução na população, é identificado o vizinho mais próximo. Em seguida, os valores da função objetivo são comparados. A melhor solução do par é “recompensada” com a redefinição de seu contador de dano, enquanto o contador da solução pior é incrementado. A solução com pior desempenho na comparação é deslocada em direção à melhor solução global.

    Em seguida, as soluções “danificadas” são substituídas: se uma solução acumular dano suficiente (atingindo o valor máximo definido por maxDamage), ela é substituída por uma nova solução gerada aleatoriamente. Periodicamente, de acordo com o valor da variável delta, ocorre o estreitamento da área de busca. Esse processo se repete ao longo de várias iterações do algoritmo. Através das comparações com vizinhos, as soluções se deslocam progressivamente em direção a regiões mais vantajosas do espaço de busca.

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_BRO::Revision ()
    {
      epoch++;
    
      // Обновление глобального наилучшего решения
      for (int i = 0; i < popSize; i++)
      {
        if (a [i].f > fB)
        {
          fB = a [i].f;
          ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);
        }
      }
    
      // Сравнение каждого решения с его ближайшим соседом и обновление счетчиков повреждений
      for (int i = 0; i < popSize; i++)
      {
        int neighbor = FindNearestNeighbor (i);
    
        if (neighbor != -1)
        {
          if (a [i].f >= a [neighbor].f)
          {
            // Решение i побеждает
            damages [i] = 0;
            damages [neighbor]++;
    
            // Проигравший (сосед) движется к наилучшему решению
            for (int c = 0; c < coords; c++)
            {
              double r = u.RNDfromCI (0, 1);
              a [neighbor].c [c] = a [neighbor].c [c] + r * (cB [c] - a [neighbor].c [c]);
              a [neighbor].c [c] = u.SeInDiSp (a [neighbor].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
            }
          }
          else
          {
            // Решение i проигрывает
            damages [i]++;
            damages [neighbor] = 0;
    
            // Проигравший (i) движется к наилучшему решению
            for (int c = 0; c < coords; c++)
            {
              double r = u.RNDfromCI (0, 1);
              a [i].c [c] = a [i].c [c] + r * (cB [c] - a [i].c [c]);
              a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
            }
          }
        }
      }
    
      // Проверка, достигло ли какое-либо решение максимального повреждения, и его замена
      for (int i = 0; i < popSize; i++)
      {
        if (damages [i] >= maxDamage)
        {
          // Сброс счетчика повреждений
          damages [i] = 0;
    
          // Генерация нового случайного решения
          for (int c = 0; c < coords; c++)
          {
            double coordinate = u.RNDfromCI (rangeMin [c], rangeMax [c]);
            a [i].c [c] = u.SeInDiSp (coordinate, rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
      }
    
      // Периодическое сужение пространства поиска
      if (epochs > 0 && epoch % delta == 0)
      {
        ShrinkSearchSpace ();
        // Обновление delta
        delta = delta + (int)MathRound (delta / 2);
      }
    }
    //——————————————————————————————————————————————————————————————————————————————

    O método FindNearestNeighbor() encontra o índice do vizinho mais próximo para uma solução de índice index dentro da população. Ele percorre todas as soluções, calcula a distância até cada uma delas (exceto até si própria, representada por index) e retorna o índice da solução com a menor distância. Se não for possível encontrar um vizinho (por exemplo, se existir apenas uma solução na população), o método retorna -1. Em resumo, esse método identifica o vizinho mais próximo para uma solução específica.

    //——————————————————————————————————————————————————————————————————————————————
    int C_AO_BRO::FindNearestNeighbor (int index)
    {
      double minDistance = DBL_MAX;
      int nearestIndex = -1;
    
      for (int i = 0; i < popSize; i++)
      {
        if (i == index) continue;
    
        double distance = CalculateDistance (index, i);
        if (distance < minDistance)
        {
          minDistance = distance;
          nearestIndex = i;
        }
      }
    
      return nearestIndex;
    }
    //——————————————————————————————————————————————————————————————————————————————

    O método CalculateDistance() calcula a distância euclidiana entre duas soluções da população, identificadas pelos índices idx1 e idx2. O processo inicia com a variável distanceSum igual a zero. Essa variável acumula a soma dos quadrados das diferenças entre as coordenadas correspondentes das duas soluções. O loop for percorre todas as coordenadas das soluções. A cada iteração, calcula a diferença entre as coordenadas de idx1 e idx2. O quadrado dessa diferença é adicionado a distanceSum.

    Após a conclusão do loop, o método retorna a raiz quadrada de distanceSum, que representa a distância euclidiana entre as duas soluções. Assim, o método retorna um valor numérico que reflete a “distância” entre duas soluções no espaço de busca. Quanto maior esse valor, mais distantes entre si estão as soluções.

    //——————————————————————————————————————————————————————————————————————————————
    double C_AO_BRO::CalculateDistance (int idx1, int idx2)
    {
      double distanceSum = 0.0;
    
      for (int c = 0; c < coords; c++)
      {
        double diff = a [idx1].c [c] - a [idx2].c [c];
        distanceSum += diff * diff;
      }
    
      return MathSqrt (distanceSum);
    }
    //——————————————————————————————————————————————————————————————————————————————

    O método CalculateStandardDeviations() calcula o desvio padrão de cada coordenada das soluções da população e armazena os resultados no array sdValues. O tamanho do array de entrada sdValues é ajustado para comportar o valor do desvio padrão de cada uma das coordenadas (coords). Em seguida, o loop percorre cada coordenada das soluções e realiza o cálculo do desvio padrão. O método zera a soma dos quadrados dos desvios para a coordenada atual e também reinicia seu valor médio. O loop soma os valores da coordenada “c” de todas as soluções da população. Em seguida, calcula o valor médio dessa coordenada. 

    Para calcular a soma dos quadrados dos desvios, o loop percorre todas as soluções da população e computa a soma dos quadrados das diferenças em relação ao valor médio da coordenada atual. Ele calcula a diferença entre o valor da coordenada “c” da solução “i” e o valor médio dessa coordenada. Adiciona o quadrado dessa diferença à soma total dos desvios. O desvio padrão é então obtido como a raiz quadrada da soma dos quadrados dos desvios dividida pelo tamanho da população. O resultado é armazenado no elemento correspondente do array sdValues.

    Assim, o método calcula uma medida de dispersão dos valores de cada coordenada das soluções na população e armazena esses valores no array sdValues. O desvio padrão mostra o quanto os valores de uma determinada coordenada variam em torno do valor médio.

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_BRO::CalculateStandardDeviations (double &sdValues [])
    {
      ArrayResize (sdValues, coords);
    
      for (int c = 0; c < coords; c++)
      {
        double sum = 0.0;
        double mean = 0.0;
    
        // Вычисление среднего
        for (int i = 0; i < popSize; i++) mean += a [i].c [c];
    
        mean /= popSize;
    
        // Вычисление суммы квадратов отклонений
        for (int i = 0; i < popSize; i++)
        {
          double diff = a [i].c [c] - mean;
          sum += diff * diff;
        }
    
        sdValues [c] = MathSqrt (sum / popSize);
      }
    }
    //——————————————————————————————————————————————————————————————————————————————

    O método ShrinkSearchSpace() realiza o estreitamento do espaço de busca com base nos desvios padrão das coordenadas e na posição da melhor solução encontrada. Ele atua como uma forma de concentrar a busca na região mais promissora, onde já foi identificado um bom resultado.

    Primeiramente, são calculados os desvios padrão por meio do método CalculateStandardDeviations(), que avalia o grau de variação de cada coordenada das soluções da população e armazena os valores obtidos em sdValues. O cálculo das novas fronteiras do espaço de busca é então realizado: essas fronteiras são centralizadas em torno da melhor solução encontrada, e sua largura é determinada pelo desvio padrão. Se o desvio padrão for pequeno, o espaço de busca é estreitado ao redor da melhor solução. Se for grande, a busca permanece mais ampla. É feita uma verificação de validade para garantir que o novo espaço de busca não ultrapasse os limites originalmente definidos como permitidos.

    //——————————————————————————————————————————————————————————————————————————————
    void C_AO_BRO::ShrinkSearchSpace ()
    {
      double sdValues [];
      CalculateStandardDeviations (sdValues);
    
      for (int c = 0; c < coords; c++)
      {
        // Новые границы центрированы вокруг наилучшего решения с шириной стандартного отклонения
        double newMin = cB [c] - sdValues [c];
        double newMax = cB [c] + sdValues [c];
    
        // Убедитесь, что новые границы находятся в пределах исходных ограничений
        if (newMin < rangeMin [c]) newMin = rangeMin [c];
        if (newMax > rangeMax [c]) newMax = rangeMax [c];
    
        // Обновление границ
        rangeMin [c] = newMin;
        rangeMax [c] = newMax;
      }
    }
    //——————————————————————————————————————————————————————————————————————————————


    Resultados dos testes

    Após a realização dos testes, observou-se que o algoritmo apresentou um bom desempenho nas funções Hilly e Forest. No entanto, em relação à função discreta Megacity, os indicadores de convergência mostraram-se significativamente mais fracos.

    BRO|Battle Royale Optimizer|50.0|3.0|
    =============================
    5 Hilly's; Func runs: 10000; result: 0.7494493002235458
    25 Hilly's; Func runs: 10000; result: 0.4983307394255448
    500 Hilly's; Func runs: 10000; result: 0.27994639979348446
    =============================
    5 Forest's; Func runs: 10000; result: 0.6962444245506945
    25 Forest's; Func runs: 10000; result: 0.3845619185097379
    500 Forest's; Func runs: 10000; result: 0.20427058729050862
    =============================
    5 Megacity's; Func runs: 10000; result: 0.3815384615384616
    25 Megacity's; Func runs: 10000; result: 0.21107692307692308
    500 Megacity's; Func runs: 10000; result: 0.10607692307692404
    =============================
    All score: 3.51150 (39.02%)

    Na visualização, é possível observar a dispersão dos valores dos resultados e as capacidades de busca mais fracas na última função discreta, Megacity.

    Hilly

    BRO na função de teste Hilly

    Forest

    BRO na função de teste Forest

    Megacity

    BRO na função de teste Megacity

    Com base nos resultados dos testes, o algoritmo BRO ocupa a última posição no ranking dos algoritmos 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)
    1ANSacross neighbourhood search0,949480,847760,438572,235811,000000,923340,399882,323230,709230,634770,230911,574916,13468,15
    2CLAcode lock algorithm (joo)0,953450,871070,375902,200420,989420,917090,316422,222940,796920,693850,193031,683806,10767,86
    3AMOmanimal migration ptimization M0,903580,843170,462842,209590,990010,924360,465982,380340,567690,591320,237731,396755,98766,52
    4(P+O)ES(P+O) evolution strategies0,922560,881010,400212,203790,977500,874900,319452,171850,673850,629850,186341,490035,86665,17
    5CTAcomet tail algorithm (joo)0,953460,863190,277702,094350,997940,857400,339492,194840,887690,564310,105121,557125,84664,96
    6TETAtime evolution travel algorithm (joo)0,913620,823490,319902,057010,970960,895320,293242,159520,734620,685690,160211,580525,79764,41
    7SDSmstochastic diffusion search M0,930660,854450,394762,179880,999830,892440,196192,088460,723330,611000,106701,441035,70963,44
    8BOAmbilliards optimization algorithm M0,957570,825990,252352,035901,000000,900360,305022,205380,735380,525230,095631,356255,59862,19
    9AAmarchery algorithm M0,917440,708760,421602,047800,925270,758020,353282,036570,673850,552000,237381,463235,54861,64
    10ESGevolution of social groups (joo)0,999060,796540,350562,146161,000000,828630,131021,959650,823330,553000,047251,423585,52961,44
    11SIAsimulated isotropic annealing (joo)0,957840,842640,414652,215130,982390,795860,205071,983320,686670,493000,090531,270205,46960,76
    12ACSartificial cooperative search0,755470,747440,304071,806981,000000,888610,224132,112740,690770,481850,133221,305835,22658,06
    13DAdialectical algorithm0,861830,700330,337241,899400,981630,727720,287181,996530,703080,452920,163671,319675,21657,95
    14BHAmblack hole algorithm M0,752360,766750,345831,864930,935930,801520,271772,009230,650770,516460,154721,321955,19657,73
    15ASOanarchy society optimization0,848720,746460,314651,909830,961480,791500,238031,991010,570770,540620,166141,277525,17857,54
    16RFOroyal flush optimization (joo)0,833610,737420,346291,917330,894240,738240,240981,873460,631540,502920,164211,298675,08956,55
    17AOSmatomic orbital search M0,802320,704490,310211,817020,856600,694510,219961,771070,746150,528620,143581,418355,00655,63
    18TSEAturtle shell evolution algorithm (joo)0,967980,644800,296721,909490,994490,619810,227081,841390,690770,426460,135981,253225,00455,60
    19DEdifferential evolution0,950440,616740,303081,870260,953170,788960,166521,908650,786670,360330,029531,176534,95555,06
    20SRAsuccessful restaurateur algorithm (joo)0,968830,634550,292171,895550,946370,555060,191241,692670,749230,440310,125261,314804,90354,48
    21CROchemical reaction optimisation0,946290,661120,298531,905930,879060,584220,211461,674730,758460,426460,126861,311784,89254,36
    22BIOblood inheritance optimization (joo)0,815680,653360,308771,777810,899370,653190,217601,770160,678460,476310,139021,293784,84253,80
    23BSAbird swarm algorithm0,893060,649000,262501,804550,924200,711210,249391,884790,693850,326150,100121,120124,80953,44
    24HSharmony search0,865090,687820,325271,878180,999990,680020,095901,775920,620000,422670,054581,097254,75152,79
    25SSGsaplings sowing and growing0,778390,649250,395431,823080,859730,624670,174291,658690,646670,441330,105981,193984,67651,95
    26BCOmbacterial chemotaxis optimization M0,759530,622680,314831,697040,893780,613390,225421,732590,653850,420920,144351,219124,64951,65
    27ABOafrican buffalo optimization0,833370,622470,299641,755480,921700,586180,197231,705110,610000,431540,132251,173784,63451,49
    28(PO)ES(PO) evolution strategies0,790250,626470,429351,846060,876160,609430,195911,681510,590000,379330,113221,082554,61051,22
    29TSmtabu search M0,877950,614310,291041,783300,928850,518440,190541,637830,610770,382150,121571,114494,53650,40
    30BSObrain storm optimization0,937360,576160,296881,810410,931310,558660,235371,725340,552310,290770,119140,962224,49849,98
    31WOAmwale optimization algorithm M0,845210,562980,262631,670810,931000,522780,163651,617430,663080,411380,113571,188034,47649,74
    32AEFAartificial electric field algorithm0,877000,617530,252351,746880,927290,726980,180641,834900,666150,116310,095080,877544,45949,55
    33AEOartificial ecosystem-based optimization algorithm0,913800,467130,264701,645630,902230,437050,214001,553270,661540,308000,285631,255174,45449,49
    34ACOmant colony optimization M0,881900,661270,303771,846930,858730,586800,150511,596040,596670,373330,024720,994724,43849,31
    35BFO-GAbacterial foraging optimization - ga0,891500,551110,315291,757900,969820,396120,063051,428990,726670,275000,035251,036924,22446,93
    36SOAsimple optimization algorithm0,915200,469760,270891,655850,896750,374010,169841,440600,695380,280310,108521,084224,18146,45
    37ABHAartificial bee hive algorithm0,841310,542270,263041,646630,878580,477790,171811,528180,509230,338770,103970,951974,12745,85
    38ACMOatmospheric cloud model optimization0,903210,485460,304031,692700,802680,378570,191781,373030,623080,244000,107950,975034,04144,90
    39ADAMmadaptive moment estimation M0,886350,447660,266131,600140,844970,384930,168891,398800,661540,270460,105941,037944,03744,85
    40CGOchaos game optimization0,572560,371580,320181,264320,611760,619310,621611,852670,375380,219230,190280,784903,90243,35
    41ATAmartificial tribe algorithm M0,717710,553040,252351,523100,824910,559040,204731,588670,440000,186150,094110,720263,83242,58
    42CFOcentral force optimization0,609610,549580,278311,437500,634180,468330,225411,327920,572310,234770,095860,902943,66840,76
    43ASHAartificial showering algorithm0,896860,404330,256171,557370,803600,355260,191601,350460,476920,181230,097740,755893,66440,71
    44ASBOadaptive social behavior optimization0,763310,492530,326191,582020,795460,400350,260971,456770,264620,171690,182000,618313,65740,63
    45BRObattle royale optimizer0,749450,498330,279951,527730,696240,384560,204271,285070,381540,211080,106080,698703,51239,02
    RWneuroboids optimization algorithm 2(joo)0,487540,321590,257811,066940,375540,219440,158770,753750,279690,149170,098470,527342,34826,09


    Considerações finais

    O algoritmo BRO demonstra uma abordagem interessante para a otimização meta-heurística, abrindo caminho para métodos “baseados em jogos”, utilizando a metáfora de um “Battle Royale”, em que as soluções competem entre si. Os pontos fortes do algoritmo incluem sua simplicidade conceitual, o fato de ser intuitivo e relativamente fácil de implementar, o estreitamento automático do espaço de busca baseado em características estatísticas da população, e o uso do conceito de vizinhos mais próximos para competições locais. O algoritmo BRO é um método de otimização muito promissor, cujo potencial ainda está longe de ser totalmente explorado.

    Tab

    Figura 3. Graduação de cores dos algoritmos conforme os respectivos testes

    chart

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

    Prós e contras do algoritmo BRO:

    Prós:

    1. Ideia inovadora.
    2. Implementação simples.
    3. Desenvolvimento promissor.

    Contras:

    1. Desempenho fraco em funções discretas.

    O artigo inclui um arquivo com as versões mais recentes dos códigos dos algoritmos. O autor não assume responsabilidade pela precisão absoluta das descrições dos algoritmos canônicos, pois muitos deles foram modificados para melhorar as capacidades de busca. As conclusões e considerações apresentadas neste texto baseiam-se exclusivamente nos resultados experimentais obtidos.


    Programas utilizados no artigo

    #NomeTipoDescrição
    1#C_AO.mqh
    Arquivo incluído
    Classe base dos algoritmos populacionais de otimização
    2#C_AO_enum.mqh
    Arquivo incluído
    Enumeração dos algoritmos populacionais de otimização
    3TestFunctions.mqh
    Arquivo incluído
    Biblioteca de funções de teste
    4
    TestStandFunctions.mqh
    Arquivo incluído
    Biblioteca de funções para o ambiente de teste
    5
    Utilities.mqh
    Arquivo incluído
    Biblioteca de funções auxiliares
    6
    CalculationTestResults.mqh
    Arquivo incluído
    Script para cálculo dos resultados e geração da tabela comparativa
    7
    Testing AOs.mq5
    ScriptAmbiente unificado de teste para todos os algoritmos populacionais de otimização
    8
    Simple use of population optimization algorithms.mq5
    Script
    Exemplo simples de uso dos algoritmos populacionais de otimização sem visualização
    9
    Test_AO_BRO.mq5
    ScriptAmbiente de teste específico para o BRO

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

    Arquivos anexados |
    BRO.ZIP (423.38 KB)
    Redes neurais em trading: Hierarquia de habilidades para comportamento adaptativo de agentes (Conclusão) Redes neurais em trading: Hierarquia de habilidades para comportamento adaptativo de agentes (Conclusão)
    O artigo analisa a implementação prática do framework HiSSD em tarefas de trading algorítmico. É mostrado como a hierarquia de habilidades e a arquitetura adaptativa podem ser utilizadas para desenvolver estratégias de negociação robustas.
    Simulação de mercado: Position View (XVIII) Simulação de mercado: Position View (XVIII)
    Neste artigo, mostrei da forma o mais didática possível. Como você pode conseguir modificar e gerar um código que seja capaz de cumprir alguns objetivos. Isto modificando o mínimo possível um código já existente. Iremos adicionar um indicador de volume, ao mesmo tempo impedir que o usuário ou operador venha a remover objetos criados pelo indicador de posição.
    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.
    Do básico ao intermediário: Filas, Listas e Árvores (VII) Do básico ao intermediário: Filas, Listas e Árvores (VII)
    Neste artigo, iremos demonstrar e explicar de uma maneira bastante lucida, como ocorre a remoção de um node de uma árvore. Algo que na maior parte das vezes, mais gera dúvidas e confusão na mente de iniciantes do que necessariamente os ajuda a entender como todo o processo acontece. E por que ele precisa ser feito desta ou daquela maneira.