
Алгоритм оптимизации сновидениями — Dream Optimization Algorithm (DOA)
Содержание
Введение
В рамках широкого исследования оптимальных методов оптимизации моё внимание привлёк совершенно новый и необычный подход, вдохновлённый весьма спорным и малоизученным феноменом — механизмом сновидений.
В марте 2025 года Y. Lang и Y. Gao представили научному сообществу инновационный метаэвристический алгоритм оптимизации — Dream Optimization Algorithm (DOA), опубликованный в журнале Computer Methods in Applied Mechanics and Engineering (Volume 436). Этот алгоритм, вдохновленный уникальными характеристиками человеческих сновидений, открывает новые перспективы для решения сложных оптимизационных задач, включая настройку параметров торговых систем.
DOA имитирует три ключевых аспекта процесса сна: частичное сохранение памяти, избирательное забывание с последующим восполнением информации и обмен «снами» между агентами популяции. В контексте алгоритмического трейдинга эти механизмы позволяют балансировать между исследованием новых областей параметрического пространства и эксплуатацией найденных оптимальных решений, что критически важно при оптимизации торговых стратегий в условиях нестационарности финансовых рынков.
В статье мы детально рассмотрим математическую основу алгоритма, сделаем реализацию на языке MQL5, и проведем сравнительный анализ с другими популяционными методами оптимизации.
Реализация алгоритма
Когда мы спим, наш мозг делает три важные вещи, а именно: запоминает важную информацию дня, забывает ненужные детали и комбинирует разные воспоминания в новые идеи.
DOA использует эти же принципы для поиска оптимальных решений. После инициализации вся популяция делится на несколько команд. Каждая команда получает свой "стиль памяти" — от первой группы с наилучшей памятью до группы с наибольшей забывчивостью, где каждая группа изменяет разное количество измерений "k" в зависимости от номера группы и общей размерности задачи "D".
В течение определенного времени работы алгоритм находится в фазе исследования. Сначала каждая группа применяет стратегию памяти, возвращая всех своих агентов к лучшей найденной позиции группы, затем, с определенной вероятностью, применяется стратегия забывания и дополнения. В этом случае "k" случайно выбранных измерений модифицируются с использованием косинусоидальной модуляции по формуле cos((i+T/10)π/T), обеспечивающей большие шаги в начале и постепенное их уменьшение. С оставшейся вероятностью срабатывает стратегия обмена снами, копирующая "k" измерений из случайного агента популяции.
На заключительном этапе времени алгоритм переходит в фазу эксплуатации, где все агенты сбрасываются к глобально лучшему решению и выполняют точную настройку с минимальными шагами, благодаря косинусоидальной функции cos(iπ/T), стремящейся к нулю в конце оптимизации, что создает баланс между исследованием пространства поиска через механизм групповой памяти и кратковременной, но точной финальной оптимизацией.
Рисунок 1. Иллюстрация работы алгоритма DOA
Выше на изображении представлена структурная диаграмма алгоритма DOA. В верхней части центральным элементом является облако мыслей с надписью "Dream-Inspired Search". От него расходятся три линии, ведущие к трем основным стратегиям алгоритма: голубой блок "Remember Best" — отражает стратегию памяти, в центре — розовый блок "Forget Explore", символизирует стратегию забывания и исследования, справа — зеленый блок "Share Dreams", представляет стратегию обмена опытом между агентами.
Ниже этих трех стратегий размещен раздел "Two Phases", демонстрирующий временное распределение работы алгоритма в виде горизонтальной полосы прогресса, где зеленая часть занимает 99% и обозначена как "Explore", а красная полоска справа занимает оставшийся 1%, визуально подчеркивая экстремальную диспропорцию между фазами исследования и эксплуатации.
В самом низу иллюстрации представлена ключевая математическая формула алгоритма "Position += Random × Cosine_Wave(iteration)", подчеркивая техническую природу и важность косинусоидальной модуляции в работе алгоритма DOA. После детального разбора стратегии оптимизации алгоритма, переходим к написанию псевдокода.
Инициализация
НАЧАЛО алгоритма DOA
УСТАНОВИТЬ параметры:
- Размер популяции = 60 агентов
- Количество групп = 6
- Доля исследования = 99% от общего времени
- Вероятность забывания = 30%
СОЗДАТЬ 60 агентов со случайными позициями в пространстве поиска
РАЗДЕЛИТЬ агентов на 6 равных групп (по 10 в каждой)
ИНИЦИАЛИЗИРОВАТЬ лучшее решение каждой группы как худшее возможное
Основной цикл оптимизации
ДЛЯ каждой итерации от 1 до максимума:
ЕСЛИ итерация <= 99% от общего числа итераций:
ВЫПОЛНИТЬ фазу исследования
ИНАЧЕ:
ВЫПОЛНИТЬ фазу эксплуатации
Фаза исследования (Exploration)
ДЛЯ каждой группы m от 1 до 6:
НАЙТИ лучшего агента в группе m
ОБНОВИТЬ лучшее решение группы
ВЫЧИСЛИТЬ количество измерений для изменения:
k_минимум = потолок(D / 8 / m)
k_максимум = потолок(D / 3 / m)
k = случайное_число между k_минимум и k_максимум
Примечание: Группа 1 меняет больше измерений (лучшая память),
Группа 6 меняет меньше измерений (больше забывает)
ДЛЯ каждого агента j в группе m:
ШАГ 1 - Стратегия памяти:
СКОПИРОВАТЬ позицию лучшего агента группы в текущего агента
(все агенты группы "вспоминают" лучшее найденное решение)
ШАГ 2 - Выбрать k случайных измерений для модификации
ШАГ 3 - Стратегия забывания или обмена:
ЕСЛИ случайное_число < 0.3 (30% вероятность):
// Стратегия забывания и дополнения
ДЛЯ каждого из k выбранных измерений:
новое_значение = текущее + случайное × косинусоидальная_волна
где косинусоидальная_волна = (cos((итерация + T/10) × π / T) + 1) / 2
Примечание: косинус обеспечивает большие шаги в начале,
маленькие шаги к концу исследования
ИНАЧЕ (70% вероятность):
// Стратегия обмена снами
ДЛЯ каждого из k выбранных измерений:
СКОПИРОВАТЬ значение из случайного агента популяции
(агент "видит во сне" решение другого агента)
ПРОВЕРИТЬ и исправить границы для всех измерений
Фаза эксплуатации (Exploitation)
ДЛЯ каждого агента j из всех 60:
ШАГ 1 - Сброс к глобальному лучшему:
СКОПИРОВАТЬ глобально лучшее решение в текущего агента
(все агенты собираются у найденной "вершины")
ШАГ 2 - Тонкая настройка:
ВЫЧИСЛИТЬ количество измерений для изменения:
k = случайное_число между 2 и максимум(2, потолок(D/3))
ВЫБРАТЬ k случайных измерений
ДЛЯ каждого из k выбранных измерений:
новое_значение = текущее + случайное × косинусоидальная_волна
где косинусоидальная_волна = (cos(итерация × π / T) + 1) / 2
Примечание: в конце алгоритма косинус почти = 0,
что дает очень маленькие шаги
ПРОВЕРИТЬ и исправить границы
Обновление результатов
ПОСЛЕ каждого изменения позиций:
ВЫЧИСЛИТЬ значение целевой функции для каждого агента
ОБНОВИТЬ глобально лучшее решение:
ЕСЛИ найдено решение лучше текущего глобального:
СОХРАНИТЬ его как новое глобально лучшее
В фазе исследования также:
ОБНОВИТЬ лучшие решения каждой группы
КОНЕЦ итерации
Приступим к написанию кода алгоритма. Класс будет реализовать оптимизационный алгоритм DOA и наследоваться от базового класса "C_AO"(интерфейс для различных алгоритмов оптимизации). Конструктор и деструктор — стандартные, без дополнительных действий в деструкторе. В конструкторе задаются основные параметры алгоритма и их значения, а также происходит их связывание с массивом параметров "params", что позволяет легко управлять параметрами извне.
Основные параметры:- popSize — размер популяции, число возможных решений в каждой итерации;
- numGroups — число групп, на которые разбивается популяция для параллельного обмена информацией;
- explorationRate — доля итераций, выделяемых под исследовательскую фазу, в которой алгоритм ищет новые области пространства;
- forgettingProb — вероятность применения стратегии «забывания», позволяющая избегать застревания в локальных минимумах.
- SetParams () — устанавливает параметры класса из массива "params";
- Init () — инициализация алгоритма, задающая диапазоны поиска и число эпох;
- Moving () — выполняет один шаг оптимизации;
- Revision () — используется для пересмотра и обновления текущего состояния решения.
- currentIteration, totalIterations, explorationIters — счетчики итераций и временных рамок алгоритма;
- groupBest [] — массив, хранящий лучшие решения каждой группы, что помогает в обмене информацией и эволюции решений.
- ExplorationPhase () — отвечает за исследовательский режим поиска, расширяя горизонты поиска;
- ExploitationPhase () — фаза использования уже обнаруженных хороших решений для их улучшения;
- UpdateGroupBest () — обновление лучшего решения в конкретной группе;
- GetGroupStartIndex (), GetGroupEndIndex () — помогают определить диапазоны индексов решений внутри каждой группы.
Этот класс реализует алгоритм DOA, в котором популяция разбивается на фиксированное число групп. В ходе итераций алгоритм делит свое время между фазой исследования (поиск новых решений) и фазой эксплуатации (усовершенствование уже найденных лучших решений). Стратегия «забывания» позволяет алгоритму избегать локальных максимумов. Общая идея — обеспечить баланс между исследованием новых областей и тщательным улучшением уже обнаруженных решений, что способствует эффективной глобальной оптимизации.
//———————————————————————————————————————————————————————————————————— 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); }; //————————————————————————————————————————————————————————————————————
Метод инициализации класса "C_AO_DOA_dream" выполняет подготовительные операции для запуска алгоритма оптимизации. Он задает начальные параметры и устанавливает внутренние состояния, необходимые для дальнейших итераций.
Сначала вызывается общий метод инициализации, который проверяет и задает диапазоны поиска и шаги по ним, гарантируя, что параметры заданы корректно. Если эта проверка или настройка неудачны, инициализация прекращается.
Затем происходит установка счетчиков: текущая итерация сбрасывается в "0", общее число итераций устанавливается из переданных параметров, а количество итераций, выделенных под исследование, высчитывается как часть общего числа с учетом заданной доли (explorationRate).
Далее инициализируется массив лучших решений групп (groupBest), размер которого равен количеству групп. Для каждой группы создается начальное решение с помощью метода "Init", а значение функции качества этого решения устанавливается в минимально возможное число, чтобы обеспечить правильное сравнение и обновление в дальнейшем.
В результате, после выполнения этого метода, алгоритм готов к началу процесса оптимизации с установленными параметрами, счетчиками и начальными решениями групп.
//———————————————————————————————————————————————————————————————————— //--- Инициализация 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; } //————————————————————————————————————————————————————————————————————
Метод "Moving" является основным шагом итерации алгоритма DOA. Он реализует логику продвижения алгоритма от одной итерации к другой. Сначала увеличивается счетчик текущей итерации (currentIteration), отслеживающий прогресс алгоритма.
Первоначальная инициализация (только при первом запуске). Проверяется флаг "revision", если он "false" (то есть, алгоритм запускается впервые), выполняется начальная инициализация популяции, тогда для каждого агента в популяции и для каждой координаты агента:
- генерируется случайное значение координаты в пределах заданного диапазона (rangeMin и rangeMax) с помощью функции "u.RNDfromCI ()";
- применяется корректировка этого значения с учетом шага (rangeStep) с помощью функции "u.SeInDiSp ()", которая приводит значение к ближайшему допустимому значению (кратному шагу);
- после инициализации флаг "revision" устанавливается в "true", чтобы избежать повторной инициализации на следующих итерациях;
- в этот момент метод завершает свою работу.
- проверяется, находится ли текущая итерация в фазе исследования (currentIteration <= explorationIters);
- если итерация находится в фазе исследования, вызывается метод "ExplorationPhase ()";
- в противном случае (если итерация находится в фазе эксплуатации), вызывается метод "ExploitationPhase ()".
Таким образом, метод "Moving" управляет процессом оптимизации, обеспечивая начальную инициализацию популяции, а затем переключаясь между фазами исследования и эксплуатации на основе счетчика итераций. Начальная инициализация происходит только один раз, после чего алгоритм переходит в цикл, определяющий, какую фазу выполнять на текущей итерации.
//———————————————————————————————————————————————————————————————————— //--- Основной шаг алгоритма 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 (); } } //————————————————————————————————————————————————————————————————————
Метод "ExplorationPhase" реализует фазу исследования в алгоритме DOA. В этой фазе происходит обновление и разнообразие решений для групп агентов с целью поиска новых возможных областей пространства поиска. Для каждой группы агентов обновляется лучшее решение в группе, чтобы оно отражало текущий лучший найденный результат. Далее определяется количество измерений (размерность) для процедуры забывания, основанное на текущем номере группы и общем количестве координат. Для каждой группы рассчитываются индексы начальных и конечных агентов, входящих в нее.
Затем, для каждого агента внутри группы, к текущему решению агента копируется лучшее решение всей группы (стратегия "Memory"), что обеспечивает некоторое "старое" хорошее решение, к которому возвращаются. Создается список измерений (координат), которые будут подвергаться операции "забывания" и восполнения. Массив измерений перемешивается, чтобы случайным образом выбрать, какие из них будут изменены.
Для каждого агента в группе определяется стратегия обновления выбранных измерений с вероятностью, заданной параметром "forgettingProb", применяется стратегия "забывания" с косинусоидальной модуляцией.
Для выбранных измерений генерируется случайное смещение в пределах диапазона. Модуляция с косинусом зависит от текущей итерации и общего числа итераций, что позволяет управлять степенью изменений во времени. После обновления значение измерения приводится к валидному диапазону с учетом шагов. Или, если не выбран сценарий "забывания", применяется стратегия "dream sharing". Значения измерений копируются из случайного другого агента, то есть происходит обмен информацией между агентами.
В результате, эта фаза способствует исследованию пространства поиска, увеличивая разнообразие решений и помогая избегать локальных ловушек за счет случайных изменений и обмена информацией между агентами.
//———————————————————————————————————————————————————————————————————— //--- Фаза исследования (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]; } } } } } //————————————————————————————————————————————————————————————————————
Метод "ExploitationPhase" реализует фазу эксплуатации в алгоритме оптимизации. Его основная задача — направить поверхности поиска к наилучшему найденному решению, чтобы улучшить текущие результаты.
Для каждого агента в популяции восстанавливается решение агента к глобальному лучшему решению, найденному на данный момент, что позволяет сосредоточиться на наиболее перспективных областях пространства поиска. Затем определяется количество измерений (размерность), которые будут подвергаться модификации. Обычно выбирается не менее двух и не более определенного значения, связанного с количеством координат. Создается список всех измерений (координат), который затем перемешивается случайным образом, чтобы случайным образом выбрать, какие из них будут изменены. Для каждого выбранного измерения:
- рассчитывается диапазон изменения этого измерения;
- генерируется случайное значение внутри этого диапазона;
- производится модуляция этого значения с помощью косинусоидальной функции, которая зависит от текущего номера итерации и общего количества итераций, позволяя управлять степенью воздействия изменений во времени;
- в результате, значение измерения меняется с учетом сгенерированного значения и косинусоидальной модуляции;
- после этого, значение приводится к допустимому диапазону с учетом шага дискретизации, чтобы решение оставалось валидным.
Цель этого метода — усилить уже достигнутые хорошие решения, используя случайные и управляемые модификации, что помогает войти в более оптимальную область пространства поиска и получить более качественные решения.
//———————————————————————————————————————————————————————————————————— //--- Фаза эксплуатации (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]); } } } //————————————————————————————————————————————————————————————————————
Метод "UpdateGroupBest" предназначен для определения лучшего решения внутри конкретной группы агентов. Его основные действия заключаются в следующем:
- получение индексов стартовой и конечной позиций агентов, входящих в данную группу;
- проход по всем агентам в указанной группе;
- для каждого агента сравнивается значение функции оценки (метрика качества решения) с текущим лучшим значением, сохраненным для группы;
- если у агента найдено решение с лучшим значением фитнес-функции, оно обновляет запись о лучшем решении в группе, заменяя его на самое эффективное.
Таким образом, метод обеспечивает поддержку актуальной информации о самом лучшем решении внутри каждой группы, что важно для дальнейших этапов алгоритма, таких как стратегия поиска и обновления решений.
//———————————————————————————————————————————————————————————————————— //--- Обновить лучшее решение в группе 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); } } } //————————————————————————————————————————————————————————————————————
Метод "GetGroupStartIndex" предназначен для вычисления начального индекса элементов, входящих в указанную группу в массиве решений или агентов. Он основывается на расчетах, предполагающих равномерное распределение групп по всему массиву. Основная идея — определить позицию первого агента (или элемента) в конкретной группе, исходя из номера группы, общего количества групп и общего размера популяции.
Для этого происходит умножение номера группы на общий размер популяции и деление на число групп, что дает индекс первого элемента внутри заданной группы. Этот подход обеспечивает равномерное разделение данных на группы и удобно используется для дальнейших операций, связанных с группировкой решений.
//———————————————————————————————————————————————————————————————————— //--- Получить начальный индекс группы int C_AO_DOA_dream::GetGroupStartIndex (int groupNum) { return (int)((double)groupNum * popSize / numGroups); } //————————————————————————————————————————————————————————————————————
Метод "GetGroupEndIndex" выполняет вычисление конечного индекса элементов, входящих в указанную группу. Он определяет последний элемент в группе, основываясь на номере группы, общем размере популяции и количестве групп. Расчет проводится путем умножения номера группы, увеличенного на единицу, на общий размер популяции и деления на число групп. Затем полученное значение уменьшается на единицу, чтобы определить индекс последнего элемента в группе.
Дополнительная проверка необходима для предотвращения выхода за границы массива: если вычисленный индекс превышает размер популяции, он корректируется до значения последнего валидного индекса. Такой подход обеспечивает правильное определение границ каждой группы агентов в популяции.
//———————————————————————————————————————————————————————————————————— //--- Получить конечный индекс группы 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; } //————————————————————————————————————————————————————————————————————
Метод "Revision" предназначен для обновления информации о лучшем найденном решении в процессе работы алгоритма. В первую очередь, он итерируется по всем решениям в текущей популяции. Внутри цикла, для каждого решения, проверяется значение его целевой функции и, если это значение больше текущего лучшего значения, то "fB" обновляется, принимая значение целевой функции текущего решения, а также лучшим решением становится текущее решение.
После этого метод проверяет, находится ли текущая итерация в фазе "исследования" (explorationIters), если да, то в дополнение к обновлению глобального лучшего решения, метод также рассматривает лучшие решения, найденные внутри каждой группы. Для этого цикл итерируется по всем группам, и для каждой группы сравнивается значение целевой функции лучшего решения в этой группе с текущим лучшим значением.
Если решение внутри группы лучше, то "fB" обновляется, а решение из "groupBest" копируется в глобальный буфер "cB". Таким образом, метод "Revision" постоянно отслеживает и обновляет информацию о лучшем найденном решении в зависимости от текущей фазы алгоритма (поиска или разработки), обеспечивая сохранение наилучшего варианта решения, найденного на текущий момент.
//———————————————————————————————————————————————————————————————————— //--- Обновление лучшего и худшего решений 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); } } } } //————————————————————————————————————————————————————————————————————
Результаты тестов
Теперь, после реализации алгоритма DOA, мы можем перейти непосредственно к тестированию на тестовых функциях. Как можно заметить, алгоритм DOA набрал 53,62% и будет занесен в нашу рейтинговую таблицу.
=============================
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%)
На визуализации работы алгоритма DOA на малых размерностях (зеленые линии) заметен разброс результатов, особенно на функциях "Forest" и "Megacity".
DOA на тестовой функции Hilly
DOA на тестовой функции Forest
DOA на тестовой функции Megacity
По результатам тестирования алгоритм DOA занимает 26 место в общем рейтинге популяционных алгоритмов оптимизации.
№ | 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 |
Выводы
Dream Optimization Algorithm демонстрирует достаточную эффективность, набрав 53,62% из 100% возможных в проведенных тестах, оказавшись в нашей рейтинговой таблице.
Анализ результатов выявляет характерную для многих метаэвристических алгоритмов закономерность — существенное снижение производительности с ростом размерности задачи. На малых размерностях DOA показывает результаты в диапазоне 73-86%, при переходе к задачам высокой размерности эффективность падает до 18-37%, за конечное число итераций.
В целом алгоритм является настоящим "середнячком" среди других методов оптимизации. Желающие могут поэкспериментировать с настройками алгоритма, возможно, еще есть потенциал для получения лучших результатов.
Рисунок 2. Цветовая градация алгоритмов по соответствующим тестам
Рисунок 3. Гистограмма результатов тестирования алгоритмов (по шкале от 0 до 100, чем больше, тем лучше, где 100 — максимально возможный теоретический результат, в архиве скрипт для расчета рейтинговой таблицы)
Плюсы и минусы алгоритма DOA:
Плюсы:
- Простая реализация.
- Быстрый.
Минусы:
- Разброс значений на функциях малой размерности.
К статье прикреплён архив с актуальными версиями кодов алгоритмов. Автор статьи не несёт ответственности за абсолютную точность в описании канонических алгоритмов, во многие из них были внесены изменения для улучшения поисковых возможностей. Выводы и суждения, представленные в статьях, основываются на результатах проведённых экспериментов.
Программы, используемые в статье
# | Имя | Тип | Описание |
---|---|---|---|
1 | #C_AO.mqh | Включаемый файл | Родительский класс популяционных алгоритмов оптимизации |
2 | #C_AO_enum.mqh | Включаемый файл | Перечисление популяционных алгоритмов оптимизации |
3 | TestFunctions.mqh | Включаемый файл | Библиотека тестовых функций |
4 | TestStandFunctions.mqh | Включаемый файл | Библиотека функций тестового стенда |
5 | Utilities.mqh | Включаемый файл | Библиотека вспомогательных функций |
6 | CalculationTestResults.mqh | Включаемый файл | Скрипт для расчета результатов в сравнительную таблицу |
7 | Testing AOs.mq5 | Скрипт | Единый испытательный стенд для всех популяционных алгоритмов оптимизации |
8 | Simple use of population optimization algorithms.mq5 | Скрипт | Простой пример использования популяционных алгоритмов оптимизации без визуализации |
9 | Test_AO_DOA.mq5 | Скрипт | Испытательный стенд для DOA |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.




- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования