English Русский 中文 Español Deutsch 日本語
preview
Algoritmo de Fechadura Codificada (Code Lock Algorithm, CLA)

Algoritmo de Fechadura Codificada (Code Lock Algorithm, CLA)

MetaTrader 5Exemplos | 17 outubro 2024, 08:44
161 0
Andrey Dik
Andrey Dik

Conteúdo:

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


1. Introdução

As fechaduras codificadas, também conhecidas como fechaduras digitais ou fechaduras de combinação, são mecanismos de segurança usados para controlar o acesso a locais, cofres, armários e outros objetos. Elas diferem das fechaduras comuns porque, em vez de usar uma chave, é necessário inserir uma combinação numérica específica para abri-las.

As fechaduras codificadas geralmente são equipadas com um teclado, cilindros especiais ou outros mecanismos rotativos, onde é necessário inserir uma sequência numérica correta para destravar a fechadura. Após a inserção da combinação correta, a fechadura codificada ativa o mecanismo que a destrava, permitindo abrir a porta ou acessar o conteúdo do cofre. O usuário pode definir seu próprio código ou usar o código fornecido para abrir a fechadura.

Vantagens das fechaduras codificadas:

  • Segurança: as fechaduras codificadas podem proporcionar um alto nível de segurança, especialmente se os códigos forem trocados regularmente.
  • Conveniência: não é necessário carregar chaves, o que torna as fechaduras codificadas convenientes de usar.
  • Possibilidade de definir vários códigos: alguns modelos permitem a definição de diversos códigos para diferentes usuários ou para intervalos de tempo distintos.

As fechaduras codificadas são usadas em várias áreas, como segurança residencial, edifícios comerciais, hotéis, escolas, escritórios e outras instituições onde o controle de acesso é necessário. O uso de fechaduras codificadas no contexto de algoritmos de otimização pode representar uma ideia interessante. Uma das possíveis aplicações das fechaduras codificadas em algoritmos de otimização pode ser a criação de um sistema que utilize os princípios de funcionamento dessas fechaduras para resolver problemas de otimização, como a busca pelos valores ideais de parâmetros em problemas de aprendizado de máquina ou para resolver outras tarefas de otimização.

O princípio de funcionamento das fechaduras codificadas, baseado na inserção da combinação numérica correta para destravá-las, pode ser análogo aos mecanismos dos algoritmos de otimização, que tentam encontrar a solução ideal por meio da alteração iterativa de parâmetros e da avaliação de sua eficácia.

Por exemplo, podemos imaginar um algoritmo de otimização que usa a analogia com uma fechadura codificada da seguinte forma:

  1. Combinação como parâmetros: os parâmetros que precisam ser otimizados podem ser representados como uma "combinação" de valores de entrada.
  2. Verificação da combinação correta: o algoritmo pode alterar os valores dos parâmetros e avaliar sua eficácia, de forma semelhante ao usuário que insere várias combinações na fechadura para verificar sua correção.
  3. Solução ótima como desbloqueio: quando o algoritmo encontra os valores ótimos dos parâmetros, isso pode ser visto como o "desbloqueio" da solução ideal.

Dessa forma, as fechaduras codificadas me inspiraram a desenvolver um algoritmo de otimização que utiliza os princípios de seu funcionamento, que pode ser usado para encontrar soluções ótimas em várias tarefas, incluindo aprendizado de máquina e desenvolvimento de sistemas de trading.


2. Implementação do algoritmo

Vamos imaginar uma população de agentes como uma população de fechaduras, onde cada fechadura representa uma solução para o problema. A seguir, apresento um exemplo de pseudocódigo para o algoritmo da fechadura codificada:

1. Inicialização:
      Criar um array de fechaduras de tamanho "popSize", onde cada fechadura possui conjuntos de discos "coords", e cada conjunto tem "lockDiscs" discos.
2. Ajuste das combinações das fechaduras:
      Se for o primeiro passo, inicializar cada disco de cada conjunto de cada fechadura com um número aleatório entre 0 e 9.
      Caso contrário, para cada fechadura:
         - Se o número aleatório for menor que "copyProb", copiar o conjunto de discos de uma fechadura escolhida aleatoriamente.
         - Caso contrário, para cada conjunto de discos:
            - Se o número aleatório for menor que "rotateProb", definir o disco com um número aleatório entre 0 e 9.
            - Caso contrário, copiar o disco de uma fechadura escolhida aleatoriamente.
3. Avaliação da qualidade das combinações das fechaduras:
      Atualizar a qualidade da combinação de cada fechadura.   
      Encontrar a fechadura com a melhor combinação e atualizar a solução global, se necessário.
      Copiar todas as fechaduras para um pool comum de fechaduras.
      Ordenar o pool de fechaduras pela adaptabilidade.
