Algoritmo de Otimização por Sonhos: Dream Optimization Algorithm (DOA)
Sumário
Introdução
Durante um amplo estudo sobre métodos de otimização eficientes, uma abordagem nova e pouco comum, inspirada em um fenômeno controverso e pouco estudado, chamou minha atenção: o mecanismo dos sonhos.
Em março de 2025, Y. Lang e Y. Gao apresentaram à comunidade científica um algoritmo meta-heurístico inovador de otimização, o Dream Optimization Algorithm (DOA), publicado na revista Computer Methods in Applied Mechanics and Engineering (Volume 436). Esse algoritmo, inspirado nas características únicas dos sonhos humanos, abre novas perspectivas para a resolução de problemas complexos de otimização, incluindo o ajuste de parâmetros de sistemas de trading.
O DOA imita três aspectos fundamentais do processo do sono: preservação parcial da memória, esquecimento seletivo com posterior recomposição das informações e troca de "sonhos" entre os agentes da população. No contexto do trading algorítmico, esses mecanismos permitem equilibrar a exploração de novas regiões do espaço de parâmetros e a intensificação em torno das soluções ótimas encontradas, algo essencial na otimização de estratégias de trading em condições de não estacionariedade dos mercados financeiros.
Neste artigo, analisaremos em detalhes a base matemática do algoritmo, faremos uma implementação em MQL5 e conduziremos uma análise comparativa com outros métodos populacionais de otimização.
Implementação do algoritmo
Quando dormimos, nosso cérebro faz três coisas importantes: memoriza as informações relevantes do dia, esquece detalhes desnecessários e combina diferentes lembranças em novas ideias.
O DOA usa esses mesmos princípios para buscar soluções ótimas. Após a inicialização, toda a população é dividida em vários grupos. Cada grupo recebe seu próprio "estilo de memória", do primeiro, com a melhor memória, até aquele com maior tendência ao esquecimento; cada um altera uma quantidade diferente de dimensões "k" conforme o número do grupo e a dimensionalidade total do problema "D".
Durante determinado período de execução, o algoritmo permanece na fase de exploração. Primeiro, cada grupo aplica a estratégia de memória, retornando todos os seus agentes à melhor posição encontrada pelo grupo. Em seguida, com certa probabilidade, é aplicada a estratégia de esquecimento e complementação. Nesse caso, "k" dimensões escolhidas aleatoriamente são modificadas por meio de uma modulação cossenoidal dada pela fórmula cos((i+T/10)π/T), que permite passos maiores no início, diminuindo-os gradualmente ao longo do tempo. Com a probabilidade restante, é acionada a estratégia de troca de sonhos, que copia "k" dimensões de um agente aleatório da população.
Na etapa final da execução, o algoritmo passa para a fase de intensificação, na qual todos os agentes são reposicionados na melhor solução global e executam um ajuste fino com passos mínimos, por meio da função cossenoidal cos(iπ/T), que tende a zero no fim da otimização. Isso cria um equilíbrio entre a exploração do espaço de busca por meio do mecanismo de memória de grupo e uma otimização final breve, porém precisa.

