Алгоритм оптимизации Архимеда — Archimedes Optimization Algorithm (AOA)
Содержание
Введение
Проделаем мысленный эксперимент. Возьмём высокий стакан воды и опустим в него три предмета. Пробка мгновенно всплывает и прижимается к поверхности. Камень также быстро уходит на дно. А третий предмет — аккуратно подобранный по весу — повисает где-то посередине, не поднимаясь и не опускаясь. Он нашёл то, чего не нашли первые два: равновесие. Его плотность совпала с плотностью воды вокруг, и результирующая сила, действующая на него, обнулилась.
Задержим этот образ. Теперь представим, что водяной столб — это пространство поиска, а нужная плотность — решение некоторой задачи оптимизации. Тогда предмет, нашедший нейтральную плавучесть, в самом буквальном смысле эту задачу решил: он свёл действующую на себя силу к нулю. Именно эта идея лежит в основе Archimedes Optimization Algorithm (AOA), предложенного Hashim и соавторами в 2021 году, и именно она необычна.
Мы привыкли думать об оптимизации как о восхождении: есть ландшафт, есть вершина, и задача — на неё забраться. AOA не карабкается вверх. Он расслабляется к равновесию. Оптимум здесь переопределён — это не самая высокая точка, а состояние, в котором система пришла в покой. И агент в этой модели — не привычный голый вектор координат, как почти во всех роевых методах, а физический объект, у которого есть плотность, объём и ускорение, и у каждого из этих свойств своя собственная динамика.
Из такой постановки даром достаётся то, ради чего другие алгоритмы городят отдельные механизмы. Бросьте горсть объектов в неспокойную воду — поначалу они хаотично сталкиваются друг с другом. Это и есть разведка: широкий, ничем не направленный обзор пространства. Турбулентность затихает — объекты перестают сталкиваться и начинают плавно дрейфовать к своим положениям равновесия. Это эксплуатация: аккуратное дожатие найденного. Переход от первого режима ко второму в AOA не задаётся убывающим коэффициентом, навешенным поверх алгоритма, — он возникает сам, по мере того как затихает турбулентность. Баланс explore/exploit становится внутренним свойством модели, а не внешней надстройкой, которую приходится подгонять руками.
Стакан воды — это, конечно, игрушка. Настоящий водяной столб, ради которого всё затевается, — это пространство параметров торговой системы: периоды индикаторов, пороги, множители риска на дискретной сетке. Ландшафт фитнес-функции изломан и зашумлён, а каждый её вызов — дорогой прогон на истории. Бюджет таких вызовов измеряется тысячами, не миллионами. Требование к оптимизатору одно: при фиксированном бюджете (на стенде — 10 000 вызовов фитнеса) стабильно выдавать высокий результат от запуска к запуску, а не эпизодически. Связку высокое среднее по повторам плюс малый разброс при заданном бюджете мы и принимаем за решение. Это и измеряет рейтинговая таблица на функциях Hilly, Forest и Megacity в трёх размерностях.
Дальше — по делу. Разберём идею и математику AOA, переведём её в псевдокод, реализуем алгоритм в фреймворке C_AO, прогоним на стандартном стенде и посмотрим, какое место он займёт в рейтинге рядом с уже разобранными алгоритмами.
Реализация алгоритма
Мысленный эксперимент из введения AOA превращает в строгий алгоритм. Каждый объект популяции — это кандидат-решение, то есть точка X в пространстве поиска. Но, в отличие от обычного роевого агента, он несёт с собой ещё три физических атрибута: плотность den, объём vol и ускорение acc. Алгоритм итеративно подстраивает все четыре величины так, чтобы популяция сходилась к равновесию, — а конфигурация равновесия и есть искомый оптимум.
Физическая основа. Закон Архимеда: на тело, погружённое в жидкость, действует выталкивающая сила, равная весу вытеснённой жидкости. Равновесие наступает, когда выталкивающая сила уравновешивает вес: результирующая сила обнуляется, и тело замирает — та самая нейтральная плавучесть третьего предмета из стакана. AOA берёт это соотношение, записанное через плотность, объём и ускорение, и делает из него правило поиска: пока на объект действует ненулевая сила, он движется; алгоритм гонит популяцию к состоянию, где движение прекращается.
Инициализация. Создаётся популяция из объектов. Каждый получает случайную стартовую позицию — формула (4):
где , — границы пространства поиска, — равномерное случайное число из . Плотность и объём задаются случайно в диапазоне — формула (5):
а ускорение — снова в границах поиска, формула (6):
Вся популяция оценивается, выбирается лучший объект, и его атрибуты запоминаются как , , , . В нашем фреймворке C_AO семантика максимизации, поэтому лучший — это объект с наибольшим значением фитнес-функции.
Обновление плотности и объёма. На каждой итерации каждый объект подтягивает свою плотность и объём к плотности и объёму текущего лучшего — формула (7):
Здесь — одно случайное число на всю популяцию за итерацию. Поскольку и оба конца отрезка лежат в , это выпуклое смешивание: плотность и объём остаются в и постепенно выравниваются по популяции — жидкость приходит к однородному состоянию.
Операторы перехода: TF и d. Переходом разведка → эксплуатация управляют две скалярные величины, зависящие только от номера итерации и максимума .
Оператор переноса (transfer operator) — формула (8):
Он монотонно растёт примерно от в начале до в конце. Это часы затихающей турбулентности из введения: малое — вода неспокойна, объекты сталкиваются (разведка); большое — вода успокоилась, объекты оседают (эксплуатация).
Фактор уменьшения плотности (density decreasing factor) — формула (9):
Он убывает примерно от до нуля. Если выбирает режим, то задаёт масштаб шага: широкие прыжки в начале, всё более мелкая подстройка к концу.
Обновление ускорения. Ускорение пересчитывается каждую итерацию, и формула зависит от режима, заданного .
Фаза столкновений (малое ) — формула (10). Объект считается столкнувшимся со случайно выбранным объектом , и его новое ускорение следует из баланса выталкивающих сил для этой пары:
Ускорение диктуется случайным соседом — источник хаотичной, ненаправленной разведки.
Фаза без столкновений (большое ) — формула (11). Столкновения затихли, и ускорение объекта диктуется уже лучшим объектом:
Теперь тяга каждого объекта направлена к конфигурации лидера — это направленная эксплуатация.
Замечание о пороге. В тексте оригинальной статьи граница между фазами описана значением . В C_AO — для ускорения используется порог , а для обновления позиций ниже 0, для нашей реализации.
Нормализация ускорения. Сырое ускорение из формул (10)–(11) может принимать значения в широком диапазоне, поэтому оно нормализуется по всей популяции в фиксированную полосу — формула (12):
где , , а и берутся по всем объектам и всем координатам сразу. Результат лежит в . Это и есть механизм самодиагностики, упомянутый во введении: нормализованное ускорение работает как индивидуальный регулятор длины шага. Объект далеко от оптимума получает высокое значение — ему ещё нужно много двигаться; объект рядом с оптимумом получает низкое — ему пора лишь дошлифовывать. Переход explore/exploit оказывается не только глобальным (через и ), но и локальным, у каждого объекта своим.
Обновление позиций. Наконец объект перемещается, и режим снова выбирает .
Фаза разведки (малое ) — формула (13), применяется покоординатно:
Объект шагает в сторону случайно выбранного объекта Xrand; — фиксированный масштаб. Шаг модулируется нормализованным ускорением (насколько двигаться) и фактором (общее сжатие). Случайная цель плюс большое в начале дают широкий охват пространства.
Фаза эксплуатации (большое ) — формула (14), покоординатно:
Теперь объект движется относительно глобально лучшего , . Добавляются две величины — формула (15):
Множитель растёт вместе с и задаёт, насколько сильно притягивает объект. Знак шага выбирает : при объект приближается к лидеру с одной стороны (знак +), иначе — с другой (знак −). Этот выбор знака позволяет объектам прощупывать обе стороны от лидера, а не схлопываться все в одну точку, — защита от преждевременной сходимости. и — две настраиваемые ручки алгоритма (по умолчанию , ); , , , зафиксированы.
Завершение итерации. Новые позиции загоняются в границы поиска и оцениваются. AOA применяет жадный отбор: объект принимает новую позицию только если она улучшила фитнес, иначе остаётся на месте. При этом плотность, объём и ускорение продолжают эволюционировать независимо от того, принялась позиция или нет, — это память объекта о физическом процессе. Лучший объект обновляется, и цикл повторяется, пока не исчерпан бюджет вызовов фитнеса. По мере роста оператор 1, а фактор — к нулю: столкновения прекращаются, шаги сжимаются, популяция оседает. Жидкость приходит в покой — и её равновесная конфигурация и есть ответ.