4. Repetir os passos 2 e 3 pelo número especificado de épocas ou até que o critério de parada seja atingido.

O conceito é bastante empolgante, porém alguns aspectos ainda levantam questões. Em particular, não está totalmente claro como a combinação de códigos em uma fechadura pode representar uma solução para o problema. Para uma compreensão mais profunda, seria útil ter uma ilustração.

Assim, imagine que a solução de um problema, onde é necessário otimizar um ou mais parâmetros, é representada como uma fechadura codificada. Nesse caso, cada parâmetro pode ser representado como uma combinação de dígitos, onde cada dígito está localizado em um disco separado. O número de discos pode ser qualquer e é definido como um parâmetro externo do algoritmo. Assim, cada parâmetro é representado por um conjunto de discos com marcações numéricas.

Se, por exemplo, o parâmetro do problema tiver um conjunto de 9 discos, em cada um dos quais há números de 0 a 9, a combinação pode variar de 000000000 a 999999999. O número obtido na combinação é escalado para o intervalo de "min" a "max" do respectivo parâmetro. Assim, se tivermos, por exemplo, três parâmetros a serem otimizados, nossa fechadura terá 3 x 9 = 27 discos numéricos. A Figura 1 ilustra o mecanismo de codificação para um parâmetro.

Code

Figura 1. Decodificação do parâmetro do problema de um código numérico para um valor real

Os operadores em algoritmos de otimização, como crossover e mutação, podem ser integrados ao algoritmo de otimização CLA (Code Lock Algorithm), utilizando a ideia de uma fechadura codificada.

1. Crossover (Cruzamento). O crossover em algoritmos genéticos é utilizado para combinar o material genético de dois "pais" para gerar descendentes.

  • No contexto das fechaduras codificadas, podemos imaginar o crossover como a combinação dos valores dos discos de diferentes fechaduras para criar uma nova combinação de parâmetros codificados do problema.
  • Esse processo fornece ao algoritmo a capacidade de explorar novas combinações de parâmetros, de forma similar à maneira como o usuário pode experimentar diferentes combinações em uma fechadura e, ao mesmo tempo, reutilizar as combinações mais bem-sucedidas de dígitos em diferentes fechaduras.

2. Mutação (Mutation). A mutação em algoritmos genéticos refere-se à alteração aleatória do material genético para introduzir diversidade na população.

  • No contexto das fechaduras codificadas, a mutação pode ser interpretada como uma rotação aleatória dos discos para qualquer número, alterando assim a combinação da fechadura.
  • Esse processo ajuda o algoritmo a explorar novas combinações de parâmetros em todo o espaço de soluções, evitando ficar preso em ótimos locais.

De modo geral, a integração do crossover e da mutação no algoritmo de otimização pode aumentar a diversidade das soluções, acelerar a convergência para a solução ótima e ajudar a evitar uma convergência prematura. Tal abordagem pode ser útil em problemas de otimização com grandes espaços de busca e funções-objetivo complexas. Aliás, não temos outra escolha senão girar os discos, utilizando a analogia com os operadores genéticos.

Para resolver o problema, é necessário encontrar a combinação correta de números na fechadura codificada. Precisamos girar os discos até encontrar a combinação certa que abrirá a fechadura. Ao contrário dos algoritmos genéticos, onde ao "girar" o valor de um código binário nos genes a probabilidade é de 50/50 (ou 0 ou 1), no algoritmo da fechadura codificada podemos usar distribuições de probabilidade na estratégia de rotação dos discos. Vamos considerar algumas opções.

Podemos utilizar uma distribuição Gaussiana, onde os parâmetros da combinação são submetidos a mutações, com cada parâmetro alterado por um valor aleatório retirado de uma distribuição normal (Gaussiana). Isso permitirá introduzir algumas mudanças aleatórias nos parâmetros, mantendo, ao mesmo tempo, o valor atual em certa medida.

A aplicação da distribuição Gaussiana para mutação pode ser útil porque permite controlar o grau de alteração dos parâmetros. Os parâmetros podem ser modificados por valores pequenos, próximos dos atuais, o que ajuda a preservar as boas propriedades da solução atual, enquanto o caráter aleatório da mutação permite que o algoritmo explore novas áreas no espaço de busca.

Dessa forma, o uso da distribuição Gaussiana para mutação pode ajudar a equilibrar a exploração de novas soluções com a preservação das boas propriedades das soluções atuais, promovendo uma busca eficiente pela solução ótima em espaços de parâmetros complexos.

Também podemos utilizar a distribuição de potência no processo de mutação. Ao contrário da distribuição Gaussiana, a distribuição de potência possui caudas mais pesadas, o que significa que grandes mudanças nos parâmetros podem ocorrer com maior probabilidade, o que pode ser útil em casos onde uma exploração mais radical do espaço de parâmetros é necessária. A distribuição de potência pode ajudar o algoritmo a "escapar" de armadilhas locais.

Podemos também tentar usar uma terceira opção – a distribuição uniforme, na qual há uma probabilidade igual de escolher qualquer número no disco da fechadura.

Agora podemos passar da parte teórica para a escrita de código e investigações práticas.

No algoritmo CLA, o agente que representa a solução do problema será descrito pela estrutura "S_CLA_Agent". Aqui estão seus componentes principais:

  • f – Esta variável armazena o valor de adaptabilidade do agente. Ela é inicializada como -DBL_MAX, o que significa o menor valor possível para o tipo double.
  • struct S_Lock – Esta é uma estrutura aninhada, que contém o array "lock". Este array é utilizado para armazenar a combinação codificada de um parâmetro otimizado específico do problema.
  • S_Lock code [] – Este é o array de estruturas "S_Lock". Todo esse array representa a "fechadura".
  • void Init (int coords, int lockDiscs) – Esta é uma função de inicialização. Ela recebe dois parâmetros: "coords" – que define o número de conjuntos de parâmetros, e "lockDiscs" – que define o número de discos em cada conjunto. Dentro desta função, ocorre a inicialização do array "code".

No geral, essa estrutura representa o agente do algoritmo de otimização CLA. Cada agente tem sua própria adaptabilidade e descrição da combinação da "fechadura", que serão otimizadas durante a execução do algoritmo.

//——————————————————————————————————————————————————————————————————————————————
struct S_CLA_Agent
{
    double f;  //fitness

    struct S_Lock
    {
        int lock [];
    };

    S_Lock code [];


    void Init (int coords, int lockDiscs)
    {
      f = -DBL_MAX;

      ArrayResize (code, coords);

      for (int i = 0; i < coords; i++)
      {
        ArrayResize (code [i].lock, lockDiscs);
      }
    }
};
//——————————————————————————————————————————————————————————————————————————————

Agora, vamos descrever a classe "C_AO_CLA", que deriva da classe "C_AO". Seus componentes principais são:

  • C_AO_CLA () – O construtor da classe, que inicializa o objeto da classe. Dentro deste construtor, são inicializados parâmetros como "popSize", "lockDiscs", "copyProb", "rotateProb" e "params"".
  • SetParams () – Função que define os parâmetros do algoritmo com base nos valores do array "params".
  • Init () – Função de inicialização, que recebe os intervalos de busca e o número de épocas como parâmetros.
  • Moving (), Revision () – São funções utilizadas para executar a lógica principal do algoritmo.
  • lockDiscs, copyProb, rotateProb – Variáveis que armazenam os parâmetros do algoritmo.
  • S_CLA_Agent agent [] – Um array de agentes, cada um dos quais representa um objeto da estrutura "S_CLA_Agent".
  • maxLockNumber, S_CLA_Agent parents [], S_CLA_Agent parTemp [] – Variáveis e arrays privados que são utilizados dentro da classe.
  • ArrayToNumber (), LockToDouble () – Métodos privados usados para converter o array da combinação codificada em um número e a fechadura em um número com ponto flutuante, respectivamente.
//——————————————————————————————————————————————————————————————————————————————
class C_AO_CLA : public C_AO
{
  public: //--------------------------------------------------------------------
  ~C_AO_CLA () { }
  C_AO_CLA ()
  {
    ao_name = "CLA";
    ao_desc = "Code Lock Algorithm";
    ao_link = "https://www.mql5.com/ru/articles/14878";

    popSize     = 100;   //population size

    lockDiscs   = 8;     //lock discs
    copyProb    = 0.8;   //copying probability
    rotateProb  = 0.03;  //rotate disc probability

    ArrayResize (params, 4);

    params [0].name = "popSize";     params [0].val = popSize;

    params [1].name = "lockDiscs";   params [1].val = lockDiscs;
    params [2].name = "copyProb";    params [2].val = copyProb;
    params [3].name = "rotateProb";  params [3].val = rotateProb;
  }

  void SetParams ()
  {
    popSize    = (int)params [0].val;

    lockDiscs  = (int)params [1].val;
    copyProb   = params      [2].val;
    rotateProb = params      [3].val;
  }