Figura 1. Ilustração do funcionamento do algoritmo DOA
A imagem acima apresenta o diagrama estrutural do algoritmo DOA. Na parte superior, o elemento central é uma nuvem de pensamentos com a inscrição "Dream-Inspired Search". Dela partem três linhas que levam às três principais estratégias do algoritmo: o bloco azul-claro "Remember Best" reflete a estratégia de memória; no centro, o bloco rosa "Forget Explore" simboliza a estratégia de esquecimento e exploração; à direita, o bloco verde "Share Dreams" representa a estratégia de troca de experiência entre os agentes.
Abaixo dessas três estratégias, aparece a seção "Two Phases", que mostra a distribuição temporal da execução na forma de uma barra horizontal de progresso, em que a parte verde ocupa 99% e é identificada como "Explore", enquanto a faixa vermelha à direita ocupa o 1% restante, destacando visualmente a desproporção extrema entre as fases de exploração e intensificação.
Na parte inferior da ilustração, aparece a fórmula matemática central do algoritmo, "Position += Random × Cosine_Wave(iteration)", ressaltando a natureza técnica e a importância da modulação cossenoidal no funcionamento do algoritmo DOA. Depois de analisar em detalhes a estratégia de otimização, passamos à escrita do pseudocódigo.
Inicialização
INÍCIO do algoritmo DOA
DEFINIR parâmetros:
Tamanho da população = 60 agentes
Número de grupos = 6
Proporção de exploração = 99% do tempo total
Probabilidade de esquecimento = 30%
CRIAR 60 agentes com posições aleatórias no espaço de busca
DIVIDIR os agentes em 6 grupos iguais (10 em cada)
INICIALIZAR a melhor solução de cada grupo como a pior possível
Laço principal de otimização
PARA cada iteração de 1 até o máximo:
SE iteração <= 99% do número total de iterações:
EXECUTAR a fase de exploração
CASO CONTRÁRIO:
EXECUTAR a fase de intensificação
Fase de exploração (Exploration)
PARA cada grupo m de 1 a 6:
ENCONTRAR o melhor agente no grupo m
ATUALIZAR a melhor solução do grupo
CALCULAR o número de dimensões a alterar:
k_mínimo = teto(D / 8 / m)
k_máximo = teto(D / 3 / m)
k = número_aleatório entre k_mínimo e k_máximo
Observação: O Grupo 1 altera mais dimensões (melhor memória),
O Grupo 6 altera menos dimensões (maior esquecimento)
PARA cada agente j no grupo m:
PASSO 1 - Estratégia de memória:
COPIAR a posição do melhor agente do grupo para o agente atual
(todos os agentes do grupo "lembram" a melhor solução encontrada)
PASSO 2 - Escolher k dimensões aleatórias para modificação
PASSO 3 - Estratégia de esquecimento ou troca:
SE número_aleatório < 0.3 (30% de probabilidade):
// Estratégia de esquecimento e complementação
PARA cada uma das k dimensões escolhidas:
novo_valor = atual + aleatório × onda_cossenoidal
onde onda_cossenoidal = (cos((iteração + T/10) × π / T) + 1) / 2
Observação: o cosseno permite passos maiores no início
e passos menores no fim da exploração
CASO CONTRÁRIO (70% de probabilidade):
// Estratégia de troca de sonhos
PARA cada uma das k dimensões escolhidas:
COPIAR o valor de um agente aleatório da população
(o agente "sonha" com a solução de outro agente)
VERIFICAR e corrigir os limites para todas as dimensões
Fase de intensificação (Exploitation)
PARA cada agente j dentre os 60 agentes:
PASSO 1 - Reposicionamento no melhor global:
COPIAR a melhor solução global para o agente atual
(todos os agentes se concentram na "crista" encontrada)
PASSO 2 - Ajuste fino:
CALCULAR o número de dimensões a alterar:
k = número_aleatório entre 2 e máximo(2, teto(D/3))
ESCOLHER k dimensões aleatórias
PARA cada uma das k dimensões escolhidas:
novo_valor = atual + aleatório × onda_cossenoidal
onde onda_cossenoidal = (cos(iteração × π / T) + 1) / 2
Observação: no fim do algoritmo, o cosseno é quase = 0,
o que resulta em passos muito pequenos
VERIFICAR e corrigir os limites
Atualização dos resultados
APÓS cada alteração de posição:
CALCULAR o valor da função objetivo para cada agente
ATUALIZAR a melhor solução global:
SE for encontrada uma solução melhor que a melhor solução global atual:
SALVAR essa solução como a nova melhor global
Na fase de exploração, também:
ATUALIZAR as melhores soluções de cada grupo
FIM da iteração
Passemos à escrita do código do algoritmo. A classe implementará o algoritmo de otimização DOA herdando da classe base "C_AO" (interface para diferentes algoritmos de otimização). O construtor e o destrutor são padrão, sem ações adicionais no destrutor. O construtor define os principais parâmetros do algoritmo, seus valores e os vincula ao array de parâmetros "params", o que permite gerenciar facilmente os parâmetros externamente.
Parâmetros principais:- popSize: tamanho da população, número de soluções possíveis em cada iteração;
- numGroups: número de grupos em que a população é dividida para troca paralela de informações;
- explorationRate: proporção de iterações alocadas à fase de exploração, na qual o algoritmo busca novas regiões do espaço;
- forgettingProb: probabilidade de aplicar a estratégia de "esquecimento", permitindo evitar que o algoritmo fique preso em mínimos locais.
- SetParams (): define os parâmetros da classe a partir do array "params";
- Init (): inicializa o algoritmo, definindo os intervalos de busca e o número de épocas;
- Moving (): executa um passo de otimização;
- Revision (): revisa e atualiza o estado atual da solução.
- currentIteration, totalIterations, explorationIters: contadores de iterações e das janelas temporais do algoritmo;
- groupBest []: array que armazena as melhores soluções de cada grupo, auxiliando a troca de informações e a evolução das soluções.
- ExplorationPhase (): controla o modo exploratório da busca, ampliando o alcance da busca;
- ExploitationPhase (): usa as boas soluções já encontradas para melhorá-las;
- UpdateGroupBest (): atualiza a melhor solução em um grupo específico;
- GetGroupStartIndex (), GetGroupEndIndex (): determinam os intervalos de índices das soluções dentro de cada grupo.
Essa classe implementa o algoritmo DOA, que divide a população em um número fixo de grupos. Ao longo da execução, o algoritmo distribui as iterações entre a fase de exploração, voltada à busca de novas soluções, e a fase de intensificação, voltada ao aprimoramento das melhores soluções já encontradas. A estratégia de "esquecimento" permite que o algoritmo evite ficar preso em máximos locais. A ideia geral é garantir um equilíbrio entre a exploração de novas regiões e o refinamento das soluções já encontradas, contribuindo para uma otimização global eficiente.
//———————————————————————————————————————————————————————————————————— class C_AO_DOA_dream : public C_AO { public: //---------------------------------------------------------- ~C_AO_DOA_dream () { } C_AO_DOA_dream () { ao_name = "DOA"; ao_desc = "Dream Optimization Algorithm"; ao_link = "https://www.mql5.com/en/articles/19177"; popSize = 60; // population size numGroups = 6; // number of groups (fixed in the original) explorationRate = 0.99; // iteration rate for the exploration phase (9/10 in the original) forgettingProb = 0.3; // probability of using the main forgetting strategy ArrayResize (params, 4); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "numGroups"; params [1].val = numGroups; params [2].name = "explorationRate"; params [2].val = explorationRate; params [3].name = "forgettingProb"; params [3].val = forgettingProb; } void SetParams () { popSize = (int)params [0].val; numGroups = (int)params [1].val; explorationRate = params [2].val; forgettingProb = params [3].val; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0); void Moving (); void Revision (); //------------------------------------------------------------------ int numGroups; // number of groups double explorationRate; // iteration rate for the exploration phase double forgettingProb; // probability of applying the main strategy private: //--------------------------------------------------------- int currentIteration; // current iteration int totalIterations; // total number of iterations int explorationIters; // number of exploration iterations S_AO_Agent groupBest []; // best solutions in each group void ExplorationPhase (); void ExploitationPhase (); void UpdateGroupBest (int groupNum); int GetGroupStartIndex (int groupNum); int GetGroupEndIndex (int groupNum); }; //————————————————————————————————————————————————————————————————————
O método de inicialização da classe "C_AO_DOA_dream" prepara a execução do algoritmo de otimização. Ele define os parâmetros iniciais e configura os estados internos necessários para as iterações seguintes.
Primeiro, o método geral de inicialização é chamado para verificar e definir os intervalos de busca e seus respectivos passos, garantindo que os parâmetros estejam configurados corretamente. Se essa etapa falhar, a inicialização é interrompida.
Em seguida, o método configura os contadores: redefine a iteração atual para "0", define o número total de iterações a partir dos parâmetros recebidos, e o número de iterações alocadas à exploração é calculado como parte do total, levando em conta a proporção especificada em "explorationRate".
Depois disso, o array "groupBest", que armazena as melhores soluções dos grupos, é inicializado com tamanho igual ao número de grupos. Para cada grupo, o método "Init" cria uma solução inicial e define o valor da função de qualidade dessa solução como o menor número possível, para permitir comparações e atualizações corretas nas etapas seguintes.
Ao final da execução desse método, o algoritmo fica pronto para iniciar a otimização com os parâmetros, contadores e soluções iniciais dos grupos devidamente configurados.
//———————————————————————————————————————————————————————————————————— //--- Initialization bool C_AO_DOA_dream::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ currentIteration = 0; totalIterations = epochsP; explorationIters = (int)(totalIterations * explorationRate); ArrayResize (groupBest, numGroups); for (int i = 0; i < numGroups; i++) { groupBest [i].Init (coords); groupBest [i].f = -DBL_MAX; // Initialize with the worst value } return true; } //————————————————————————————————————————————————————————————————————
O método "Moving" executa o principal passo de cada iteração do algoritmo DOA. Ele implementa a lógica de avanço do algoritmo de uma iteração para a próxima. Primeiro, o contador da iteração atual, "currentIteration", é incrementado para acompanhar o progresso do algoritmo.
Inicialização inicial, apenas na primeira execução. O sinalizador "revision" é verificado; se estiver como "false", ou seja, se o algoritmo estiver sendo executado pela primeira vez, a população é inicializada. Nesse caso, para cada agente da população e para cada coordenada do agente:
- gera-se um valor aleatório da coordenada dentro do intervalo especificado, "rangeMin" e "rangeMax", usando a função "u.RNDfromCI ()";
- aplica-se uma correção a esse valor com base no passo, "rangeStep", por meio da função "u.SeInDiSp ()", que ajusta o valor ao valor permitido mais próximo, isto é, a um múltiplo do passo;
- após a inicialização, o flag "revision" é definido como "true", para evitar nova inicialização nas iterações seguintes;
- nesse ponto, o método é encerrado.
- verifica-se se a iteração atual está na fase de exploração (currentIteration <= explorationIters);
- se a iteração estiver na fase de exploração, chama-se o método "ExplorationPhase ()";
- caso contrário, se a iteração estiver na fase de intensificação, chama-se o método "ExploitationPhase ()".
Assim, o método "Moving" coordena a execução da otimização: primeiro inicializa a população e, em seguida, alterna entre as fases de exploração e intensificação com base no contador de iterações. A inicialização da população ocorre apenas uma vez; depois disso, o algoritmo entra no laço que determina qual fase deve ser executada na iteração atual.
//———————————————————————————————————————————————————————————————————— //--- The main step of the algorithm void C_AO_DOA_dream::Moving () { currentIteration++; // Initial population setup if (!revision) { for (int i = 0; i < popSize; i++) { for (int j = 0; j < coords; j++) { a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]); a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]); } } revision = true; return; } //------------------------------------------------------------------ // Determine the phase of the algorithm if (currentIteration <= explorationIters) { ExplorationPhase (); } else { ExploitationPhase (); } } //————————————————————————————————————————————————————————————————————
O método "ExplorationPhase" implementa a fase de exploração no algoritmo DOA. Nessa fase, as soluções dos grupos de agentes são atualizadas e diversificadas com o objetivo de buscar possíveis novas regiões do espaço de busca. Para cada grupo de agentes, a melhor solução do grupo é atualizada para refletir o melhor resultado encontrado até o momento. Em seguida, determina-se o número de dimensões, ou coordenadas, para a etapa de esquecimento, com base no número do grupo atual e no total de coordenadas. Para cada grupo, são calculados os índices do primeiro e do último agente que pertencem a ele.
Depois, para cada agente dentro do grupo, a melhor solução do grupo é copiada para a solução atual do agente pela estratégia "Memory", fornecendo uma boa solução já conhecida à qual o agente pode retornar. Cria-se uma lista de dimensões, ou coordenadas, que serão submetidas à operação de "esquecimento" e recomposição. O array de dimensões é embaralhado para escolher aleatoriamente quais delas serão alteradas.
Para cada agente do grupo, define-se a estratégia de atualização das dimensões escolhidas: com a probabilidade definida pelo parâmetro "forgettingProb", aplica-se a estratégia de "esquecimento" com modulação cossenoidal.
Para as dimensões escolhidas, é gerado um valor aleatório dentro do intervalo. A modulação por cosseno depende da iteração atual e do número total de iterações, permitindo controlar a intensidade das alterações ao longo do tempo. Após a atualização, o valor da dimensão é ajustado para o intervalo válido, considerando os passos. Caso o cenário de "esquecimento" não seja escolhido, aplica-se a estratégia "dream sharing". Os valores das dimensões são copiados de outro agente aleatório, ou seja, ocorre uma troca de informações entre os agentes.
Como resultado, essa fase favorece a exploração do espaço de busca, aumentando a diversidade das soluções e ajudando a evitar máximos locais por meio de alterações aleatórias e da troca de informações entre os agentes.
//———————————————————————————————————————————————————————————————————— //--- Exploration phase void C_AO_DOA_dream::ExplorationPhase () { // Handle each group for (int m = 0; m < numGroups; m++) { // Update the best solution in the group UpdateGroupBest (m); // Calculate the number of dimensions to forget int kMin = (int)MathCeil ((double)coords / 8.0 / (m + 1)); int kMax = (int)MathCeil ((double)coords / 3.0 / (m + 1)); int k = u.RNDintInRange (kMin, kMax); // Handle agents in the group int startIdx = GetGroupStartIndex (m); int endIdx = GetGroupEndIndex (m); for (int j = startIdx; j <= endIdx; j++) { // Memory strategy - reset to the best group solution ArrayCopy (a [j].c, groupBest [m].c, 0, 0, WHOLE_ARRAY); // Select random dimensions to forget int dims []; ArrayResize (dims, coords); for (int i = 0; i < coords; i++) dims [i] = i; // Shuffle the array of dimensions for (int i = coords - 1; i > 0; i--) { int idx = u.RNDintInRange (0, i); int temp = dims [i]; dims [i] = dims [idx]; dims [idx] = temp; } // Strategy of forgetting and replenishment if (u.RNDprobab () < forgettingProb) { // Basic strategy with cosine modulation for (int h = 0; h < k; h++) { int dim = dims [h]; double range = rangeMax [dim] - rangeMin [dim]; double randomValue = u.RNDprobab () * range + rangeMin [dim]; double cosineModulation = (MathCos ((1.0 * currentIteration + totalIterations / 10.0) * M_PI / totalIterations) + 1.0) / 2.0; a [j].c [dim] = a [j].c [dim] + randomValue * cosineModulation; a [j].c [dim] = u.SeInDiSp (a [j].c [dim], rangeMin [dim], rangeMax [dim], rangeStep [dim]); } } else { // Dream sharing - copying from a random agent for (int h = 0; h < k; h++) { int dim = dims [h]; int donor = u.RNDintInRange (0, popSize - 1); a [j].c [dim] = a [donor].c [dim]; } } } } } //————————————————————————————————————————————————————————————————————
O método "ExploitationPhase" implementa a fase de intensificação no algoritmo de otimização. Sua principal tarefa é direcionar os agentes para a melhor solução encontrada até o momento, a fim de melhorar os resultados atuais.
Para cada agente da população, a solução do agente é redefinida para a melhor solução global encontrada até o momento, permitindo concentrar a busca nas regiões mais promissoras do espaço de busca. Em seguida, determina-se o número de dimensões, ou coordenadas, que serão modificadas. Em geral, escolhem-se pelo menos duas dimensões e, no máximo, um valor determinado em função do número de coordenadas. Cria-se uma lista com todas as dimensões, ou coordenadas, que depois é embaralhada para escolher quais delas serão alteradas. Para cada dimensão escolhida:
- calcula-se o intervalo de alteração dessa dimensão;
- gera-se um valor aleatório dentro desse intervalo;
- esse valor é modulado por meio de uma função cossenoidal, que depende do número da iteração atual e do número total de iterações, permitindo controlar a intensidade das alterações ao longo do tempo;
- como resultado, o valor da dimensão muda com base no valor gerado e na modulação cossenoidal;
- depois disso, o valor é ajustado ao intervalo permitido, considerando o passo de discretização, para que a solução permaneça válida.
O objetivo desse método é intensificar as boas soluções já alcançadas usando modificações aleatórias e controladas, o que ajuda a avançar para uma região mais promissora do espaço de busca e obter soluções de maior qualidade.
//———————————————————————————————————————————————————————————————————— //--- Exploitation phase void C_AO_DOA_dream::ExploitationPhase () { // In the exploitation phase, all agents move towards the global best for (int j = 0; j < popSize; j++) { // Reset to global best solution ArrayCopy (a [j].c, cB, 0, 0, WHOLE_ARRAY); // Calculate the number of dimensions to modify int km = MathMax (2, (int)MathCeil ((double)coords / 3.0)); int k = u.RNDintInRange (2, km); // Select random dimensions int dims []; ArrayResize (dims, coords); for (int i = 0; i < coords; i++) dims [i] = i; // Shuffle the array of dimensions for (int i = coords - 1; i > 0; i--) { int idx = u.RNDintInRange (0, i); int temp = dims [i]; dims [i] = dims [idx]; dims [idx] = temp; } // Apply the forgetting and addition strategy for (int h = 0; h < k; h++) { int dim = dims [h]; double range = rangeMax [dim] - rangeMin [dim]; double randomValue = u.RNDprobab () * range + rangeMin [dim]; double cosineModulation = (MathCos (currentIteration * M_PI / totalIterations) + 1.0) / 2.0; a [j].c [dim] = a [j].c [dim] + randomValue * cosineModulation; a [j].c [dim] = u.SeInDiSp (a [j].c [dim], rangeMin [dim], rangeMax [dim], rangeStep [dim]); } } } //————————————————————————————————————————————————————————————————————
O método "UpdateGroupBest" tem como objetivo determinar a melhor solução dentro de um grupo específico de agentes. Suas principais operações são as seguintes:
- obter os índices das posições inicial e final dos agentes pertencentes a esse grupo;
- percorrer todos os agentes do grupo indicado;
- para cada agente, comparar o valor da função de avaliação, isto é, a métrica de qualidade da solução, com o melhor valor atual armazenado para o grupo;
- se um agente tiver uma solução com melhor valor da função de fitness, o registro da melhor solução do grupo é atualizado e passa a armazenar essa solução mais eficiente.
Assim, o método mantém atualizado o registro da melhor solução dentro de cada grupo, o que é importante para as etapas seguintes do algoritmo, como as estratégias de busca e de atualização de soluções.
//———————————————————————————————————————————————————————————————————— //--- Update the best solution in the group void C_AO_DOA_dream::UpdateGroupBest (int groupNum) { int startIdx = GetGroupStartIndex (groupNum); int endIdx = GetGroupEndIndex (groupNum); for (int i = startIdx; i <= endIdx; i++) { if (a [i].f > groupBest [groupNum].f) { groupBest [groupNum].f = a [i].f; ArrayCopy (groupBest [groupNum].c, a [i].c, 0, 0, WHOLE_ARRAY); } } } //————————————————————————————————————————————————————————————————————
O método "GetGroupStartIndex" calcula o índice inicial dos elementos pertencentes ao grupo indicado no array de soluções ou agentes. Ele se baseia em cálculos que pressupõem uma distribuição uniforme dos grupos em todo o array. A ideia principal é determinar a posição do primeiro agente, ou elemento, em um grupo específico, com base no número do grupo, no número total de grupos e no tamanho total da população.
Para isso, multiplica-se o número do grupo pelo tamanho total da população e divide-se o resultado pelo número de grupos, obtendo o índice do primeiro elemento dentro do grupo especificado. Essa abordagem permite dividir os dados uniformemente em grupos e facilita as operações seguintes relacionadas ao agrupamento das soluções.
//———————————————————————————————————————————————————————————————————— //--- Get the starting index of the group int C_AO_DOA_dream::GetGroupStartIndex (int groupNum) { return (int)((double)groupNum * popSize / numGroups); } //————————————————————————————————————————————————————————————————————
O método "GetGroupEndIndex" calcula o índice final dos elementos pertencentes ao grupo indicado. Ele determina o último elemento do grupo com base no número do grupo, no tamanho total da população e no número de grupos. O cálculo é feito multiplicando o número do grupo, acrescido de uma unidade, pelo tamanho total da população e dividindo o resultado pelo número de grupos. Em seguida, o valor obtido é reduzido em uma unidade para determinar o índice do último elemento do grupo.
Uma verificação adicional é necessária para evitar acesso fora dos limites do array: se o índice calculado exceder o tamanho da população, ele é corrigido para o valor do último índice válido. Essa abordagem permite determinar corretamente as fronteiras de índice de cada grupo de agentes na população.
//———————————————————————————————————————————————————————————————————— //--- Get the ending index of the group int C_AO_DOA_dream::GetGroupEndIndex (int groupNum) { int endIdx = (int)((double)(groupNum + 1) * popSize / numGroups) - 1; if (endIdx >= popSize) endIdx = popSize - 1; return endIdx; } //————————————————————————————————————————————————————————————————————
O método "Revision" tem como objetivo atualizar o registro da melhor solução encontrada durante a execução do algoritmo. Primeiro, ele percorre todas as soluções da população atual. Dentro do laço, o método verifica, para cada solução, o valor da função objetivo e, se esse valor for maior que o melhor valor atual, "fB" recebe o valor da função objetivo da solução atual, e essa solução passa a ser a melhor solução encontrada.
Depois disso, o método verifica se a iteração atual está na fase de "exploração", isto é, se currentIteration <= explorationIters. Em caso afirmativo, além de atualizar a melhor solução global, o método também considera as melhores soluções encontradas dentro de cada grupo. Para isso, o laço percorre todos os grupos e compara, em cada um, a função objetivo da melhor solução do grupo com o melhor valor atual.
Se a solução dentro do grupo for melhor, "fB" é atualizado, e a solução armazenada em "groupBest" é copiada para o buffer global "cB". Assim, o método "Revision" acompanha e atualiza continuamente o registro da melhor solução encontrada, dependendo da fase atual do algoritmo (exploração ou intensificação), preservando a melhor solução encontrada até o momento.
//———————————————————————————————————————————————————————————————————— //--- Update the best and worst solutions void C_AO_DOA_dream::Revision () { // Update the global best solution 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); } } // Update the best solutions of groups in the exploration phase if (currentIteration <= explorationIters) { for (int m = 0; m < numGroups; m++) { if (groupBest [m].f > fB) { fB = groupBest [m].f; ArrayCopy (cB, groupBest [m].c, 0, 0, WHOLE_ARRAY); } } } } //————————————————————————————————————————————————————————————————————
Resultados dos testes
Agora, após implementar o algoritmo DOA, podemos passar diretamente aos testes nas funções de referência. Como vemos, o algoritmo DOA obteve 53,62% e será incluído na nossa tabela de classificação.
=============================
5 Hilly's; Func runs: 10000; result: 0.8555594031110225
25 Hilly's; Func runs: 10000; result: 0.7008493263471764
500 Hilly's; Func runs: 10000; result: 0.37279821121874124
=============================
5 Forest's; Func runs: 10000; result: 0.7342194493052585
25 Forest's; Func runs: 10000; result: 0.48905397049976357
500 Forest's; Func runs: 10000; result: 0.24146681094197792
=============================
5 Megacity's; Func runs: 10000; result: 0.7723076923076921
25 Megacity's; Func runs: 10000; result: 0.4735384615384616
500 Megacity's; Func runs: 10000; result: 0.18561538461538593
=============================
All score: 4.82541 (53.62%)
Na visualização do funcionamento do algoritmo DOA em baixas dimensionalidades, representada pelas linhas verdes, observa-se uma dispersão dos resultados, especialmente nas funções "Forest" e "Megacity".