Рисунок 1. Иллюстрация работы алгоритма AOA
На иллюстрации: три панели — одно и то же пространство поиска с фиксированным оптимумом (звезда = точка равновесия), популяция показана на трёх стадиях работы. Слева разведка — объекты разбросаны широко, длинные хаотичные шаги, между близкими парами вспышки столкновений. По центру переход — рой стягивается к лучшему объекту, шаги средней длины направлены внутрь. Справа эксплуатация — плотный кластер у оптимума, шаги короткие. Снизу градиентная полоса TF (коралл → бирюза) связывает стадии: тот же оператор переноса, что управляет развилками на схеме потока.
Составим псевдокод алгоритма AOA.
ВХОД: N — число объектов (Materials_no) T_max — максимум итераций dim, lb, ub — размерность и границы поиска C1 = 2, C2 = 6 — фиксированные константы C3, C4 — настраиваемые (обычно C3 ∈ {1,2}, C4 ≈ 0.5) u = 0.9, l = 0.1 — диапазон нормализации ускорения, Eq.(12) ──────────── ИНИЦИАЛИЗАЦИЯ ──────────── для каждого объекта i = 1..N: X[i] = lb + rand(dim) · (ub − lb) // позиции, Eq.(4) den[i] = rand(dim) // плотность, Eq.(5) vol[i] = rand(dim) // объём, Eq.(5) acc[i] = lb + rand(dim) · (ub − lb) // ускорение, Eq.(6) Y[i] = f(X[i]) (Scorebest, idx) = min(Y) Xbest = X[idx]; den_best = den[idx]; vol_best = vol[idx]; acc_best = acc[idx] acc_norm = acc ──────────── ОСНОВНОЙ ЦИКЛ ──────────── для t = 1..T_max: //--- глобальные коэффициенты перехода TF = exp((t − T_max) / T_max) // оператор переноса, Eq.(8) если TF > 1: TF = 1 d = exp((T_max − t)/T_max) − (t/T_max) // фактор плотности, Eq.(9) acc = acc_norm // ускорение с прошлой итерации r = rand() // одно общее число на итерацию // --- обновление плотности, объёма, ускорения для каждого объекта i = 1..N: den[i] = den[i] + r · (den_best − den[i]) // Eq.(7) vol[i] = vol[i] + r · (vol_best − vol[i]) // Eq.(7) если TF ≤ 0.45: // СТОЛКНОВЕНИЯ (разведка) mr = случайный индекс 1..N acc_tmp[i] = (den[mr] + vol[mr]·acc[mr]) / (rand · den[i]·vol[i]) // Eq.(10) иначе: // БЕЗ СТОЛКНОВЕНИЙ (эксплуатация) acc_tmp[i] = (den_best + vol_best·acc_best) / (rand · den[i]·vol[i]) // Eq.(11) // --- нормализация ускорения по ВСЕЙ популяции a_min = min(acc_tmp по всем i и j) a_max = max(acc_tmp по всем i и j) для каждого i, j: acc_norm[i,j] = u · (acc_tmp[i,j] − a_min)/(a_max − a_min) + l // Eq.(12) // --- обновление позиций для каждого объекта i = 1..N: если TF ≤ 0.4: // РАЗВЕДКА для каждого измерения j: mrand = случайный индекс 1..N Xnew[i,j] = X[i,j] + C1·rand·acc_norm[i,j]· (X[mrand,j] − X[i,j])·d // Eq.(13) иначе: // ЭКСПЛУАТАЦИЯ для каждого измерения j: p = 2·rand − C4 // Eq.(15) T = C3 · TF; если T > 1: T = 1 если p ≤ 0.5: Xnew[i,j] = Xbest[j] + C2·rand·acc_norm[i,j]· (T·Xbest[j] − X[i,j])·d // Eq.(14) иначе: Xnew[i,j] = Xbest[j] − C2·rand·acc_norm[i,j]· (T·Xbest[j] − X[i,j])·d // --- контроль границ Xnew = clamp(Xnew, lb, ub) // --- жадный отбор для каждого объекта i = 1..N: v = f(Xnew[i]) если v < Y[i]: X[i] = Xnew[i]; Y[i] = v // --- обновление лучшего решения (Ybest, idx) = min(Y) если Ybest < Scorebest: Scorebest = Ybest Xbest = X[idx] den_best = den[idx]; vol_best = vol[idx]; acc_best = acc_norm[idx] ВЫХОД: Xbest, Scorebest
Можем перейти к рассмотрению реализации алгоритма AOA.
Поля класса. Агент фреймворка ( S_AO_Agent ) несёт только координаты ( c, cP, cB, cW) и значения фитнеса. Но объекту AOA нужны ещё три физических атрибута — плотность, объём, ускорение, — и у каждого своя динамика. Поэтому они вынесены в отдельные приватные буферы класса: den , vol , accNorm и промежуточный accTemp. Все четыре — плоские одномерные массивы размером popSize * coords, элемент объекта i по координате c адресуется как [i * coords + c]. Плоская раскладка вместо двумерной выбрана ради простоты и предсказуемого доступа к памяти.
Ещё три буфера размером coords — denBest, volBest, accBest — хранят атрибуты лучшего объекта; они нужны в формуле (11). Пара snap_c и snap_f — снимок популяции (о нём ниже). Наконец, t и Tmax — счётчик и горизонт расписания итераций. Публичными остаются только две настраиваемые ручки — C3 и C4.
Конструктор задаёт паспорт алгоритма — имя AOA, описание, ссылку на статью — и значения по умолчанию: popSize = 25, C3 = 2.0, C4 = 0.5. Затем массив params расширяется до трёх элементов, и в каждый записывается имя и значение параметра. Через этот массив тестовый стенд видит и выставляет настройки алгоритма.
SetParams(). Метод считывает значения из params[] обратно в поля popSize, C3, C4 и ставит предохранители: размер популяции не меньше 2 (фазе столкновений нужен хотя бы один сосед), коэффициенты C3 и C4 не отрицательны. SetParams() вызывается до Init(), поэтому к моменту выделения памяти popSize уже корректен.
//+------------------------------------------------------------------+ class C_AO_AOAarch : public C_AO { public: ~C_AO_AOAarch() {} C_AO_AOAarch() { ao_name = "AOA"; ao_desc = "Archimedes Optimization Algorithm"; ao_link = "https://www.mql5.com/ru/articles/22648"; popSize = 25; C3 = 2.0; C4 = 0.5; ArrayResize(params, 3); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "C3"; params [1].val = C3; params [2].name = "C4"; params [2].val = C4; } void SetParams() { popSize = (int)params [0].val; C3 = params [1].val; C4 = params [2].val; //--- предохранители if(popSize < 2) // столкновениям нужен хотя бы один сосед popSize = 2; if(C3 < 0.0) C3 = 0.0; if(C4 < 0.0) C4 = 0.0; } bool Init(const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0); void Moving(); void Revision(); //--- видимые параметры double C3; // масштаб влияния лучшего: T = C3*TF, Eq.(15) double C4; // смещение знака шага: p = 2*rand - C4, Eq.(15) private: //--- физические атрибуты объектов (плоские буферы popSize*coords) double den []; // плотность, Eq.(5),(7) double vol []; // объём, Eq.(5),(7) double accNorm []; // ускорение (нормализованное), Eq.(12) double accTemp []; // ускорение до нормализации, Eq.(10),(11) //--- атрибуты лучшего объекта (буферы coords) double denBest []; double volBest []; double accBest []; //--- snapshot для генерации кандидатов и greedy acceptance double snap_c []; // popSize*coords double snap_f []; // popSize //--- расписание итераций int t; // номер текущей рабочей итерации (1..Tmax) int Tmax; // полное число рабочих итераций }; //+------------------------------------------------------------------+
Init(). Инициализация начинается с StandardInit() — базовый класс пересоздаёт массив агентов, копирует диапазоны поиска, сбрасывает fB и флаг revision. Дальше идёт специфика AOA.
Сначала настраивается расписание: Tmax = epochsP - 1. Вычитание единицы здесь принципиально — первая эпоха внешнего цикла уходит на оценку стартовой популяции, рабочих итераций остаётся epochsP - 1, и именно на этот горизонт должны быть растянуты операторы TF и d. Счётчик t обнуляется, Tmax страхуется снизу единицей.
Затем выделяются все буферы и заполняется стартовое состояние популяции: координаты — случайно в границах поиска (формула 4, кладутся в cP), плотность и объём — случайно из [0,1] (формула 5), ускорение — случайно в границах поиска (формула 6). Тонкость: стартовое ускорение пишется сразу в accNorm, а не в отдельный буфер. Так сделано потому, что на первой рабочей итерации ускорение acc совпадает с acc_norm, и хранить их раздельно нет смысла.
//+------------------------------------------------------------------+ //| Init | //+------------------------------------------------------------------+ bool C_AO_AOAarch::Init(const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if(!StandardInit(rangeMinP, rangeMaxP, rangeStepP)) return false; //--- расписание: первая эпоха уходит на оценку стартовой популяции, //--- поэтому рабочих итераций epochsP - 1 Tmax = epochsP - 1; if(Tmax < 1) Tmax = 1; t = 0; //--- буферы ArrayResize(den, popSize * coords); ArrayResize(vol, popSize * coords); ArrayResize(accNorm, popSize * coords); ArrayResize(accTemp, popSize * coords); ArrayResize(denBest, coords); ArrayResize(volBest, coords); ArrayResize(accBest, coords); ArrayResize(snap_c, popSize * coords); ArrayResize(snap_f, popSize); //--- стартовая популяция и физические атрибуты for(int i = 0; i < popSize; i++) { for(int c = 0; c < coords; c++) { //--- позиции, Eq.(4) — в cP[], попадут в c[] на первом Moving a [i].cP [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]); //--- плотность и объём ~ U[0,1], Eq.(5) den [i * coords + c] = u.RNDfromCI(0.0, 1.0); vol [i * coords + c] = u.RNDfromCI(0.0, 1.0); //--- ускорение ~ U[lb,ub], Eq.(6). //--- Кладём в accNorm: на первой итерации acc = acc_norm = это значение accNorm [i * coords + c] = u.RNDfromCI(rangeMin [c], rangeMax [c]); } } return true; } //+------------------------------------------------------------------+
Moving(). Это сердце алгоритма. Первый вызов — особый: пока revision равен false, метод просто переносит стартовые координаты из cP в c (через u.SeInDiSp, с приведением к сетке и границам) и выходит. Это даёт внешнему циклу оценить стартовую популяцию.
На рабочих вызовах сначала фиксируются константы алгоритма — C1 = 2, C2 = 6, пределы нормализации uN = 0.9, lN = 0.1, пороги фаз TFc = 0.45 и TFx = 0.40, а также EPS для защиты знаменателей. Дальше итерация идёт по этапам.
Этап 0 — снимок. Координаты и фитнес всех агентов копируются в snap_c и snap_f. Снимок нужен по двум причинам. Во-первых, формулы (13) и (14) читают позиции на начало итерации, а метод тут же перезаписывает a[i].c новыми кандидатами — без снимка обработанный раньше агент исказил бы вход для обработанного позже. Во-вторых, снимок — основа жадного отбора в Revision().
Этап 1 — расписание. Счётчик t увеличивается, считаются оператор переноса TF (формула 8, с потолком 1), фактор плотности d (формула 9) и общее на всю итерацию случайное число r (формула 7).
Этап 2 — атрибуты. В одном цикле по агентам сначала обновляются плотность и объём — они подтягиваются к лучшему прямо на месте, in-place (формула 7), — затем считается сырое ускорение accTemp. Если TF ниже порога TFc, это фаза столкновений: выбирается случайный объект mr, ускорение берётся от него (формула 10). Иначе — фаза без столкновений, ускорение берётся от лучшего (формула 11). Обновление den / vol на месте означает, что объект с меньшим индексом уже обновлён к моменту, когда его читает объект с большим, — это сознательно повторяет порядок эталонного кода. Проверка EPS в знаменателе не даёт получить inf или nan, если плотность, объём или случайный множитель окажутся нулевыми.
Этап 3 — нормализация. По всему буферу accTemp (все объекты, все координаты) находятся глобальные минимум и максимум, и ускорение линейно отображается в полосу [lN, uN] (формула 12), результат пишется в accNorm. Вырожденный случай, когда все ускорения равны, обрабатывается отдельно.
Этап 4 — позиции. Считается множитель T = C3 · TF с потолком 1 (формула 15). Затем по агентам: если TF ниже TFx — фаза разведки, шаг в сторону случайного объекта (формула 13); иначе — фаза эксплуатации, шаг относительно глобально лучшего cB , причём знак шага на каждой координате выбирается величиной p (формулы 14–15). Все новые координаты пропускаются через u.SeInDiSp — это покоординатный контроль границ и шага сетки, заменивший дефектную проверку fun_checkpositions из оригинала.
//+------------------------------------------------------------------+ //| Moving | //| | //| Структура рабочей итерации: | //| 0. snapshot текущей популяции; | //| 1. коэффициенты перехода TF (Eq.8) и d (Eq.9); | //| 2. обновление den/vol (Eq.7) + расчёт accTemp: | //| TF < 0.45 — столкновения, Eq.(10); | //| иначе — без столкновений, Eq.(11); | //| 3. нормализация ускорения по всей популяции, Eq.(12); | //| 4. обновление позиций: | //| TF < 0.4 — разведка, Eq.(13); | //| иначе — эксплуатация, Eq.(14)-(15). | //+------------------------------------------------------------------+ void C_AO_AOAarch::Moving() { //--- первый прогон: cP -> c для оценки стартовой популяции if(!revision) { for(int i = 0; i < popSize; i++) for(int c = 0; c < coords; c++) a [i].c [c] = u.SeInDiSp(a [i].cP [c], rangeMin [c], rangeMax [c], rangeStep [c]); return; } //--- константы алгоритма (Hashim et al., 2021) const double C1 = 2.0; // масштаб шага разведки, Eq.(13) const double C2 = 6.0; // масштаб шага эксплуатации, Eq.(14) const double uN = 0.9; // верхний предел нормализации, Eq.(12) const double lN = 0.1; // нижний предел нормализации, Eq.(12) const double TFc = 0.45; // порог фазы столкновений const double TFx = 0.40; // порог фазы разведки позиций const double EPS = 1e-300; // защита знаменателей от inf/nan //--- 0. snapshot for(int i = 0; i < popSize; i++) { for(int c = 0; c < coords; c++) snap_c [i * coords + c] = a [i].c [c]; snap_f [i] = a [i].f; } //--- 1. коэффициенты перехода t++; double tt = 1; //(double)t; double Tm = (double)Tmax; double TF = MathExp((tt - Tm) / Tm); // оператор переноса, Eq.(8) if(TF > 1.0) TF = 1.0; double d = MathExp((Tm - tt) / Tm) - (tt / Tm); // фактор плотности, Eq.(9) double r = u.RNDfromCI(0.0, 1.0); // общий на итерацию, Eq.(7) //--- 2. обновление den/vol + расчёт accTemp for(int i = 0; i < popSize; i++) { //--- плотность и объём тянутся к лучшему (in-place), Eq.(7) for(int c = 0; c < coords; c++) { int idx = i * coords + c; den [idx] += r * (denBest [c] - den [idx]); vol [idx] += r * (volBest [c] - vol [idx]); } double rnd = u.RNDfromCI(0.0, 1.0); // свежий скаляр на объект if(TF < TFc) { //--- ФАЗА СТОЛКНОВЕНИЙ (разведка), Eq.(10) int mr = (int)u.RNDfromCI(0.0, (double)popSize); if(mr >= popSize) mr = popSize - 1; if(mr < 0) mr = 0; for(int c = 0; c < coords; c++) { int idx = i * coords + c; int idxR = mr * coords + c; double denom = rnd * den [idx] * vol [idx]; if(MathAbs(denom) < EPS) denom = (denom < 0.0 ? -EPS : EPS); accTemp [idx] = (den [idxR] + vol [idxR] * accNorm [idxR]) / denom; } } else { //--- БЕЗ СТОЛКНОВЕНИЙ (эксплуатация), Eq.(11) for(int c = 0; c < coords; c++) { int idx = i * coords + c; double denom = rnd * den [idx] * vol [idx]; if(MathAbs(denom) < EPS) denom = (denom < 0.0 ? -EPS : EPS); accTemp [idx] = (denBest [c] + volBest [c] * accBest [c]) / denom; } } } //--- 3. нормализация ускорения по всей популяции, Eq.(12) int total = popSize * coords; double gMin = accTemp [0]; double gMax = accTemp [0]; for(int k = 1; k < total; k++) { double v = accTemp [k]; if(v < gMin) gMin = v; if(v > gMax) gMax = v; } double span = gMax - gMin; for(int k = 0; k < total; k++) { if(span > EPS) accNorm [k] = uN * (accTemp [k] - gMin) / span + lN; else accNorm [k] = lN; // вырожденный случай: все ускорения равны } //--- 4. обновление позиций double T = C3 * TF; // Eq.(15) if(T > 1.0) T = 1.0; for(int i = 0; i < popSize; i++) { if(TF < TFx) { //--- ФАЗА РАЗВЕДКИ, Eq.(13) for(int c = 0; c < coords; c++) { int mrand = (int)u.RNDfromCI(0.0, (double)popSize); if(mrand >= popSize) mrand = popSize - 1; if(mrand < 0) mrand = 0; int idx = i * coords + c; double x_old = snap_c [idx]; double x_rnd = snap_c [mrand * coords + c]; double v = x_old + C1 * u.RNDfromCI(0.0, 1.0) * accNorm [idx] * (x_rnd - x_old) * d; a [i].c [c] = u.SeInDiSp(v, rangeMin [c], rangeMax [c], rangeStep [c]); } } else { //--- ФАЗА ЭКСПЛУАТАЦИИ, Eq.(14) for(int c = 0; c < coords; c++) { int idx = i * coords + c; double p = 2.0 * u.RNDfromCI(0.0, 1.0) - C4; // Eq.(15) double step = C2 * u.RNDfromCI(0.0, 1.0) * accNorm [idx] * (T * cB [c] - snap_c [idx]) * d; double v; if(p < 0.5) v = cB [c] + step; else v = cB [c] - step; a [i].c [c] = u.SeInDiSp(v, rangeMin [c], rangeMax [c], rangeStep [c]); } } } } //+------------------------------------------------------------------+
Revision(). Метод завершает итерацию. Первый вызов снова особый: стартовая популяция только что оценена, поэтому метод обновляет личные лучшие значения агентов и глобальное fB / cB, запоминает индекс лучшего объекта и снимает с него атрибуты в denBest, volBest, accBest, после чего ставит revision = true и выходит. Жадного отбора здесь нет — снимка ещё не существует.
На рабочих вызовах первым делом идёт жадный отбор: если новый фитнес агента хуже значения из снимка, координаты и фитнес откатываются к snap_c / snap_f. Важно, что атрибуты den, vol, acc откату не подлежат — они эволюционируют независимо. После отбора обновляются личные и глобальные лучшие, и, если глобальный оптимум улучшился, с нового лучшего объекта снимаются den / vol / accNorm в буферы лучшего. Порядок — сначала отбор, потом выбор лучшего — повторяет эталонную реализацию: лучший выбирается уже из принятой популяции.
//+------------------------------------------------------------------+ //| Revision | //| | //| Greedy acceptance: позиция/фитнес агента откатываются к | //| снапшоту, если новый кандидат оказался хуже. Атрибуты | //| den/vol/acc откату НЕ подлежат — они эволюционируют независимо. | //| Затем обновляются лучшие решения и атрибуты лучшего объекта. | //+------------------------------------------------------------------+ void C_AO_AOAarch::Revision() { //--- первый прогон: оценка стартовой популяции, выбор лучшего if(!revision) { int bestIdx = -1; for(int i = 0; i < popSize; i++) { if(a [i].f > a [i].fB) { a [i].fB = a [i].f; ArrayCopy(a [i].cB, a [i].c, 0, 0, coords); } if(a [i].f > fB) { fB = a [i].f; ArrayCopy(cB, a [i].c, 0, 0, coords); bestIdx = i; } } //--- атрибуты лучшего объекта стартовой популяции if(bestIdx >= 0) { for(int c = 0; c < coords; c++) { denBest [c] = den [bestIdx * coords + c]; volBest [c] = vol [bestIdx * coords + c]; accBest [c] = accNorm [bestIdx * coords + c]; // сырое ускорение Eq.(6) } } revision = true; return; } //--- greedy acceptance: откат, если стало хуже for(int i = 0; i < popSize; i++) { if(a [i].f < snap_f [i]) { for(int c = 0; c < coords; c++) a [i].c [c] = snap_c [i * coords + c]; a [i].f = snap_f [i]; } } //--- обновление личных/глобального лучших + захват атрибутов лучшего int bestIdx = -1; for(int i = 0; i < popSize; i++) { if(a [i].f > a [i].fB) { a [i].fB = a [i].f; ArrayCopy(a [i].cB, a [i].c, 0, 0, coords); } if(a [i].f > fB) { fB = a [i].f; ArrayCopy(cB, a [i].c, 0, 0, coords); bestIdx = i; } } //--- den/vol/acc лучшего обновляются только при улучшении глобума if(bestIdx >= 0) { for(int c = 0; c < coords; c++) { denBest [c] = den [bestIdx * coords + c]; volBest [c] = vol [bestIdx * coords + c]; accBest [c] = accNorm [bestIdx * coords + c]; } } } //+------------------------------------------------------------------+
Результаты тестов
Прогон AOA на стандартном стенде даёт общий результат 39,66%. Этого, к сожалению, недостаточно, чтобы войти в рейтинговую таблицу. Но интереснее не сама цифра, а то, как она складывается, об этом подробнее в выводах.
=============================
5 Hilly's; Func runs: 10000; result: 0.8206488162634686
25 Hilly's; Func runs: 10000; result: 0.4924635010346886
500 Hilly's; Func runs: 10000; result: 0.2768438671570971
=============================
5 Forest's; Func runs: 10000; result: 0.6788879303684001
25 Forest's; Func runs: 10000; result: 0.24788382890806288
500 Forest's; Func runs: 10000; result: 0.06499823135837557
=============================
5 Megacity's; Func runs: 10000; result: 0.5906666666666667
25 Megacity's; Func runs: 10000; result: 0.27759999999999996
500 Megacity's; Func runs: 10000; result: 0.11920000000000104
=============================
All score: 3.56919 (39.66%)
Тест анти-чит показывает подобную динамику первых показателей функций предыдущего основного теста. Честный алгоритм.
AOA|Archimedes Optimization Algorithm|25.0|2.0|0.5|
=============================
Composite anti-cheat test: Hilly + Forest + Megacity + Peaks + Skin
Coordinates: 10; Epochs: 400; Repeats: 10
=============================
Run 1/10: 0.6677793808019812
Run 2/10: 0.6355386750189551
Run 3/10: 0.5813048402935594
Run 4/10: 0.7378557003396822
Run 5/10: 0.7012834397505456
Run 6/10: 0.6202804626691847
Run 7/10: 0.8256544601079605
Run 8/10: 0.7089113881121764
Run 9/10: 0.9011830945159774
Run 10/10: 0.9514789578735151
=============================
Average result: 0.7331270399 (73.31%)
=============================
Визуализация работы алгоритма на наших трёх первых тестовых функциях и дополнительно на некоторых других.

AOA на тестовой функции Hilly

AOA на тестовой функции Forest

AOA на тестовой функции Megacity

AOA на функции Rosenbrock

AOA на функции Ackley
По результатам тестирования алгоритм AOA представлен в рейтинговой таблице ознакомительно.
| cc | 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 | 1,00000 | 0,88228 | 0,40138 | 2,28366 | 1,00000 | 0,95281 | 0,28092 | 2,23373 | 0,94667 | 0,85733 | 0,22389 | 2,02789 | 6,545 | 72,72 |
| 2 | AMOm | animal migration optimization M | 0,91624 | 0,83603 | 0,46790 | 2,22017 | 0,98482 | 0,92010 | 0,36391 | 2,26883 | 0,91733 | 0,81707 | 0,25177 | 1,98617 | 6,475 | 71,94 |
| 3 | CLA | code lock algorithm (joo) | 0,95139 | 0,86199 | 0,37879 | 2,19217 | 0,99349 | 0,93500 | 0,26497 | 2,19346 | 0,93600 | 0,84267 | 0,24060 | 2,01927 | 6,405 | 71,17 |
| 4 | (P+O)ES | (P+O) evolution strategies | 0,86571 | 0,89539 | 0,39740 | 2,15850 | 0,97761 | 0,89820 | 0,26878 | 2,14459 | 0,92133 | 0,80240 | 0,23952 | 1,96325 | 6,266 | 69,62 |
| 5 | SDSm | stochastic diffusion search M | 0,95195 | 0,84944 | 0,36249 | 2,16388 | 0,98061 | 0,88457 | 0,22112 | 2,08630 | 0,92267 | 0,79013 | 0,21380 | 1,92660 | 6,177 | 68,63 |
| 6 | AAm | archery algorithm M | 0,84685 | 0,73320 | 0,42590 | 2,00595 | 0,96709 | 0,77837 | 0,27789 | 2,02335 | 0,86133 | 0,77707 | 0,28712 | 1,92552 | 5,955 | 66,17 |
| 7 | SIA | simulated isotropic annealing (joo) | 0,93543 | 0,86504 | 0,38483 | 2,18530 | 0,94069 | 0,80609 | 0,23835 | 1,98513 | 0,86400 | 0,66160 | 0,19536 | 1,72096 | 5,891 | 65,46 |
| 8 | TETA | time evolution travel algorithm (joo) | 0,91452 | 0,86369 | 0,25579 | 2,03400 | 0,99654 | 0,91291 | 0,14394 | 2,05339 | 0,85467 | 0,82213 | 0,10443 | 1,78123 | 5,869 | 65,21 |
| 9 | ESG | evolution of social groups (joo) | 0,98111 | 0,79857 | 0,31167 | 2,09135 | 0,98954 | 0,82270 | 0,15032 | 1,96256 | 0,92133 | 0,73440 | 0,15315 | 1,80888 | 5,863 | 65,14 |
| 10 | CTA | comet tail algorithm (joo) | 0,92435 | 0,86786 | 0,27838 | 2,07059 | 0,99039 | 0,84571 | 0,19448 | 2,03058 | 0,95467 | 0,69680 | 0,11008 | 1,76155 | 5,863 | 65,14 |
| 11 | ECBO | enhanced colliding bodies optimization | 0,94024 | 0,72363 | 0,32356 | 1,98743 | 0,99477 | 0,80291 | 0,13056 | 1,92824 | 0,87600 | 0,70160 | 0,17433 | 1,75193 | 5,668 | 62,98 |
| 12 | DA | dialectical algorithm | 0,93117 | 0,75400 | 0,26205 | 1,94722 | 0,98925 | 0,81375 | 0,08662 | 1,88962 | 0,92667 | 0,68107 | 0,11315 | 1,72089 | 5,558 | 61,76 |
| 13 | BBO | biogeography based optimization | 0,95876 | 0,70609 | 0,35752 | 2,02237 | 0,92981 | 0,70660 | 0,16970 | 1,80611 | 0,87467 | 0,63013 | 0,20813 | 1,71293 | 5,541 | 61,57 |
| 14 | BHAm | black hole algorithm M | 0,79558 | 0,76207 | 0,34682 | 1,90447 | 0,99836 | 0,75798 | 0,13826 | 1,89460 | 0,85067 | 0,64427 | 0,17020 | 1,66514 | 5,464 | 60,71 |
| 15 | HS | harmony search | 0,91420 | 0,69049 | 0,29924 | 1,90393 | 0,97627 | 0,73373 | 0,14193 | 1,85193 | 0,91733 | 0,62720 | 0,15364 | 1,69817 | 5,454 | 60,60 |
| 16 | RFO | royal flush optimization (joo) | 0,80989 | 0,74481 | 0,34546 | 1,90016 | 0,95251 | 0,77926 | 0,15185 | 1,88362 | 0,80400 | 0,66427 | 0,19071 | 1,65898 | 5,443 | 60,48 |
| 17 | BOAm | billiards optimization algorithm M | 0,76177 | 0,72421 | 0,25275 | 1,73873 | 0,90890 | 0,81960 | 0,28853 | 2,01703 | 0,83733 | 0,74613 | 0,09763 | 1,68109 | 5,437 | 60,41 |
| 18 | ASO | anarchy society optimization | 0,73070 | 0,73713 | 0,31195 | 1,77978 | 0,99732 | 0,87700 | 0,17619 | 2,05051 | 0,72000 | 0,68773 | 0,18988 | 1,59761 | 5,428 | 60,31 |
| 19 | EOm | extremal optimization_M | 0,76527 | 0,75205 | 0,31908 | 1,83640 | 0,99999 | 0,76426 | 0,12437 | 1,88862 | 0,84133 | 0,64133 | 0,15247 | 1,63513 | 5,360 | 59,56 |
| 20 | ACS | artificial cooperative search | 0,75545 | 0,77162 | 0,31653 | 1,84360 | 1,00000 | 0,80488 | 0,10705 | 1,91193 | 0,76933 | 0,60800 | 0,14157 | 1,51890 | 5,274 | 58,60 |
| 21 | SSG | saplings sowing and growing | 0,75436 | 0,63206 | 0,35935 | 1,74577 | 0,91907 | 0,69694 | 0,19755 | 1,81356 | 0,81867 | 0,60533 | 0,21347 | 1,63747 | 5,197 | 57,74 |
| 22 | AOSm | atomic orbital search M | 0,76184 | 0,68435 | 0,31344 | 1,75963 | 0,90015 | 0,80044 | 0,11501 | 1,81560 | 0,82800 | 0,63280 | 0,15696 | 1,61776 | 5,193 | 57,70 |
| 23 | TSEA | turtle shell evolution algorithm (joo) | 0,95809 | 0,64852 | 0,29571 | 1,90232 | 0,99522 | 0,58104 | 0,10542 | 1,68168 | 0,92133 | 0,52160 | 0,14567 | 1,58860 | 5,173 | 57,48 |
| 24 | DE | differential evolution | 0,96398 | 0,62346 | 0,26089 | 1,84833 | 0,98482 | 0,77018 | 0,11459 | 1,86959 | 0,93067 | 0,36213 | 0,11000 | 1,40280 | 5,121 | 56,90 |
| 25 | BIO | blood inheritance optimization (joo) | 0,72580 | 0,66522 | 0,31228 | 1,70330 | 0,99995 | 0,68125 | 0,11540 | 1,79660 | 0,85467 | 0,59333 | 0,15364 | 1,60164 | 5,102 | 56,69 |
| 26 | (PO)ES | (PO) evolution strategies | 0,73972 | 0,58190 | 0,38896 | 1,71058 | 0,91199 | 0,59975 | 0,21262 | 1,72436 | 0,82400 | 0,56240 | 0,23432 | 1,62072 | 5,056 | 56,18 |
| 27 | BO | bonobo optimizer | 0,75555 | 0,64366 | 0,32657 | 1,72578 | 0,94332 | 0,70442 | 0,13999 | 1,78773 | 0,73467 | 0,61440 | 0,16728 | 1,51635 | 5,030 | 55,89 |
| 28 | SRA | successful restaurateur algorithm (joo) | 0,89010 | 0,63359 | 0,29115 | 1,81484 | 0,96634 | 0,55285 | 0,08914 | 1,60833 | 0,89333 | 0,52800 | 0,13911 | 1,56044 | 4,984 | 55,38 |
| 29 | CRO | chemical reaction optimisation | 0,91281 | 0,65681 | 0,29866 | 1,86828 | 0,90513 | 0,56020 | 0,10939 | 1,57472 | 0,82800 | 0,50133 | 0,14149 | 1,47082 | 4,914 | 54,60 |
| 30 | BCOm | bacterial chemotaxis optimization M | 0,82589 | 0,61733 | 0,31584 | 1,75906 | 0,95296 | 0,63718 | 0,11984 | 1,70998 | 0,76533 | 0,51653 | 0,15800 | 1,43986 | 4,909 | 54,54 |
| 31 | DOA | dream optimization algorithm | 0,78522 | 0,78121 | 0,36036 | 1,92679 | 0,61584 | 0,42117 | 0,12254 | 1,15955 | 0,86667 | 0,72587 | 0,21127 | 1,80381 | 4,890 | 54,33 |
| 32 | ABO | african buffalo optimization | 0,92295 | 0,62528 | 0,29885 | 1,84708 | 0,92992 | 0,57468 | 0,09372 | 1,59832 | 0,73333 | 0,51333 | 0,14324 | 1,38990 | 4,835 | 53,72 |
| 33 | BSA | bird swarm algorithm | 0,94432 | 0,67941 | 0,26401 | 1,88774 | 0,91649 | 0,65619 | 0,12054 | 1,69322 | 0,80933 | 0,33547 | 0,10652 | 1,25132 | 4,832 | 53,69 |
| 34 | TSm | tabu search M | 0,87806 | 0,61040 | 0,28993 | 1,77839 | 0,98116 | 0,52165 | 0,08544 | 1,58825 | 0,82667 | 0,49547 | 0,13552 | 1,45766 | 4,824 | 53,60 |
| 35 | BSA | backtracking search algorithm | 0,87128 | 0,53190 | 0,28675 | 1,68993 | 0,92408 | 0,51602 | 0,09153 | 1,53163 | 0,96000 | 0,47253 | 0,13760 | 1,57013 | 4,792 | 53,24 |
| 36 | WOAm | whale optimization algorithm M | 0,93893 | 0,59477 | 0,26695 | 1,80065 | 0,98036 | 0,53873 | 0,07112 | 1,59021 | 0,78667 | 0,47600 | 0,11892 | 1,38159 | 4,772 | 53,02 |
| 37 | ACA | andean_condor_algorithm | 0,78444 | 0,53260 | 0,33108 | 1,64812 | 0,79071 | 0,44960 | 0,10685 | 1,34716 | 0,92266 | 0,67733 | 0,17613 | 1,77612 | 4,771 | 53,02 |
| 38 | CSO | competitive swarm optimizer | 0,85151 | 0,60786 | 0,29896 | 1,75833 | 0,84085 | 0,58491 | 0,11974 | 1,54550 | 0,80000 | 0,48560 | 0,14184 | 1,42744 | 4,731 | 52,57 |
| 39 | FBA | fractal-based algorithm | 0,69419 | 0,64267 | 0,28955 | 1,62641 | 0,99812 | 0,54905 | 0,08705 | 1,63422 | 0,76133 | 0,51253 | 0,13689 | 1,41075 | 4,671 | 51,90 |
| 40 | ECOi | eco-inspired evolutionary algorithm | 0,78817 | 0,54402 | 0,29360 | 1,62579 | 0,88996 | 0,46592 | 0,09747 | 1,45335 | 0,78533 | 0,45173 | 0,14295 | 1,38001 | 4,459 | 49,54 |
| 41 | BSO | brain storm optimization | 0,92207 | 0,57625 | 0,29732 | 1,79564 | 0,80764 | 0,42508 | 0,09448 | 1,32720 | 0,77200 | 0,36533 | 0,13065 | 1,26798 | 4,391 | 48,79 |
| 42 | CAm | camel algorithm M | 0,71534 | 0,56917 | 0,35985 | 1,64436 | 0,84094 | 0,47174 | 0,10850 | 1,42118 | 0,70400 | 0,41947 | 0,19563 | 1,31910 | 4,385 | 48,72 |
| 43 | ACOm | ant colony optimization_M | 0,71885 | 0,48410 | 0,30990 | 1,51285 | 0,75792 | 0,48639 | 0,11871 | 1,36302 | 0,83600 | 0,48667 | 0,16148 | 1,48415 | 4,360 | 48,44 |
| 44 | BSO | brain storm optimization | 0,91331 | 0,55813 | 0,29705 | 1,76849 | 0,85329 | 0,44038 | 0,09447 | 1,38814 | 0,72267 | 0,32880 | 0,13325 | 1,18472 | 4,341 | 48,23 |
| 45 | AEFA | artificial electric field algorithm | 0,88798 | 0,65769 | 0,25276 | 1,79843 | 0,95550 | 0,63602 | 0,03946 | 1,63098 | 0,60800 | 0,16000 | 0,09845 | 0,86645 | 4,296 | 47,73 |
| AOA | archimedes_optimization_algorithm | 0,82064 | 0,49246 | 0,27684 | 1,58994 | 0,67888 | 0,24788 | 0,06500 | 0,99176 | 0,59066 | 0,27759 | 0,11920 | 0,98745 | 3,569 | 39,66 | |
| RW | random walk | 0,49970 | 0,32333 | 0,25791 | 1,08094 | 0,30754 | 0,11470 | 0,04400 | 0,46624 | 0,36133 | 0,17013 | 0,10244 | 0,63390 | 2,181 | 24,23 | |
Выводы
Прогон AOA на стандартном стенде дал общий результат 39,66%. Этого оказалось недостаточно, чтобы войти в рейтинговую таблицу, — алгоритм остаётся за чертой уже разобранных лидеров. Но интереснее не сама цифра, а то, как она складывается.
Посмотрим на результаты по размерности. Усреднённо по трём функциям AOA набирает около 70% на задачах с 10 координатами, около 34% на 50 и лишь около 15% на 1000. С каждым шагом вверх по размерности оценка/результат падает примерно вдвое. На Forest это видно резче всего: 68% → 25% → 6,5%. Картина одинакова на всех трёх ландшафтах — Hilly, Forest, Megacity — и это не разброс, а устойчивая закономерность.
Такой профиль — характерная подпись преждевременной сходимости. На низкоразмерных задачах AOA работает вполне достойно: пространство тесное, заблудиться негде, и алгоритм аккуратно находит хорошую область. Но чем больше координат, тем раньше популяция схлопывается. И здесь стоит вернуться к устройству алгоритма. Разведка в AOA включена лишь пока TF мал, а пороги (0,45 и 0,4) держат эту фазу всего в первых ~8–20% итераций; дальше — сплошная эксплуатация, движение к лучшему. В пятимерной задаче этого хватает. В пятисотмерной — нет: турбулентность затихает быстрее, чем рой успевает обследовать пространство, и объекты оседают у первого же приличного оптимума, случайно найденного в начале. Жидкость приходит в покой слишком рано — и застывает не там.
Композитный анти-чит тест подтверждает, что числам можно верить. На пяти функциях с непохожей геометрией оптимума AOA набрал 73,31%, и это согласуется с его честной силой на низких размерностях — каждая функция в композите получает лишь двумерный срез. Будь у AOA геометрический чит вроде диагонального выравнивания, композит обрушил бы счёт; этого не произошло. То есть и сильная сторона, и слабая — настоящие, без артефактов.
По стабильности AOA — крепкий середняк. Разброс между повторами заметный, но не хаотичный: алгоритм не выдаёт ни стабильно высокого, ни стабильно низкого результата — он попадает в широкую, но предсказуемую полосу. На практике это значит, что одиночному прогону доверять не стоит, нужно усреднять по нескольким.
И всё же записывать AOA в неудачники не хочется. Идея — переопределить оптимизацию как поиск равновесия, а баланс explore/exploit отдать физике — красива и работоспособна; на низких размерностях она себя оправдывает. Слабое место не сама модель, а её настройка по умолчанию, унаследованная из эталонной статьи: расписание перекошено в эксплуатацию. Очевидный путь доработки лежит ровно там, где мы наметили его при реализации: вынести пороги фаз в параметры и сместить их, удлинив разведку, а возможно — поставить долю разведки в зависимость от размерности задачи. С такой переработкой AOA вполне мог бы подняться значительно выше нынешних 39,66%.
Весь инструментарий: класс C_AO_AOA, стандартный стенд и композитный анти-чит тест — у читателя на руках. Приглашаю не останавливаться на этих выводах, а проверить и поискать самому: возможно, кому-то удастся больше, чем вышло у меня. AOA в нынешнем виде — не финал, а открытая задача.