  bool Init (const double &rangeMinP  [], //minimum search range
             const double &rangeMaxP  [], //maximum search range
             const double &rangeStepP [], //step search
             const int     epochsP = 0);  //number of epochs

  void Moving   ();
  void Revision ();
  void Injection (const int popPos, const int coordPos, const double value);

  //----------------------------------------------------------------------------
  int    lockDiscs;    //lock discs
  double copyProb;     //copying probability
  double rotateProb;   //rotate disc probability

  S_CLA_Agent agent [];

  private: //-------------------------------------------------------------------
  int maxLockNumber; //max lock number

  S_CLA_Agent parents [];
  S_CLA_Agent parTemp [];

  int    ArrayToNumber (int &arr []);
  double LockToDouble  (int lockNum, int coordPos);
};
//——————————————————————————————————————————————————————————————————————————————

Definimos o método "Init" na classe "C_AO_CLA". Esse método inicializa o algoritmo com os intervalos de busca e o número de épocas especificados. Descrição de cada etapa:

1. if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; – chama a função StandardInit com os intervalos de busca definidos. Se a inicialização falhar, a função retorna false.

2. ArrayResize (agent, popSize); – esta linha altera o tamanho do array agent para o tamanho da população "popSize".

3. for (int i = 0; i < popSize; i++) agent [i].Init (coords, lockDiscs); – o laço inicializa cada agente da população com o número de coordenadas "coords" e discos de fechadura "lockDiscs" definidos.

4. ArrayResize (parents, popSize * 2); ArrayResize<2>} (parTemp, popSize * 2); – estas linhas redimensionam os arrays "parents" e "parTemp" para o dobro do tamanho da população.

5. for (int i = 0; i < popSize * 2; i++) { parents [i].Init (coords, lockDiscs); parTemp<2>} [i].Init<3>} (coords, lockDiscs); } – o laço inicializa cada pai e pai temporário nos arrays "parents" e "parTemp".

6. maxLockNumber = 0; for (int i = 0; i < lockDiscs; i++) { maxLockNumber += 9 * (int)pow (10, i); } – o laço calcula o número máximo de combinações da fechadura "maxLockNumber<2>}", utilizando o número de discos da fechadura "lockDiscs<3>}".

7. return true; – se todas as operações anteriores forem bem-sucedidas, a função retorna true.

Essa função inicializa os agentes e pais no algoritmo de otimização, usando fechaduras codificadas CLA, e define o número máximo de combinações da fechadura com base na quantidade de discos.

//——————————————————————————————————————————————————————————————————————————————
bool C_AO_CLA::Init (const double &rangeMinP  [], //minimum search range
                     const double &rangeMaxP  [], //maximum search range
                     const double &rangeStepP [], //step search
                     const int     epochsP = 0)   //number of epochs
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //----------------------------------------------------------------------------
  ArrayResize (agent, popSize);
  for (int i = 0; i < popSize; i++) agent [i].Init (coords, lockDiscs);

  ArrayResize (parents, popSize * 2);
  ArrayResize (parTemp, popSize * 2);

  for (int i = 0; i < popSize * 2; i++)
  {
    parents [i].Init (coords, lockDiscs);
    parTemp [i].Init (coords, lockDiscs);
  }

  maxLockNumber = 0;
  for (int i = 0; i < lockDiscs; i++)
  {
    maxLockNumber += 9 * (int)pow (10, i);
  }

  return true;
}
//——————————————————————————————————————————————————————————————————————————————

O processo de ajuste das combinações numéricas nas fechaduras será descrito no método "Moving" na classe "C_AO_CLA". O processo é o seguinte:

  • val = 0.0; code<2>} = 0; pos <3>} = 0; – inicializa as variáveis que serão usadas no método.
  • if (!revision) {...} – o bloco de código é executado se "revision" for false. Dentro desse bloco, os agentes e os pais são inicializados com valores aleatórios. 
  • Em seguida, para cada agente e pai, são calculados os valores "code" e "val<3>}", usados para atualizar as coordenadas do agente. Depois disso, "revision" é definido como true, e a função é finalizada.
  • for (int i = 0; i < popSize; i++) {...} – o bloco de código é executado se "revision" for true. Dentro desse bloco, os agentes são atualizados. Se o número aleatório for menor que "copyProb", o código da fechadura de um dos pais é copiado para o agente. Caso contrário, o código da fechadura do agente é atualizado com um valor aleatório ou com o código de um dos pais. 
  • Em seguida, para cada agente, são calculados os valores "code" e "val<2>}", usados para atualizar as coordenadas do agente.