DOA na função de teste Hilly

DOA na função de teste Forest

DOA na função de teste Megacity
Com base nos resultados dos testes, o algoritmo DOA ocupa a 26ª posição na classificação geral 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) | ||||||||
| 1 | ANS | across neighbourhood search | 0.94948 | 0.84776 | 0.43857 | 2.23581 | 1.00000 | 0.92334 | 0.39988 | 2.32323 | 0.70923 | 0.63477 | 0.23091 | 1.57491 | 6.134 | 68.15 |
| 2 | CLA | code lock algorithm (joo) | 0.95345 | 0.87107 | 0.37590 | 2.20042 | 0.98942 | 0.91709 | 0.31642 | 2.22294 | 0.79692 | 0.69385 | 0.19303 | 1.68380 | 6.107 | 67.86 |
| 3 | AMOm | animal migration ptimization M | 0.90358 | 0.84317 | 0.46284 | 2.20959 | 0.99001 | 0.92436 | 0.46598 | 2.38034 | 0.56769 | 0.59132 | 0.23773 | 1.39675 | 5.987 | 66.52 |
| 4 | (P+O)ES | (P+O) evolution strategies | 0.92256 | 0.88101 | 0.40021 | 2.20379 | 0.97750 | 0.87490 | 0.31945 | 2.17185 | 0.67385 | 0.62985 | 0.18634 | 1.49003 | 5.866 | 65.17 |
| 5 | CTA | comet tail algorithm (joo) | 0.95346 | 0.86319 | 0.27770 | 2.09435 | 0.99794 | 0.85740 | 0.33949 | 2.19484 | 0.88769 | 0.56431 | 0.10512 | 1.55712 | 5.846 | 64.96 |
| 6 | TETA | time evolution travel algorithm (joo) | 0.91362 | 0.82349 | 0.31990 | 2.05701 | 0.97096 | 0.89532 | 0.29324 | 2.15952 | 0.73462 | 0.68569 | 0.16021 | 1.58052 | 5.797 | 64.41 |
| 7 | SDSm | stochastic diffusion search M | 0.93066 | 0.85445 | 0.39476 | 2.17988 | 0.99983 | 0.89244 | 0.19619 | 2.08846 | 0.72333 | 0.61100 | 0.10670 | 1.44103 | 5.709 | 63.44 |
| 8 | BOAm | billiards optimization algorithm M | 0.95757 | 0.82599 | 0.25235 | 2.03590 | 1.00000 | 0.90036 | 0.30502 | 2.20538 | 0.73538 | 0.52523 | 0.09563 | 1.35625 | 5.598 | 62.19 |
| 9 | AAm | archery algorithm M | 0.91744 | 0.70876 | 0.42160 | 2.04780 | 0.92527 | 0.75802 | 0.35328 | 2.03657 | 0.67385 | 0.55200 | 0.23738 | 1.46323 | 5.548 | 61.64 |
| 10 | ESG | evolution of social groups (joo) | 0.99906 | 0.79654 | 0.35056 | 2.14616 | 1.00000 | 0.82863 | 0.13102 | 1.95965 | 0.82333 | 0.55300 | 0.04725 | 1.42358 | 5.529 | 61.44 |
| 11 | SIA | simulated isotropic annealing (joo) | 0.95784 | 0.84264 | 0.41465 | 2.21513 | 0.98239 | 0.79586 | 0.20507 | 1.98332 | 0.68667 | 0.49300 | 0.09053 | 1.27020 | 5.469 | 60.76 |
| 12 | EOm | extremal_optimization_M | 0.76166 | 0.77242 | 0.31747 | 1.85155 | 0.99999 | 0.76751 | 0.23527 | 2.00277 | 0.74769 | 0.53969 | 0.14249 | 1.42987 | 5.284 | 58.71 |
| 13 | BBO | biogeography based optimization | 0.94912 | 0.69456 | 0.35031 | 1.99399 | 0.93820 | 0.67365 | 0.25682 | 1.86867 | 0.74615 | 0.48277 | 0.17369 | 1.40261 | 5.265 | 58.50 |
| 14 | ACS | artificial cooperative search | 0.75547 | 0.74744 | 0.30407 | 1.80698 | 1.00000 | 0.88861 | 0.22413 | 2.11274 | 0.69077 | 0.48185 | 0.13322 | 1.30583 | 5.226 | 58.06 |
| 15 | DA | dialectical algorithm | 0.86183 | 0.70033 | 0.33724 | 1.89940 | 0.98163 | 0.72772 | 0.28718 | 1.99653 | 0.70308 | 0.45292 | 0.16367 | 1.31967 | 5.216 | 57.95 |
| 16 | BHAm | black hole algorithm M | 0.75236 | 0.76675 | 0.34583 | 1.86493 | 0.93593 | 0.80152 | 0.27177 | 2.00923 | 0.65077 | 0.51646 | 0.15472 | 1.32195 | 5.196 | 57.73 |
| 17 | ASO | anarchy society optimization | 0.84872 | 0.74646 | 0.31465 | 1.90983 | 0.96148 | 0.79150 | 0.23803 | 1.99101 | 0.57077 | 0.54062 | 0.16614 | 1.27752 | 5.178 | 57.54 |
| 18 | RFO | royal flush optimization (joo) | 0.83361 | 0.73742 | 0.34629 | 1.91733 | 0.89424 | 0.73824 | 0.24098 | 1.87346 | 0.63154 | 0.50292 | 0.16421 | 1.29867 | 5.089 | 56.55 |
| 19 | AOSm | atomic orbital search M | 0.80232 | 0.70449 | 0.31021 | 1.81702 | 0.85660 | 0.69451 | 0.21996 | 1.77107 | 0.74615 | 0.52862 | 0.14358 | 1.41835 | 5.006 | 55.63 |
| 20 | TSEA | turtle shell evolution algorithm (joo) | 0.96798 | 0.64480 | 0.29672 | 1.90949 | 0.99449 | 0.61981 | 0.22708 | 1.84139 | 0.69077 | 0.42646 | 0.13598 | 1.25322 | 5.004 | 55.60 |
| 21 | BSA | backtracking_search_algorithm | 0.97309 | 0.54534 | 0.29098 | 1.80941 | 0.99999 | 0.58543 | 0.21747 | 1.80289 | 0.84769 | 0.36953 | 0.12978 | 1.34700 | 4.959 | 55.10 |
| 22 | DE | differential evolution | 0.95044 | 0.61674 | 0.30308 | 1.87026 | 0.95317 | 0.78896 | 0.16652 | 1.90865 | 0.78667 | 0.36033 | 0.02953 | 1.17653 | 4.955 | 55.06 |
| 23 | SRA | successful restaurateur algorithm (joo) | 0.96883 | 0.63455 | 0.29217 | 1.89555 | 0.94637 | 0.55506 | 0.19124 | 1.69267 | 0.74923 | 0.44031 | 0.12526 | 1.31480 | 4.903 | 54.48 |
| 24 | CRO | chemical reaction optimization | 0.94629 | 0.66112 | 0.29853 | 1.90593 | 0.87906 | 0.58422 | 0.21146 | 1.67473 | 0.75846 | 0.42646 | 0.12686 | 1.31178 | 4.892 | 54.36 |
| 25 | BIO | blood inheritance optimization (joo) | 0.81568 | 0.65336 | 0.30877 | 1.77781 | 0.89937 | 0.65319 | 0.21760 | 1.77016 | 0.67846 | 0.47631 | 0.13902 | 1.29378 | 4.842 | 53.80 |
| 26 | DOA | dream_optimization_algorithm | 0.85556 | 0.70085 | 0.37280 | 1.92921 | 0.73421 | 0.48905 | 0.24147 | 1.46473 | 0.77231 | 0.47354 | 0.18561 | 1.43146 | 4.825 | 53.62 |
| 27 | BSA | bird swarm algorithm | 0.89306 | 0.64900 | 0.26250 | 1.80455 | 0.92420 | 0.71121 | 0.24939 | 1.88479 | 0.69385 | 0.32615 | 0.10012 | 1.12012 | 4.809 | 53.44 |
| 28 | DEA | dolphin_echolocation_algorithm | 0.75995 | 0.67572 | 0.34171 | 1.77738 | 0.89582 | 0.64223 | 0.23941 | 1.77746 | 0.61538 | 0.44031 | 0.15115 | 1.20684 | 4.762 | 52.91 |
| 29 | HS | harmony search | 0.86509 | 0.68782 | 0.32527 | 1.87818 | 0.99999 | 0.68002 | 0.09590 | 1.77592 | 0.62000 | 0.42267 | 0.05458 | 1.09725 | 4.751 | 52.79 |
| 30 | SSG | saplings sowing and growing | 0.77839 | 0.64925 | 0.39543 | 1.82308 | 0.85973 | 0.62467 | 0.17429 | 1.65869 | 0.64667 | 0.44133 | 0.10598 | 1.19398 | 4.676 | 51.95 |
| 31 | BCOm | bacterial chemotaxis optimization M | 0.75953 | 0.62268 | 0.31483 | 1.69704 | 0.89378 | 0.61339 | 0.22542 | 1.73259 | 0.65385 | 0.42092 | 0.14435 | 1.21912 | 4.649 | 51.65 |
| 32 | ABO | african buffalo optimization | 0.83337 | 0.62247 | 0.29964 | 1.75548 | 0.92170 | 0.58618 | 0.19723 | 1.70511 | 0.61000 | 0.43154 | 0.13225 | 1.17378 | 4.634 | 51.49 |
| 33 | (PO)ES | (PO) evolution strategies | 0.79025 | 0.62647 | 0.42935 | 1.84606 | 0.87616 | 0.60943 | 0.19591 | 1.68151 | 0.59000 | 0.37933 | 0.11322 | 1.08255 | 4.610 | 51.22 |
| 34 | FBA | Fractal-Based Algorithm | 0.79000 | 0.65134 | 0.28965 | 1.73099 | 0.87158 | 0.56823 | 0.18877 | 1.62858 | 0.61077 | 0.46062 | 0.12398 | 1.19537 | 4.555 | 50.61 |
| 35 | TSm | tabu search M | 0.87795 | 0.61431 | 0.29104 | 1.78330 | 0.92885 | 0.51844 | 0.19054 | 1.63783 | 0.61077 | 0.38215 | 0.12157 | 1.11449 | 4.536 | 50.40 |
| 36 | BSO | brain storm optimization | 0.93736 | 0.57616 | 0.29688 | 1.81041 | 0.93131 | 0.55866 | 0.23537 | 1.72534 | 0.55231 | 0.29077 | 0.11914 | 0.96222 | 4.498 | 49.98 |
| 37 | WOAm | wale optimization algorithm M | 0.84521 | 0.56298 | 0.26263 | 1.67081 | 0.93100 | 0.52278 | 0.16365 | 1.61743 | 0.66308 | 0.41138 | 0.11357 | 1.18803 | 4.476 | 49.74 |
| 38 | AEFA | artificial electric field algorithm | 0.87700 | 0.61753 | 0.25235 | 1.74688 | 0.92729 | 0.72698 | 0.18064 | 1.83490 | 0.66615 | 0.11631 | 0.09508 | 0.87754 | 4.459 | 49.55 |
| 39 | AEO | artificial ecosystem-based optimization algorithm | 0.91380 | 0.46713 | 0.26470 | 1.64563 | 0.90223 | 0.43705 | 0.21400 | 1.55327 | 0.66154 | 0.30800 | 0.28563 | 1.25517 | 4.454 | 49.49 |
| 40 | CAm | camel algorithm M | 0.78684 | 0.56042 | 0.35133 | 1.69859 | 0.82772 | 0.56041 | 0.24336 | 1.63149 | 0.64846 | 0.33092 | 0.13418 | 1.11356 | 4.444 | 49.37 |
| 41 | ACOm | ant colony optimization M | 0.88190 | 0.66127 | 0.30377 | 1.84693 | 0.85873 | 0.58680 | 0.15051 | 1.59604 | 0.59667 | 0.37333 | 0.02472 | 0.99472 | 4.438 | 49.31 |
| 42 | CMAES | covariance_matrix_adaptation_evolution_strategy | 0.76258 | 0.72089 | 0.00000 | 1.48347 | 0.82056 | 0.79616 | 0.00000 | 1.61672 | 0.75846 | 0.49077 | 0.00000 | 1.24923 | 4.349 | 48.33 |
| 43 | DA_duelist | duelist_algorithm | 0.92782 | 0.53778 | 0.27792 | 1.74352 | 0.86957 | 0.47536 | 0.18193 | 1.52686 | 0.62153 | 0.33569 | 0.11715 | 1.07437 | 4.345 | 48.28 |
| 44 | BFO-GA | bacterial foraging optimization - ga | 0.89150 | 0.55111 | 0.31529 | 1.75790 | 0.96982 | 0.39612 | 0.06305 | 1.42899 | 0.72667 | 0.27500 | 0.03525 | 1.03692 | 4.224 | 46.93 |
| 45 | SOA | simple optimization algorithm | 0.91520 | 0.46976 | 0.27089 | 1.65585 | 0.89675 | 0.37401 | 0.16984 | 1.44060 | 0.69538 | 0.28031 | 0.10852 | 1.08422 | 4.181 | 46.45 |
| RW | random walk | 0.48754 | 0.32159 | 0.25781 | 1.06694 | 0.37554 | 0.21944 | 0.15877 | 0.75375 | 0.27969 | 0.14917 | 0.09847 | 0.52734 | 2.348 | 26.09 | |
Conclusões
O Dream Optimization Algorithm mostra resultados satisfatórios, ao obter 53,62% dos 100% possíveis nos testes realizados, sendo incluído na nossa tabela de classificação.
A análise dos resultados revela um padrão característico de muitos algoritmos meta-heurísticos: o desempenho cai significativamente à medida que a dimensionalidade do problema aumenta. Em baixas dimensionalidades, o DOA apresenta resultados na faixa de 73% a 86%; ao passar para problemas de alta dimensionalidade, a eficiência cai para 18% a 37% com um número finito de iterações.
De modo geral, o algoritmo se mostra um algoritmo mediano em comparação com os demais. Os interessados podem experimentar os ajustes do algoritmo; talvez ainda exista potencial para obter resultados melhores.

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

Figura 3. Histograma dos resultados dos testes dos algoritmos (em escala de 0 a 100, quanto maior, melhor, em que 100 é o resultado teórico máximo possível, no arquivo há um script para calcular a tabela de classificação)
Pontos fortes e fracos do algoritmo DOA:
Pontos fortes:
- Implementação simples.
- Rápido.
Pontos fracos:
- Dispersão dos valores em funções de baixa dimensionalidade.
O artigo inclui um arquivo anexado com as versões atuais dos códigos dos algoritmos. O autor do artigo não se responsabiliza pela precisão absoluta na descrição dos algoritmos canônicos; muitos deles foram modificados para melhorar sua capacidade de busca. As conclusões e avaliações apresentadas no artigo baseiam-se nos resultados dos experimentos realizados.
Programas usados no artigo
| # | Nome | Tipo | Descrição |
|---|---|---|---|
| 1 | #C_AO.mqh | Arquivo include | Classe base dos algoritmos populacionais de otimização |
| 2 | #C_AO_enum.mqh | Arquivo include | Enumeração dos algoritmos populacionais de otimização |
| 3 | TestFunctions.mqh | Arquivo include | Biblioteca de funções de teste |
| 4 | TestStandFunctions.mqh | Arquivo include | Biblioteca de funções da bancada de testes |
| 5 | Utilities.mqh | Arquivo include | Biblioteca de funções auxiliares |
| 6 | CalculationTestResults.mqh | Arquivo include | Script para calcular os resultados para a tabela comparativa |
| 7 | Testing AOs.mq5 | Script | Bancada de testes unificada para todos os algoritmos populacionais de otimização |
| 8 | Simple use of population optimization algorithms.mq5 | Script | Exemplo simples de uso de algoritmos populacionais de otimização sem visualização |
| 9 | Test_AO_DOA.mq5 | Script | Bancada de testes para o DOA |
Traduzido do russo pela MetaQuotes Ltd.
Artigo original: https://www.mql5.com/ru/articles/19177
Aviso: Todos os direitos sobre esses materiais pertencem à MetaQuotes Ltd. É proibida a reimpressão total ou parcial.
Esse artigo foi escrito por um usuário do site e reflete seu ponto de vista pessoal. A MetaQuotes Ltd. não se responsabiliza pela precisão das informações apresentadas nem pelas possíveis consequências decorrentes do uso das soluções, estratégias ou recomendações descritas.
Treinamento de um U-Transformer não linear nos resíduos de um modelo autorregressivo linear
Redes neurais em trading: Modelo de consultas temporais (Final)
Redes neurais em trading: treinamento de metaparâmetros com base na heterogeneidade (HimNet)
Redes neurais em trading: Modelo de consultas temporais (TQNet)
- Aplicativos de negociação gratuitos
- 8 000+ sinais para cópia
- Notícias econômicas para análise dos mercados financeiros
Você concorda com a política do site e com os termos de uso