Русский
preview
Otimização com Jogo do Caos — Chaos Game Optimization (CGO)

Otimização com Jogo do Caos — Chaos Game Optimization (CGO)

MetaTrader 5Testador |
15 0
Andrey Dik
Andrey Dik

Conteúdo

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


Introdução

Os algoritmos de otimização desempenham um papel estratégico não apenas na solução de problemas complexos em várias áreas da ciência moderna, mas também no trading. Com o rápido avanço das tecnologias, esses problemas se tornam cada vez mais difíceis e a busca pela melhor solução exige mais recursos, aumentando as demandas sobre os algoritmos de otimização, especialmente no que se refere à eficácia aliada à eficiência. Um dos métodos mais recentes e promissores é o algoritmo Chaos Game Optimization (CGO), desenvolvido por Siamak Talatahari e Mehdi Azizi em 2020. Ele se baseia nos princípios da teoria do caos e utiliza sequências caóticas para gerar e aprimorar soluções.

A ideia central do algoritmo é utilizar sequências caóticas para encontrar ótimos globais em espaços multidimensionais complexos. As sequências caóticas possuem propriedades específicas que, em tese, possibilitam evitar armadilhas locais e encontrar soluções de qualidade. Neste artigo, destrincharemos os princípios e as etapas do algoritmo Chaos Game Optimization, o implementaremos em código e realizaremos testes padrões em funções de benchmark, tirando considerações finais sobre seu desempenho.


Implementação do algoritmo

Imagine um grupo de exploradores, cada um tentando encontrar um extremo em um labirinto multidimensional. No início do percurso, esses exploradores são espalhados aleatoriamente pelo labirinto e estabelecem seu primeiro ponto de referência dentro de limites bem definidos do espaço. Essa é sua posição inicial. Cada explorador não age sozinho, ele observa seus companheiros e, a cada instante, escolhe aleatoriamente um grupo de aliados, calcula o centro de suas posições, como se estivesse encontrando um ponto de equilíbrio entre eles.

Esse é o conhecimento coletivo, uma sabedoria construída a partir da média da experiência do grupo. E então começa a verdadeira magia do caos. O explorador pode escolher um entre quatro caminhos para dar o próximo passo. Cada caminho é uma fórmula particular de movimento, na qual se entrelaçam três pontos principais: a posição atual do explorador, o melhor ponto encontrado por todo o grupo e o centro do subgrupo escolhido. Esses pontos se combinam e a intensidade da influência de cada um sobre o deslocamento é determinada pelo coeficiente α, o condutor do caos.

Por si só, o coeficiente α assume diferentes formas, e cada explorador, seguindo as regras, pode se afastar de sua posição atual em direção ao ponto médio entre o melhor resultado e o centro do grupo; partir da melhor posição encontrada, explorando o espaço ao redor dela; se deslocar a partir do centro do grupo; ou realizar um salto totalmente aleatório para o desconhecido.

Ao final de cada etapa, os resultados são comparados. Se algum explorador encontrar um ponto melhor que o recorde anterior, ele se tornará o novo farol para toda a equipe em sua busca.

É justamente aí que reside a verdadeira beleza do algoritmo, em sua capacidade de transformar o caos em ordem, a aleatoriedade em movimento direcionado, a incerteza em progresso. Cada passo, cada deslocamento, está subordinado à busca de soluções entre o conhecido e o inexplorado, entre a estabilidade e o risco, entre a ordem e o caos.

cgo-illustration

Figura 1. Ações típicas do agente de busca no algoritmo CGO

Na figura 1, o ponto vermelho representa o agente atual, para o qual é calculada a nova posição. Os pontos azuis representam o grupo de agentes escolhidos aleatoriamente da população, em uma quantidade também aleatória. A circunferência tracejada em roxo mostra a posição média do grupo. O ponto dourado representa a melhor solução encontrada até o momento. Os pontos verdes são as possíveis novas posições do agente, calculadas segundo diferentes fórmulas:
  • Formula 1: atual + α(β×melhor - γ×média)
  • Formula 2: melhor + α(β×média - γ×atual)
  • Formula 3: média + α(β×melhor - γ×atual)
  • Random: posição aleatória

    As linhas tracejadas representam os vetores de influência da melhor solução e da posição média do grupo sobre o movimento do agente atual. O retângulo tracejado em cinza indica os limites da área de busca.

    Passemos agora à escrita do pseudocódigo do algoritmo.

  1. INICIALIZAÇÃO DO ALGORITMO:
    • Definir o tamanho da população (por padrão 50 agentes)
    • Definir os limites de busca para cada coordenada:
      • valores mínimos (rangeMin)
      • valores máximos (rangeMax)
      • passo de variação (rangeStep)
    • Para cada agente na população:
      • gerar coordenadas iniciais aleatórias dentro dos limites definidos
      • arredondar as coordenadas de acordo com o passo
      • calcular o valor da função objetivo
    • Determinar a melhor solução inicial entre todos os agentes
  2. CICLO PRINCIPAL DE OTIMIZAÇÃO:
    • Para cada agente na população: 
    a) Selecionar um subgrupo aleatório de agentes:
    • tamanho do subgrupo = número aleatório entre 1 e o tamanho da população
    • escolher aleatoriamente os agentes do subgrupo
    b) Calcular a posição média do subgrupo selecionado:
    • para cada coordenada: média_coord = soma_coord_grupo / tamanho_grupo
    c) Gerar coeficientes para as fórmulas de movimento:
    • α (alfa) = escolher aleatoriamente um dos métodos:
      • método 1: número aleatório de 0 a 1
      • método 2: 2 × aleatório(0,1) - 1 [resulta em número de -1 a 1]
      • método 3: Ir × aleatório(0,1) + 1
      • método 4: Ir × aleatório(0,1) + (1-Ir) onde Ir = aleatório 0 ou 1
    • β (beta) = aleatório 1 ou 2
    • γ (gama) = aleatório 1 ou 2
    d) Escolher aleatoriamente uma das quatro fórmulas de movimento:
    • Fórmula 1: nova_posição = atual + α(β×melhor - γ×média)
    • Fórmula 2: nova_posição = melhor + α(β×média - γ×atual)
    • Fórmula 3: nova_posição = média + α(β×melhor - γ×atual)
    • Fórmula 4: nova_posição = posição aleatória dentro dos limites de busca
    e) Aplicar a fórmula escolhida:
    • para cada coordenada:
      • calcular o novo valor segundo a fórmula
      • verificar se saiu dos limites de busca
      • se saiu, ajustar para a borda mais próxima
      • arredondar o valor de acordo com o passo de variação
    f) Calcular o valor da função objetivo na nova posição
  3. ATUALIZAÇÃO DA MELHOR SOLUÇÃO:
    • Para cada agente:
      • se o valor da função objetivo do agente for melhor que o melhor atual:
        • atualizar o melhor valor
        • salvar as coordenadas da nova melhor solução
  4. REPETIÇÃO:
    • Repetir os passos 2-3 até que a condição de parada seja atingida:
      • atingido o número máximo de iterações
      • ou encontrada solução com a qualidade exigida
      • ou outro critério de parada definido
  5. Agora passamos para a implementação do algoritmo. A classe "C_AO_CGO" implementa o algoritmo CGO e é derivada da classe "C_AO", herdando as propriedades e métodos da classe base. 

    Métodos:

    • SetParams () — define o valor de "popSize" com base nos dados do array de parâmetros "params". Isso é importante para configurar o algoritmo antes de utilizá-lo.
    • Init () — método de inicialização, recebe os valores mínimo e máximo do intervalo, o passo de variação e a quantidade de épocas. Seu objetivo é preparar o algoritmo para a execução, estabelecendo os limites de busca e outros parâmetros.
    • Moving () — descreve os passos relacionados ao movimento dos indivíduos durante a otimização. Sua implementação garante a lógica de alternativas de solução e seu aprimoramento.
    • Revision () — responsável pela revisão tanto das soluções atuais da população quanto da melhor solução global.

    Métodos privados:

    • GetAlpha () — usado para obter o parâmetro alpha, que controla a estratégia, a "intensidade" e a "diversidade" da busca.
    • GenerateNewSolution () — usado para gerar uma nova solução com base no índice (seedIndex) e no valor médio do grupo (meanGroup). 
    class C_AO_CGO : public C_AO
    {
      public: //--------------------------------------------------------------------
      ~C_AO_CGO () { }
      C_AO_CGO ()
      {
        ao_name = "CGO";
        ao_desc = "Chaos Game Optimization";
        ao_link = "https://www.mql5.com/ru/articles/17047";
    
        popSize = 25;
    
        ArrayResize (params, 1);
        params [0].name = "popSize"; params [0].val = popSize;
      }
    
      void SetParams ()
      {
        popSize = (int)params [0].val;
      }
    
      bool Init (const double &rangeMinP  [],  // минимальные значения
                 const double &rangeMaxP  [],  // максимальные значения
                 const double &rangeStepP [],  // шаг изменения
                 const int     epochsP = 0);   // количество эпох
    
      void Moving ();
      void Revision ();
    
      private: //-------------------------------------------------------------------
      double GetAlpha ();
      void   GenerateNewSolution (int seedIndex, double &meanGroup []);
    };
    //——————————————————————————————————————————————————————————————————————————————

    O método "Init" da classe "C_AO_CGO" é responsável por inicializar os parâmetros do algoritmo de otimização antes de sua execução. Ele recebe os seguintes argumentos: arrays contendo os valores mínimos e máximos de cada variável de busca, o passo de variação para cada variável e o número de épocas (iterações) do algoritmo.

    //——————————————————————————————————————————————————————————————————————————————
    bool C_AO_CGO::Init (const double &rangeMinP  [], // минимальные значения
                         const double &rangeMaxP  [], // максимальные значения
                         const double &rangeStepP [], // шаг изменения
                         const int     epochsP = 0)   // количество эпох
    {
      if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
    
      return true;
    }
    //——————————————————————————————————————————————————————————————————————————————

    O método "Moving" executa a lógica principal de deslocamento dos indivíduos da população de soluções no algoritmo CGO. O objetivo central desse método é atualizar as soluções da população com base nas regras, incluindo a geração de novas soluções e o cálculo da média dos resultados. Vamos detalhar suas principais etapas.

    A primeira parte é a inicialização durante a primeira chamada (quando a variável "revision" é igual a "false"):

    • É iniciado um laço externo para percorrer todos os elementos da população (popSize) e, para cada elemento (indivíduo i):
      • É iniciado um laço interno para percorrer as coordenadas (coords):
      • Um valor aleatório é gerado para cada coordenada usando o método u.RNDfromCI(), que retorna um valor dentro do intervalo definido.
      • Esse valor é então ajustado pelo método u.SeInDiSp(), garantindo que ele permaneça dentro dos limites e seja arredondado ao passo mais próximo.
      • Em seguida, a flag "revision" é definida como "true" para a próxima chamada do método, e ocorre a saída do método.
      A segunda parte é a atualização da população (quando a variável "revision" está definida como "true"):
      • Para cada indivíduo da população:
        • É gerado um tamanho de grupo aleatório "randGroupSize", variando de "1" até "popSize".
        • É criado um array "meanGroup" para armazenar os valores médios das coordenadas, com tamanho correspondente ao número de coordenadas definido em "coords".
        • É preenchido o array "randIndices", que contém índices aleatórios (indivíduos) selecionados para formar o grupo.
        • A cada iteração, são adicionados índices aleatórios em "randIndices", escolhidos de forma aleatória.
        • Em seguida, para cada grupo, são somados os valores das coordenadas de cada indivíduo selecionado aleatoriamente, e o resultado é armazenado em "meanGroup".
        • Após o somatório, o valor em "meanGroup" é dividido pelo número de indivíduos do grupo para calcular a média.
        • Um novo indivíduo para a posição "i" é então gerado com base nesse valor médio de grupo, utilizando o método "GenerateNewSolution()".
      //——————————————————————————————————————————————————————————————————————————————
      void C_AO_CGO::Moving ()
      {
        //----------------------------------------------------------------------------
        if (!revision)
        {
          for (int i = 0; i < popSize; i++)
          {
            for (int c = 0; c < coords; c++)
            {
              a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
              a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
            }
          }
      
          revision = true;
          return;
        }
      
        //----------------------------------------------------------------------------
        for (int i = 0; i < popSize; i++)
        {
          int randGroupSize = u.RNDminusOne (popSize) + 1;
          double meanGroup [];
          ArrayResize (meanGroup, coords);
          ArrayInitialize (meanGroup, 0);
      
          int randIndices [];
          ArrayResize (randIndices, randGroupSize);
      
          for (int j = 0; j < randGroupSize; j++) randIndices [j] = u.RNDminusOne (popSize);
      
          for (int j = 0; j < randGroupSize; j++)
          {
            for (int c = 0; c < coords; c++)
            {
              meanGroup [c] += a [randIndices [j]].c [c];
            }
          }
          for (int c = 0; c < coords; c++) meanGroup [c] /= randGroupSize;
      
          GenerateNewSolution (i, meanGroup);
        }
      }
      //——————————————————————————————————————————————————————————————————————————————

      O método "Revision" realiza a atualização das melhores soluções na população. Procorre todos os indivíduos em um laço e, para cada um, verifica se seu valor de função de adaptabilidade "f" é melhor que o valor atual "fB", e se for, atualiza "fB" com o novo valor de "f" e copia as coordenadas do indivíduo atual para o array "cB". Assim, o método "Revision" identifica e atualiza a melhor solução conhecida na população com base nos valores da função de adaptabilidade.

      //——————————————————————————————————————————————————————————————————————————————
      void C_AO_CGO::Revision ()
      {
        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);
          }
        }
      }
      //——————————————————————————————————————————————————————————————————————————————

      O método "GetAlpha" gera e retorna um valor aleatório para "alpha", dependendo de condições aleatórias de escolha.

      • Ir — valor aleatório que pode ser "0" ou "1".
      • Existem quatro possíveis casos (listados em "case"), em cada um dos quais o valor de "alpha" é calculado segundo uma fórmula correspondente:
        • Caso 0: Gera um valor entre 0 e 1.
        • Caso 1: Gera um valor entre -1 e 1.
        • Caso 2: Gera um valor entre 1 e 2, multiplicado por "Ir" (0 ou 1).
        • Caso 3: Gera um valor que depende de "Ir" e pode variar de 0 a 1 ou ser igual a 1, dependendo de seu valor.
      Vale observar que as expressões usadas para gerar o número "alpha" poderiam ser escritas de maneira mais simples, mas foram mantidas na forma original, conforme as fórmulas dos autores do algoritmo.
        //——————————————————————————————————————————————————————————————————————————————
        double C_AO_CGO::GetAlpha ()
        {
          int Ir = u.RNDminusOne (2);
        
          switch (u.RNDminusOne (4))
          {
            case 0: return u.RNDfromCI (0, 1);
            case 1: return 2 * u.RNDfromCI (0, 1) - 1;
            case 2: return Ir * u.RNDfromCI (0, 1) + 1;
            case 3: return Ir * u.RNDfromCI (0, 1) + (1 - Ir);
          }
          return 0;
        }
        //——————————————————————————————————————————————————————————————————————————————

        O método "GenerateNewSolution" é responsável por gerar uma nova solução para um agente específico da população, com base em diferentes parâmetros aleatórios.

        Inicialização dos parâmetros:
        • alpha — valor obtido pela chamada do método "GetAlpha()", utilizado para influenciar a nova posição.
        • beta e gamma — valores aleatórios (1 ou 2).
        Escolha da fórmula:
        • formula — uma das quatro equações possíveis é selecionada aleatoriamente para calcular a nova posição.
        Laço sobre as coordenadas: para cada coordenada (de 0 até coords):
        • newPos — variável para armazenar a nova posição com base na fórmula escolhida.
        • Em dependendo do valor de "formula":
          • Caso 0: a nova posição é calculada como a posição atual do agente somada a uma combinação das coordenadas de "cB" (melhor solução da população) e "meanGroup".
          • Caso 1: a nova posição é calculada utilizando a coordenada de "cB" e o valor médio de "meanGroup".
          • Caso 2: a nova posição é definida a partir do valor médio e da coordenada atual do agente.
          • Caso 3: a nova posição é definida aleatoriamente dentro do intervalo especificado (rangeMin[c] e rangeMax[c]).
        Correção da nova posição:
        • a[seedIndex].c[c] — a coordenada correspondente do agente é atualizada utilizando o método "u.SeInDiSp()", que considera valores mínimos, máximos e passos, garantindo que o novo valor permaneça dentro dos limites permitidos.
        //——————————————————————————————————————————————————————————————————————————————
        void C_AO_CGO::GenerateNewSolution (int seedIndex, double &meanGroup [])
        {
          double alpha = GetAlpha ();
          int    beta  = u.RNDminusOne (2) + 1;
          int    gamma = u.RNDminusOne (2) + 1;
        
          int formula = u.RNDminusOne (4);
        
          for (int c = 0; c < coords; c++)
          {
            double newPos = 0;
        
            switch (formula)
            {
              case 0:
                newPos = a [seedIndex].c [c] + alpha * (beta * cB [c] - gamma * meanGroup [c]);
                break;
        
              case 1:
                newPos = cB [c] + alpha * (beta * meanGroup [c] - gamma * a [seedIndex].c [c]);
                break;
        
              case 2:
                newPos = meanGroup [c] + alpha * (beta * cB [c] - gamma * a [seedIndex].c [c]);
                break;
        
              case 3:
                newPos = u.RNDfromCI (rangeMin [c], rangeMax [c]);
                break;
            }
        
            a [seedIndex].c [c] = u.SeInDiSp (newPos, rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
        //——————————————————————————————————————————————————————————————————————————————

        Após os testes realizados, busquei melhorar a convergência do algoritmo e decidi introduzir uma modificação em relação à versão básica do CGO. A principal diferença está no início do processamento de cada coordenada, antes da aplicação das fórmulas principais de movimento:

        double rnd = u.RNDprobab();                             // Получаем случайное число от 0.0 до 1.0
        rnd *= rnd;                                             // Возводим его в квадрат
        int ind = (int)u.Scale(rnd, 0.0, 1.0, 0, popSize - 1);  // Масштабируем в индекс
        a[seedIndex].c [c] = a[ind].c [c];                      // Копируем координату из другого агента с полученным индексом

        Ocorre a cópia de uma coordenada de um agente escolhido aleatoriamente, porém a seleção do agente não é uniforme, mas sim feita com uma distribuição quadrática de probabilidade (rnd *= rnd). Isso cria um "bias" em direção à escolha de agentes com índices menores (as melhores soluções têm maior probabilidade de serem selecionadas). O número aleatório é elevado ao quadrado, criando assim uma distribuição não uniforme, escalado para o intervalo de índices da população, e em seguida é feita a cópia antes da aplicação das fórmulas principais de movimento. Minha intenção era acelerar a convergência em regiões promissoras, mas, infelizmente, isso não trouxe o efeito esperado.

        Provavelmente, devido à convergência prematura, o aumento do efeito reduz rapidamente a diversidade da população, o que neste algoritmo resulta em maior risco de estagnação. É possível que a própria lógica do algoritmo dificulte essa adaptação. Abaixo está o trecho de código onde foram feitas as alterações, além disso, realizei outras tentativas de aprimoramento, mas nenhum progresso significativo foi alcançado, e decidi manter a versão original do algoritmo.

        //——————————————————————————————————————————————————————————————————————————————
        void C_AO_CGO::GenerateNewSolution (int seedIndex, double &meanGroup [])
        {
          double alpha = GetAlpha ();
          int    beta  = u.RNDminusOne (2) + 1;
          int    gamma = u.RNDminusOne (2) + 1;
        
          int formula = u.RNDminusOne (4);
        
          for (int c = 0; c < coords; c++)
          {
            double rnd = u.RNDprobab ();
            rnd *= rnd;
            int ind = (int)u.Scale (rnd, 0.0, 1.0, 0, popSize - 1);
            a [seedIndex].c [c] = a [ind].c [c];
        
            double newPos = 0;
        
            switch (formula)
            {
              case 0:
                newPos = a [seedIndex].c [c] + alpha * (beta * cB [c] - gamma * meanGroup [c]);
                break;
        
              case 1:
                newPos = cB [c] + alpha * (beta * meanGroup [c] - gamma * a [seedIndex].c [c]);
                break;
        
              case 2:
                newPos = meanGroup [c] + alpha * (beta * cB [c] - gamma * a [seedIndex].c [c]);
                break;
        
              case 3:
                newPos = u.RNDfromCI (rangeMin [c], rangeMax [c]);
                break;
            }
        
            a [seedIndex].c [c] = u.SeInDiSp (newPos, rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
        //——————————————————————————————————————————————————————————————————————————————


        Resultados dos testes

        Como pode ser observado nos resultados dos testes, a porcentagem geral obtida pelo algoritmo é relativamente modesta, porém, analisando com mais atenção, é possível perceber uma característica interessante desse algoritmo, que descrevo a seguir.

        CGO|Chaos Game Optimization|50.0|
        =============================
        5 Hilly's; Func runs: 10000; result: 0.5725597668122144
        25 Hilly's; Func runs: 10000; result: 0.3715760642098293
        500 Hilly's; Func runs: 10000; result: 0.32017971142744234
        =============================
        5 Forest's; Func runs: 10000; result: 0.6117551660766816
        25 Forest's; Func runs: 10000; result: 0.619308424855028
        500 Forest's; Func runs: 10000; result: 0.6216109945434442
        =============================
        5 Megacity's; Func runs: 10000; result: 0.3753846153846153
        25 Megacity's; Func runs: 10000; result: 0.2192307692307692
        500 Megacity's; Func runs: 10000; result: 0.19028461538461647
        =============================
        All score: 3.90189 (43.35%)

        Na visualização do funcionamento do algoritmo em funções de teste, nota-se claramente a formação de estruturas no agrupamento dos agentes, sendo que essas estruturas variam conforme o problema. Mas o ponto em comum no comportamento do algoritmo está na enorme dispersão dos resultados de otimização.

        Hilly

        CGO na função de teste Hilly

        Forest

        CGO na função de teste Forest

        Megacity

        CGO na função de teste Forest

        Com base nos testes, o algoritmo CGO ocupa a 38ª 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
        8AAmarchery algorithm M0,917440,708760,421602,047800,925270,758020,353282,036570,673850,552000,237381,463235,54861,64
        9ESGevolution of social groups (joo)0,999060,796540,350562,146161,000000,828630,131021,959650,823330,553000,047251,423585,52961,44
        10SIAsimulated isotropic annealing (joo)0,957840,842640,414652,215130,982390,795860,205071,983320,686670,493000,090531,270205,46960,76
        11ACSartificial cooperative search0,755470,747440,304071,806981,000000,888610,224132,112740,690770,481850,133221,305835,22658,06
        12DAdialectical algorithm0,861830,700330,337241,899400,981630,727720,287181,996530,703080,452920,163671,319675,21657,95
        13BHAmblack hole algorithm M0,752360,766750,345831,864930,935930,801520,271772,009230,650770,516460,154721,321955,19657,73
        14ASOanarchy society optimization0,848720,746460,314651,909830,961480,791500,238031,991010,570770,540620,166141,277525,17857,54
        15RFOroyal flush optimization (joo)0,833610,737420,346291,917330,894240,738240,240981,873460,631540,502920,164211,298675,08956,55
        16AOSmatomic orbital search M0,802320,704490,310211,817020,856600,694510,219961,771070,746150,528620,143581,418355,00655,63
        17TSEAturtle shell evolution algorithm (joo)0,967980,644800,296721,909490,994490,619810,227081,841390,690770,426460,135981,253225,00455,60
        18DEdifferential evolution0,950440,616740,303081,870260,953170,788960,166521,908650,786670,360330,029531,176534,95555,06
        19CROchemical reaction optimisation0,946290,661120,298531,905930,879060,584220,211461,674730,758460,426460,126861,311784,89254,36
        20BIOblood inheritance optimization (joo)0,815680,653360,308771,777810,899370,653190,217601,770160,678460,476310,139021,293784,84253,80
        21BSAbird swarm algorithm0,893060,649000,262501,804550,924200,711210,249391,884790,693850,326150,100121,120124,80953,44
        22HSharmony search0,865090,687820,325271,878180,999990,680020,095901,775920,620000,422670,054581,097254,75152,79
        23SSGsaplings sowing and growing0,778390,649250,395431,823080,859730,624670,174291,658690,646670,441330,105981,193984,67651,95
        24BCOmbacterial chemotaxis optimization M0,759530,622680,314831,697040,893780,613390,225421,732590,653850,420920,144351,219124,64951,65
        25ABOafrican buffalo optimization0,833370,622470,299641,755480,921700,586180,197231,705110,610000,431540,132251,173784,63451,49
        26(PO)ES(PO) evolution strategies0,790250,626470,429351,846060,876160,609430,195911,681510,590000,379330,113221,082554,61051,22
        27TSmtabu search M0,877950,614310,291041,783300,928850,518440,190541,637830,610770,382150,121571,114494,53650,40
        28BSObrain storm optimization0,937360,576160,296881,810410,931310,558660,235371,725340,552310,290770,119140,962224,49849,98
        29WOAmwale optimization algorithm M0,845210,562980,262631,670810,931000,522780,163651,617430,663080,411380,113571,188034,47649,74
        30AEFAartificial electric field algorithm0,877000,617530,252351,746880,927290,726980,180641,834900,666150,116310,095080,877544,45949,55
        31AEOartificial ecosystem-based optimization algorithm0,913800,467130,264701,645630,902230,437050,214001,553270,661540,308000,285631,255174,45449,49
        32ACOmant colony optimization M0,881900,661270,303771,846930,858730,586800,150511,596040,596670,373330,024720,994724,43849,31
        33BFO-GAbacterial foraging optimization - ga0,891500,551110,315291,757900,969820,396120,063051,428990,726670,275000,035251,036924,22446,93
        34SOAsimple optimization algorithm0,915200,469760,270891,655850,896750,374010,169841,440600,695380,280310,108521,084224,18146,45
        35ABHAartificial bee hive algorithm0,841310,542270,263041,646630,878580,477790,171811,528180,509230,338770,103970,951974,12745,85
        36ACMOatmospheric cloud model optimization0,903210,485460,304031,692700,802680,378570,191781,373030,623080,244000,107950,975034,04144,90
        37ADAMmadaptive moment estimation M0,886350,447660,266131,600140,844970,384930,168891,398800,661540,270460,105941,037944,03744,85
        38CGOchaos game optimization0,572560,371580,320181,264320,611760,619310,621611,852670,375380,219230,190280,784903,90243,35
        39ATAmartificial tribe algorithm M0,717710,553040,252351,523100,824910,559040,204731,588670,440000,186150,094110,720263,83242,58
        40ASHAartificial showering algorithm0,896860,404330,256171,557370,803600,355260,191601,350460,476920,181230,097740,755893,66440,71
        41ASBOadaptive social behavior optimization0,763310,492530,326191,582020,795460,400350,260971,456770,264620,171690,182000,618313,65740,63
        42MECmind evolutionary computation0,695330,533760,326611,555690,724640,330360,071981,126980,525000,220000,041980,786983,47038,55
        43CSAcircle search algorithm0,665600,453170,291261,410030,687970,413970,205251,307190,375380,236310,106460,718153,43538,17
        44IWOinvasive weed optimization0,726790,522560,331231,580580,707560,339550,074841,121960,423330,230670,046170,700173,40337,81
        45Micro-AISmicro artificial immune system0,795470,519220,308611,623300,729560,368790,093981,192330,376670,158670,028020,563353,37937,54

        RWrandom walk0,487540,321590,257811,066940,375540,219440,158770,753750,279690,149170,098470,527342,34826,09


        Considerações finais

        Após analisar os resultados do algoritmo CGO, cheguei a conclusões importantes. O Chaos Game Optimization demonstra um comportamento extremamente interessante. De forma geral, sua eficiência pode ser avaliada como abaixo da média, o que é confirmado pelo resultado global de 43,35%. Porém, o mais notável é seu desempenho em problemas de alta dimensionalidade, pois o CGO mostra grande eficiência justamente em tarefas com 1000 variáveis. Essa é uma característica atípica para a maioria dos algoritmos meta-heurísticos, que geralmente sofrem com a "maldição da dimensionalidade" e perdem eficiência conforme o número de variáveis aumenta. O CGO, ao contrário, às vezes até supera seus próprios resultados em tarefas de 10 e 50 dimensões quando aplicado a problemas de 1000 dimensões. Isso se evidencia de maneira especial na função de teste Forest, que possui um extremo global concentrado em um único ponto "agudo".

        Acredito que esse fenômeno seja explicado pela própria natureza do algoritmo. A base caótica do CGO e a variedade de fórmulas de movimento criam um mecanismo eficiente para a exploração de espaços de alta dimensionalidade. As quatro diferentes estratégias de atualização de posições, a escolha aleatória entre elas e o coeficiente α imprevisível permitem que o algoritmo enfrente tarefas em paisagens multidimensionais complexas. Ele se destacou especialmente em funções do tipo Forest, com resultados entre 0.61–0.62, bem acima de sua média geral.

        Analisando a estrutura do algoritmo, vejo que sua força em altas dimensões está relacionada ao processamento por coordenadas. Em vez de trabalhar com o vetor de solução completo, o CGO atualiza cada coordenada de forma independente, o que lhe dá vantagem à medida que a dimensionalidade cresce. Além disso, o uso de grupos aleatórios e de suas posições médias garante um intercâmbio eficiente de informações entre os agentes, mesmo em espaços de grande dimensionalidade.

        Experimentei girar a superfície da função Forest em diferentes ângulos para verificar se os resultados interessantes obtidos não eram apenas coincidência entre a lógica do algoritmo e as características da função de teste. Isso foi necessário para eliminar a possibilidade de acerto ocasional no extremo global. Os experimentos com rotação apenas confirmaram que os resultados não foram fruto do acaso. Considerando essa particularidade do CGO em funções com extremos agudos, recomendo realizar múltiplas execuções de otimização ao utilizar esse algoritmo. Essa recomendação é especialmente válida para o CGO.

        De forma geral, apesar de seus resultados médios, a capacidade única do CGO de manter e até melhorar sua eficiência em tarefas de maior dimensionalidade faz dele um algoritmo extremamente interessante para estudos futuros e aplicação em problemas complexos de otimização.

        Tab

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

        Chart

        Figura 3. Histograma dos resultados de teste dos algoritmos (em uma escala de 0 a 100, quanto maior, melhor, onde 100 é o resultado teórico máximo possível, no arquivo está o script para cálculo da tabela de classificação)

        Prós e contras do algoritmo CGO:

        Prós:

        1. Não possui parâmetros externos
        2. Boa convergência em funções de média e alta dimensionalidade

        Contras:

        1. Tende a ficar preso em extremos locais em tarefas de baixa dimensionalidade.

        Um arquivo compactado com as versões atualizadas dos códigos dos algoritmos está anexado ao artigo. O autor do artigo não se responsabiliza pela precisão absoluta na descrição dos algoritmos canônicos, já que muitos deles foram modificados para ampliar suas capacidades de busca. As conclusões e observações apresentadas nos artigos baseiam-se nos resultados obtidos nos experimentos.


        Programas utilizados no artigo

        #NomeTipoDescrição
        1C_AO.mqh
        Arquivo incluído
        Classe base dos algoritmos populacionais de
        otimização
        2C_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 do banco de testes
        5
        Utilities.mqh
        Arquivo incluído
        Biblioteca de funções auxiliares
        6
        CalculationTestResults.mqh
        Arquivo incluído
        Script para cálculo dos resultados na tabela comparativa
        7
        Testing AOs.mq5
        ScriptBanco de testes unificado 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_CGO.mq5
        ScriptBanco de testes para o CGO

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

        Arquivos anexados |
        CGO.ZIP (167.34 KB)
        Desenvolvendo um EA multimoeda (Parte 24): Conectando uma nova estratégia (I) Desenvolvendo um EA multimoeda (Parte 24): Conectando uma nova estratégia (I)
        Neste artigo, vamos analisar como conectar uma nova estratégia ao sistema de otimização automática criado. Vamos ver quais EAs precisaremos criar e se será possível evitar alterações nos arquivos da biblioteca Advisor, ou pelo menos reduzi-las ao mínimo.
        Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 1): Projetor de Gráficos Desenvolvimento do Kit de Ferramentas de Análise de Price Action (Parte 1): Projetor de Gráficos
        Este projeto tem como objetivo aproveitar o algoritmo MQL5 para desenvolver um conjunto abrangente de ferramentas de análise para o MetaTrader 5. Essas ferramentas — que vão desde scripts e indicadores até modelos de IA e expert advisors — irão automatizar o processo de análise de mercado. Em alguns momentos, esse desenvolvimento gerará ferramentas capazes de realizar análises avançadas sem intervenção humana e prever resultados em plataformas apropriadas. Nenhuma oportunidade será perdida. Junte-se a mim enquanto exploramos o processo de construção de um conjunto robusto de ferramentas personalizadas de análise de mercado. Começaremos desenvolvendo um programa simples em MQL5 que chamei de Projetor de Gráficos.
        Solicitação no Connexus (Parte 6): Criando uma Requisição e Resposta HTTP Solicitação no Connexus (Parte 6): Criando uma Requisição e Resposta HTTP
        Neste sexto artigo da série da biblioteca Connexus, focamos em uma requisição HTTP completa, cobrindo cada componente que compõe uma requisição. Criamos uma classe que representa a requisição como um todo, o que nos ajudou a reunir as classes criadas anteriormente.
        Simulação de mercado: Position View (VIII) Simulação de mercado: Position View (VIII)
        No artigo anterior vimos como poderíamos implementar o indicador de posição, para que pudéssemos fechar uma posição aberta diretamente via gráfico. Isto interagindo com um objeto que estaria a nossa disposição no gráfico. Depois que o primeiro mecanismo estava concluído e funcionando. Começamos a fazer algumas modificações para que também fosse possível remover as linhas de take profit e stop loss. Isto de uma posição que estivesse aberta. Porém como as mudanças a serem feitas precisariam de uma explicação adequada. Naquele mesmo artigo, apenas mostrei as mudanças que deveriam ocorrer no âmbito do Expert Advisor. Sendo necessário mostrar ainda as mudanças que deveriam ocorrer no Indicador de posição.