Essa função é usada para atualizar os agentes no algoritmo de otimização CLA.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_CLA::Moving ()
{
  double val  = 0.0;
  int    code = 0;
  int    pos  = 0;

  //----------------------------------------------------------------------------
  if (!revision)
  {
    for (int i = 0; i < popSize; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        for (int l = 0; l < lockDiscs; l++)
        {
          agent [i].code [c].lock [l] = u.RNDminusOne (10);
        }

        code = ArrayToNumber (agent [i].code [c].lock);
        val  = LockToDouble  (code, c);
        a [i].c [c] = u.SeInDiSp  (val, rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }

    for (int i = 0; i < popSize * 2; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        for (int l = 0; l < lockDiscs; l++)
        {
          parents [i].code [c].lock [l] = u.RNDminusOne (10);
        }
      }
    }

    revision = true;
    return;
  }

  //----------------------------------------------------------------------------
  for (int i = 0; i < popSize; i++)
  {
    for (int c = 0; c < coords; c++)
    {
      if (u.RNDprobab () < copyProb)
      {
        int pos = u.RNDminusOne (popSize);
        ArrayCopy (agent [i].code [c].lock, parents [pos].code [c].lock, 0, 0, WHOLE_ARRAY);
      }
      else
      {
        for (int l = 0; l < lockDiscs; l++)
        {
          if (u.RNDprobab () < rotateProb)
          {
            //pos = u.RNDminusOne (popSize);
            //agent [i].code [c].lock [l] = (int)round (u.GaussDistribution (agent [i].codePrev [c].lock [l], 0, 9, 8));
            //agent [i].code [c].lock [l] = (int)round (u.PowerDistribution (agent [i].codePrev [c].lock [l], 0, 9, 20));
            agent [i].code [c].lock [l] = u.RNDminusOne (10);
          }
          else
          {
            pos = u.RNDminusOne (popSize);
            agent [i].code [c].lock [l] = parents [pos].code [c].lock [l];
          }
        }
      }

      code = ArrayToNumber (agent [i].code [c].lock);
      val  = LockToDouble  (code, c);
      a [i].c [c] = u.SeInDiSp  (val, rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————
O método "Revision" na classe "C_AO_CLA" é projetado para atualizar a solução global. Ele realiza as seguintes ações:
  • ind = -1; – inicializa a variável "ind", que será usada para rastrear o índice do agente com a melhor aptidão.
  • for (int i = 0; i < popSize; i++) {...} – o loop percorre cada agente na população e verifica se a aptidão do agente é maior que o valor atual da melhor aptidão "fB". Se for, "fB" é atualizado e "ind" é definido para o índice atual.
  • if (ind != -1) ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY); – se for encontrado um agente com melhor aptidão, suas coordenadas são copiadas para "cB".
  • for (int i = 0; i < popSize; i++) { agent [i].f = a [i].f; } – o loop atualiza a aptidão de cada agente com base nos valores atuais do array "a".
  • for (int i = 0; i < popSize; i++) { parents [i + popSize] = agent [i]; } – o loop copia cada agente para o array "parents", começando da posição "popSize", ou seja, na segunda metade da população de pais.
  • u.Sorting (parents, parTemp, popSize * 2); – essa linha classifica o array "parents" usando o array "parTemp" como armazenamento temporário.
No geral, essa função atualiza a aptidão dos agentes e "fechaduras parentais" no algoritmo CLA. Isso inclui a atualização da melhor aptidão e a ordenação da população parental pela aptidão.
//——————————————————————————————————————————————————————————————————————————————
void C_AO_CLA::Revision ()
{
  //----------------------------------------------------------------------------
  int ind = -1;

  for (int i = 0; i < popSize; i++)
  {
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ind = i;
    }
  }

  if (ind != -1) ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY);

  //----------------------------------------------------------------------------
  for (int i = 0; i < popSize; i++)
  {
    agent [i].f = a [i].f;
  }

  for (int i = 0; i < popSize; i++)
  {
    parents [i + popSize] = agent [i];
  }

  u.Sorting (parents, parTemp, popSize * 2);
}
//——————————————————————————————————————————————————————————————————————————————

O método "ArrayToNumber" é usado para converter combinações de código de fechaduras em um número. As ações do método são:

  • result = 0; – inicializa a variável que será usada para armazenar o resultado.
  • for (int i = 0; i < ArraySize (arr); i++) {...} – o loop percorre cada elemento do array "arr". Para cada elemento "arr"[i]", o valor atual de "result" é multiplicado por 10 e o valor de "arr"[i] é adicionado.
  • return result; – a linha retorna o valor final de "result".

Em suma, esse método converte um array de números inteiros em um único número, interpretando cada elemento do array como um dígito na representação decimal.

Por exemplo, se o array de entrada "arr" (combinação de código) for: |0|3|1|5|7|0|. Precisamos descartar todos os zeros à esquerda da combinação e começar a formar o número a partir do 3. O resultado seria o número 31570. Se todas as células do array na combinação contiverem zeros, o resultado será 0.

//——————————————————————————————————————————————————————————————————————————————
int C_AO_CLA::ArrayToNumber (int &arr [])
{
  int result = 0;
  for (int i = 0; i < ArraySize (arr); i++)
  {
    result = result * 10 + arr [i];
  }
  return result;
}
//——————————————————————————————————————————————————————————————————————————————

O método "LockToDouble" na classe "C_AO_CLA" é usado para converter um número de código de fechadura para a escala de um parâmetro otimizado. Veja o que ele faz:

  • LockToDouble (int lockNum, int coordPos) – o método recebe dois parâmetros: "lockNum", que representa o número da fechadura, e "coordPos", que representa a posição da coordenada no array de coordenadas (parâmetro a ser otimizado).
  • return u.Scale (lockNum, 0, maxLockNumber, rangeMin [coordPos], rangeMax [coordPos]); – a linha retorna o valor que resulta ao escalar "lockNum" do intervalo [0, maxLockNumber] para o intervalo [rangeMin [coordPos], rangeMax<6>} [coordPos]].

Em resumo, este método é utilizado para converter o número da fechadura em um valor de ponto flutuante com base no intervalo especificado, ou seja, os números da fechadura são transformados em valores dos parâmetros otimizáveis do problema.

//——————————————————————————————————————————————————————————————————————————————
double C_AO_CLA::LockToDouble (int lockNum, int coordPos)
{
  return u.Scale (lockNum, 0, maxLockNumber, rangeMin [coordPos], rangeMax [coordPos]);
}
//——————————————————————————————————————————————————————————————————————————————


3. Resultados dos testes

Os resultados da execução do algoritmo de otimização CLA em funções de teste são excelentes. Alcançar 67,86% do valor máximo possível é um resultado impressionante. Isso demonstra o alto desempenho do algoritmo CLA na otimização de funções complexas.

Esses resultados mostram que o CLA é bastante eficaz e pode ser aplicado com sucesso em várias tarefas que exigem a otimização de funções multidimensionais. Sua escalabilidade e capacidade de lidar com problemas complexos o tornam uma ferramenta promissora para resolver uma ampla gama de problemas de otimização.

CLA|Code Lock Algorithm|100.0|8.0|0.8|0.03|
=============================
5 Hilly's; Func runs: 10000; result: 0.9534490932631814
25 Hilly's; Func runs: 10000; result: 0.8710742215971827
500 Hilly's; Func runs: 10000; result: 0.375900385878165
=============================
5 Forest's; Func runs: 10000; result: 0.9894215656296362
25 Forest's; Func runs: 10000; result: 0.917091907561472
500 Forest's; Func runs: 10000; result: 0.3164221021938828
=============================
5 Megacity's; Func runs: 10000; result: 0.7969230769230768
25 Megacity's; Func runs: 10000; result: 0.693846153846154
500 Megacity's; Func runs: 10000; result: 0.19303076923077062
=============================
All score: 6.10716 (67.86%)

Nas visualizações do desempenho do algoritmo em funções de teste, observa-se uma dispersão dos resultados em funções de baixa dimensionalidade, no entanto, com o aumento do número de parâmetros do problema, a dispersão diminui. Além disso, o potencial do algoritmo em problemas de alta dimensionalidade (1000 parâmetros) é evidente na forma da curva de convergência (linha vermelha), que cresce de forma acelerada. Se o limite de execuções da função de fitness fosse removido (conforme as regras de nossos testes), o algoritmo continuaria a se aproximar do ótimo global.

Hilly

  CLA na função de teste Hilly.

Forest

  CLA na função de teste Forest.

Megacity

  CLA na função de teste Megacity.