Рисунок 2. Цветовая градация алгоритмов по соответствующим тестам

Рисунок 3. Гистограмма результатов тестирования алгоритмов (по шкале от 0 до 100, чем больше, тем лучше, где 100 — максимально возможный теоретический результат), в архиве скрипт для расчёта рейтинговой таблицы
Плюсы и минусы алгоритма
Плюсы:
- Небольшое количество внешних параметров.
Минусы:
- Склонность к застреванию.
- Внутренние параметры необходимо настраивать.
К статье прикреплён архив с актуальными версиями кодов алгоритмов. Автор статьи не несёт ответственности за абсолютную точность в описании канонических алгоритмов, во многие из них были внесены изменения для улучшения поисковых возможностей. Выводы и суждения, представленные в статьях, основываются на результатах проведённых экспериментов.
Программы, используемые в статье
| # | Имя | Тип | Описание |
|---|---|---|---|
| 1 | #C_AO.mqh | Включаемый файл | Родительский класс популяционных алгоритмов оптимизации |
| 2 | #C_AO_enum.mqh | Включаемый файл | Перечисление популяционных алгоритмов оптимизации |
| 3 | TestFunctions.mqh | Включаемый файл | Библиотека тестовых функций |
| 4 | TestStandFunctions.mqh | Включаемый файл | Библиотека функций тестового стенда |
| 5 | TestStand3D.mqh | Включаемый файл | 3D-панель визуализации для тестового стенда |
| 6 | Utilities.mqh | Включаемый файл | Библиотека вспомогательных функций |
| 7 | CalculationTestResults.mqh | Включаемый файл | Скрипт для расчёта результатов в сравнительную таблицу |
| 8 | Test_AO_All.mq5 | Скрипт | Единый испытательный стенд для всех популяционных алгоритмов оптимизации |
| 9 | Test_AO_AntiCheat | Скрипт | Тест на читерство алгоритмов оптимизации |
| 10 | Simple use of population optimization algorithms.mq5 | Скрипт | Простой пример использования популяционных алгоритмов оптимизации без визуализации |
| 11 | Test_AO_AOA.mq5 | Скрипт | Испытательный стенд для AOA |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
От "лучшего прохода" к устойчивым решениям: исследование поверхности оптимизации в MetaTrader 5
Разработка инструментария для анализа Price Action (Часть 44): Создание в MQL5 сигнального советника на основе пересечений VWMA
Разработка инструментария для анализа Price Action (Часть 45): Создание динамической панели для анализа уровней в MQL5
Создание торговой системы (Часть 3): Определение минимального уровня риска для достижения реалистичных целей по прибыли
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования