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/ru/articles/19177"; popSize = 60; // размер популяции numGroups = 6; // количество групп (фиксировано в оригинале) explorationRate = 0.99; // доля итераций для фазы исследования (9/10 в оригинале) forgettingProb = 0.3; // вероятность применения основной стратегии забывания 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; // количество групп double explorationRate; // доля итераций для фазы исследования double forgettingProb; // вероятность применения основной стратегии private: //--------------------------------------------------------- int currentIteration; // текущая итерация int totalIterations; // общее количество итераций int explorationIters; // количество итераций исследования S_AO_Agent groupBest []; // лучшие решения в каждой группе 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.
//———————————————————————————————————————————————————————————————————— //--- Инициализация 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; // Инициализация худшим значением } 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.
//———————————————————————————————————————————————————————————————————— //--- Основной шаг алгоритма void C_AO_DOA_dream::Moving () { currentIteration++; // Начальная инициализация популяции 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; } //------------------------------------------------------------------ // Определяем фазу алгоритма 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 () { // Обрабатываем каждую группу for (int m = 0; m < numGroups; m++) { // Обновляем лучшее решение в группе UpdateGroupBest (m); // Вычисляем количество измерений для забывания 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); // Обрабатываем агентов в группе int startIdx = GetGroupStartIndex (m); int endIdx = GetGroupEndIndex (m); for (int j = startIdx; j <= endIdx; j++) { // Memory strategy - сброс к лучшему решению группы ArrayCopy (a [j].c, groupBest [m].c, 0, 0, WHOLE_ARRAY); // Выбираем случайные измерения для забывания int dims []; ArrayResize (dims, coords); for (int i = 0; i < coords; i++) dims [i] = i; // Перемешиваем массив измерений 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; } // Стратегия забывания и восполнения if (u.RNDprobab () < forgettingProb) { // Основная стратегия с косинусоидальной модуляцией 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 - копирование из случайного агента 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 () { // В фазе эксплуатации все агенты движутся к глобальному лучшему for (int j = 0; j < popSize; j++) { // Сброс к глобальному лучшему решению ArrayCopy (a [j].c, cB, 0, 0, WHOLE_ARRAY); // Вычисляем количество измерений для модификации int km = MathMax (2, (int)MathCeil ((double)coords / 3.0)); int k = u.RNDintInRange (2, km); // Выбираем случайные измерения int dims []; ArrayResize (dims, coords); for (int i = 0; i < coords; i++) dims [i] = i; // Перемешиваем массив измерений 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; } // Применяем стратегию забывания и дополнения 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.
//———————————————————————————————————————————————————————————————————— //--- Обновить лучшее решение в группе 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.
//———————————————————————————————————————————————————————————————————— //--- Получить начальный индекс группы 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.
//———————————————————————————————————————————————————————————————————— //--- Получить конечный индекс группы 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.
//———————————————————————————————————————————————————————————————————— //--- Обновление лучшего и худшего решений void C_AO_DOA_dream::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); } } // Обновляем лучшие решения групп в фазе исследования 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 optimisation | 0,94629 | 0,66112 | 0,29853 | 1,90593 | 0,87906 | 0,58422 | 0,21146 | 1,67473 | 0,75846 | 0,42646 | 0,12686 | 1,31178 | 4,892 | 54,36 |
| 25 | BIO | blood inheritance optimization (joo) | 0,81568 | 0,65336 | 0,30877 | 1,77781 | 0,89937 | 0,65319 | 0,21760 | 1,77016 | 0,67846 | 0,47631 | 0,13902 | 1,29378 | 4,842 | 53,80 |
| 26 | DOA | dream_optimization_algorithm | 0,85556 | 0,70085 | 0,37280 | 1,92921 | 0,73421 | 0,48905 | 0,24147 | 1,46473 | 0,77231 | 0,47354 | 0,18561 | 1,43146 | 4,825 | 53,62 |
| 27 | BSA | bird swarm algorithm | 0,89306 | 0,64900 | 0,26250 | 1,80455 | 0,92420 | 0,71121 | 0,24939 | 1,88479 | 0,69385 | 0,32615 | 0,10012 | 1,12012 | 4,809 | 53,44 |
| 28 | DEA | dolphin_echolocation_algorithm | 0,75995 | 0,67572 | 0,34171 | 1,77738 | 0,89582 | 0,64223 | 0,23941 | 1,77746 | 0,61538 | 0,44031 | 0,15115 | 1,20684 | 4,762 | 52,91 |
| 29 | HS | harmony search | 0,86509 | 0,68782 | 0,32527 | 1,87818 | 0,99999 | 0,68002 | 0,09590 | 1,77592 | 0,62000 | 0,42267 | 0,05458 | 1,09725 | 4,751 | 52,79 |
| 30 | SSG | saplings sowing and growing | 0,77839 | 0,64925 | 0,39543 | 1,82308 | 0,85973 | 0,62467 | 0,17429 | 1,65869 | 0,64667 | 0,44133 | 0,10598 | 1,19398 | 4,676 | 51,95 |
| 31 | BCOm | bacterial chemotaxis optimization M | 0,75953 | 0,62268 | 0,31483 | 1,69704 | 0,89378 | 0,61339 | 0,22542 | 1,73259 | 0,65385 | 0,42092 | 0,14435 | 1,21912 | 4,649 | 51,65 |
| 32 | ABO | african buffalo optimization | 0,83337 | 0,62247 | 0,29964 | 1,75548 | 0,92170 | 0,58618 | 0,19723 | 1,70511 | 0,61000 | 0,43154 | 0,13225 | 1,17378 | 4,634 | 51,49 |
| 33 | (PO)ES | (PO) evolution strategies | 0,79025 | 0,62647 | 0,42935 | 1,84606 | 0,87616 | 0,60943 | 0,19591 | 1,68151 | 0,59000 | 0,37933 | 0,11322 | 1,08255 | 4,610 | 51,22 |
| 34 | FBA | fractal-based Algorithm | 0,79000 | 0,65134 | 0,28965 | 1,73099 | 0,87158 | 0,56823 | 0,18877 | 1,62858 | 0,61077 | 0,46062 | 0,12398 | 1,19537 | 4,555 | 50,61 |
| 35 | TSm | tabu search M | 0,87795 | 0,61431 | 0,29104 | 1,78330 | 0,92885 | 0,51844 | 0,19054 | 1,63783 | 0,61077 | 0,38215 | 0,12157 | 1,11449 | 4,536 | 50,40 |
| 36 | BSO | brain storm optimization | 0,93736 | 0,57616 | 0,29688 | 1,81041 | 0,93131 | 0,55866 | 0,23537 | 1,72534 | 0,55231 | 0,29077 | 0,11914 | 0,96222 | 4,498 | 49,98 |
| 37 | WOAm | wale optimization algorithm M | 0,84521 | 0,56298 | 0,26263 | 1,67081 | 0,93100 | 0,52278 | 0,16365 | 1,61743 | 0,66308 | 0,41138 | 0,11357 | 1,18803 | 4,476 | 49,74 |
| 38 | AEFA | artificial electric field algorithm | 0,87700 | 0,61753 | 0,25235 | 1,74688 | 0,92729 | 0,72698 | 0,18064 | 1,83490 | 0,66615 | 0,11631 | 0,09508 | 0,87754 | 4,459 | 49,55 |
| 39 | AEO | artificial ecosystem-based optimization algorithm | 0,91380 | 0,46713 | 0,26470 | 1,64563 | 0,90223 | 0,43705 | 0,21400 | 1,55327 | 0,66154 | 0,30800 | 0,28563 | 1,25517 | 4,454 | 49,49 |
| 40 | CAm | camel algorithm M | 0,78684 | 0,56042 | 0,35133 | 1,69859 | 0,82772 | 0,56041 | 0,24336 | 1,63149 | 0,64846 | 0,33092 | 0,13418 | 1,11356 | 4,444 | 49,37 |
| 41 | ACOm | ant colony optimization M | 0,88190 | 0,66127 | 0,30377 | 1,84693 | 0,85873 | 0,58680 | 0,15051 | 1,59604 | 0,59667 | 0,37333 | 0,02472 | 0,99472 | 4,438 | 49,31 |
| 42 | CMAES | covariance_matrix_adaptation_evolution_strategy | 0,76258 | 0,72089 | 0,00000 | 1,48347 | 0,82056 | 0,79616 | 0,00000 | 1,61672 | 0,75846 | 0,49077 | 0,00000 | 1,24923 | 4,349 | 48,33 |
| 43 | DA_duelist | duelist_algorithm | 0,92782 | 0,53778 | 0,27792 | 1,74352 | 0,86957 | 0,47536 | 0,18193 | 1,52686 | 0,62153 | 0,33569 | 0,11715 | 1,07437 | 4,345 | 48,28 |
| 44 | BFO-GA | bacterial foraging optimization - ga | 0,89150 | 0,55111 | 0,31529 | 1,75790 | 0,96982 | 0,39612 | 0,06305 | 1,42899 | 0,72667 | 0,27500 | 0,03525 | 1,03692 | 4,224 | 46,93 |
| 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