O algoritmo de fechadura codificada ficou em segundo lugar no ranking. Na função aguda Forest com 10 parâmetros, o CLA conseguiu até mesmo superar o líder do ranking, o BGA. A coloração verde das células de resultados em todas as funções de teste (em comparação com os demais algoritmos no ranking) indica uma performance muito consistente em diferentes tipos de problemas, sem falhas notáveis em testes individuais. 

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 BGA binary genetic algorithm 0,99989 0,99518 0,42835 2,42341 0,96153 0,96181 0,32027 2,24360 0,91385 0,95908 0,24220 2,11512 6,782 75,36
2 CLA code lock algorithm 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 (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
4 CTA comet tail algorithm 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
5 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
6 ESG evolution of social groups 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
7 SIA simulated isotropic annealing 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
8 TSEA turtle shell evolution algorithm 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
9 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
10 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
11 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
12 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
13 (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
14 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
15 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
16 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
17 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
18 MEC mind evolutionary computation 0,69533 0,53376 0,32661 1,55569 0,72464 0,33036 0,07198 1,12698 0,52500 0,22000 0,04198 0,78698 3,470 38,55
19 IWO invasive weed optimization 0,72679 0,52256 0,33123 1,58058 0,70756 0,33955 0,07484 1,12196 0,42333 0,23067 0,04617 0,70017 3,403 37,81
20 Micro-AIS micro artificial immune system 0,79547 0,51922 0,30861 1,62330 0,72956 0,36879 0,09398 1,19233 0,37667 0,15867 0,02802 0,56335 3,379 37,54
21 COAm cuckoo optimization algorithm M 0,75820 0,48652 0,31369 1,55841 0,74054 0,28051 0,05599 1,07704 0,50500 0,17467 0,03380 0,71347 3,349 37,21
22 SDOm spiral dynamics optimization M 0,74601 0,44623 0,29687 1,48912 0,70204 0,34678 0,10944 1,15826 0,42833 0,16767 0,03663 0,63263 3,280 36,44
23 NMm Nelder-Mead method M 0,73807 0,50598 0,31342 1,55747 0,63674 0,28302 0,08221 1,00197 0,44667 0,18667 0,04028 0,67362 3,233 35,92
24 FAm firefly algorithm M 0,58634 0,47228 0,32276 1,38138 0,68467 0,37439 0,10908 1,16814 0,28667 0,16467 0,04722 0,49855 3,048 33,87
25 GSA gravitational search algorithm 0,64757 0,49197 0,30062 1,44016 0,53962 0,36353 0,09945 1,00260 0,32667 0,12200 0,01917 0,46783 2,911 32,34
26 BFO bacterial foraging optimization 0,61171 0,43270 0,31318 1,35759 0,54410 0,21511 0,05676 0,81597 0,42167 0,13800 0,03195 0,59162 2,765 30,72
27 ABC artificial bee colony 0,63377 0,42402 0,30892 1,36671 0,55103 0,21874 0,05623 0,82600 0,34000 0,14200 0,03102 0,51302 2,706 30,06
28 BA bat algorithm 0,59761 0,45911 0,35242 1,40915 0,40321 0,19313 0,07175 0,66810 0,21000 0,10100 0,03517 0,34617 2,423 26,93
29 SA simulated annealing 0,55787 0,42177 0,31549 1,29513 0,34998 0,15259 0,05023 0,55280 0,31167 0,10033 0,02883 0,44083 2,289 25,43
30 IWDm intelligent water drops M 0,54501 0,37897 0,30124 1,22522 0,46104 0,14704 0,04369 0,65177 0,25833 0,09700 0,02308 0,37842 2,255 25,06
31 PSO particle swarm optimisation 0,59726 0,36923 0,29928 1,26577 0,37237 0,16324 0,07010 0,60572 0,25667 0,08000 0,02157 0,35823 2,230 24,77
32 Boids boids algorithm 0,43340 0,30581 0,25425 0,99346 0,35718 0,20160 0,15708 0,71586 0,27846 0,14277 0,09834 0,51957 2,229 24,77
33 MA monkey algorithm 0,59107 0,42681 0,31816 1,33604 0,31138 0,14069 0,06612 0,51819 0,22833 0,08567 0,02790 0,34190 2,196 24,40
34 SFL shuffled frog-leaping 0,53925 0,35816 0,29809 1,19551 0,37141 0,11427 0,04051 0,52618 0,27167 0,08667 0,02402 0,38235 2,104 23,38
35 FSS fish school search 0,55669 0,39992 0,31172 1,26833 0,31009 0,11889 0,04569 0,47467 0,21167 0,07633 0,02488 0,31288 2,056 22,84
36 RND random 0,52033 0,36068 0,30133 1,18234 0,31335 0,11787 0,04354 0,47476 0,25333 0,07933 0,02382 0,35648 2,014 22,37
37 GWO grey wolf optimizer 0,59169 0,36561 0,29595 1,25326 0,24499 0,09047 0,03612 0,37158 0,27667 0,08567 0,02170 0,38403 2,009 22,32
38 CSS charged system search 0,44252 0,35454 0,35201 1,14907 0,24140 0,11345 0,06814 0,42299 0,18333 0,06300 0,02322 0,26955 1,842 20,46
39 EM electroMagnetism-like algorithm 0,46250 0,34594 0,32285 1,13129 0,21245 0,09783 0,10057 0,41085 0,15667 0,06033 0,02712 0,24412 1,786 19,85


Considerações finais

O algoritmo genético binário (BGA) desempenhou um papel importante na concepção do algoritmo CLA. Meu objetivo era desenvolver um algoritmo mais rápido. Foi então que tive a ideia de substituir a codificação binária pela decimal, e o mecanismo das fechaduras codificadas serviu como protótipo para a criação do algoritmo, o que se mostrou uma decisão bem-sucedida. O CLA opera de maneira mais rápida que o BGA, competindo com ele em termos de eficiência. Além disso, a codificação decimal permite o uso de vários tipos de distribuições ao gerar combinações numéricas, o que pode ser útil para certas tarefas (no código, as distribuições normal e de potência estão comentadas e podem ser utilizadas se necessário). Os experimentos demonstraram que o uso de uma distribuição uniforme simples foi mais eficaz neste caso.

Podemos resumir o que foi dito: o algoritmo de fechadura codificada (CLA) mostrou excelentes resultados, estabilidade e eficiência em todas as funções de teste. Isso evidencia a grande escalabilidade do algoritmo, sua capacidade de lidar com problemas complexos, e destaca a flexibilidade e o potencial do CLA em diferentes cenários de otimização.

Tab

Figura 2. Graduação de cores dos algoritmos em testes correspondentes. Resultados maiores ou iguais a 0,99 estão destacados em branco.

chart

Figura 3. Histograma dos resultados dos testes dos algoritmos (em uma escala de 0 a 100, onde quanto maior, melhor,

e 100 é o resultado máximo teoricamente possível, o script para cálculo do ranking está no arquivo)


Pontos positivos e negativos do algoritmo de fechadura codificada (CLA):

Pontos positivos:

  1. 1. Excelente convergência em diferentes tipos de funções.
  2. 2. Implementação simples.

Pontos negativos:

  1. 1. Dispersão dos resultados em funções de baixa dimensionalidade.

Um arquivo com as versões atualizadas dos códigos dos algoritmos está anexado ao artigo. O autor do artigo não se responsabiliza pela exatidão absoluta na descrição dos algoritmos canônicos, pois muitos deles foram modificados para melhorar suas capacidades de busca. As conclusões e julgamentos apresentados no artigo são baseadas nos resultados dos experimentos realizados.

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

Arquivos anexados |
CLA.zip (25.42 KB)
Desenvolvendo um EA multimoeda (Parte 12): Gerenciamento de Risco como em empresas de prop trading Desenvolvendo um EA multimoeda (Parte 12): Gerenciamento de Risco como em empresas de prop trading
No EA em desenvolvimento, já temos um mecanismo de controle de rebaixamento implementado. No entanto, ele tem uma natureza probabilística, pois se baseia nos resultados de testes com dados históricos de preços. Assim, o rebaixamento, embora com pequena probabilidade, às vezes pode exceder os valores máximos esperados. Vamos tentar adicionar um mecanismo que garanta a manutenção de um nível de rebaixamento predefinido.
Redes neurais de maneira fácil (Parte 91): previsão na área de frequência (FreDF) Redes neurais de maneira fácil (Parte 91): previsão na área de frequência (FreDF)
Continuamos a explorar a análise e previsão de séries temporais na área de frequência. E nesta matéria, apresentaremos um novo método de previsão nessa área, que pode ser adicionado a muitos dos algoritmos que já estudamos anteriormente.
Desenvolvimento de robô em Python e MQL5 (Parte 2): Escolha do modelo, criação e treinamento, testador customizado Python Desenvolvimento de robô em Python e MQL5 (Parte 2): Escolha do modelo, criação e treinamento, testador customizado Python
Continuamos o ciclo de artigos sobre a criação de um robô de trading em Python e MQL5. Hoje, vamos resolver a tarefa de escolher e treinar o modelo, testá-lo, implementar a validação cruzada, busca em grade, além de abordar o ensemble de modelos.
Desenvolvendo um EA multimoeda (Parte 11): Início da automação do processo de otimização Desenvolvendo um EA multimoeda (Parte 11): Início da automação do processo de otimização
Para obter um bom EA, precisamos selecionar muitos bons conjuntos de parâmetros para as instâncias das estratégias de trading. Isso pode ser feito manualmente, executando a otimização em diferentes símbolos e, em seguida, escolhendo os melhores resultados. Mas é melhor delegar esse trabalho para um programa e se concentrar em atividades mais produtivas.