Оптимизатор ястребов Харриса — Harris Hawks Optimization (HHO)
Содержание
Введение
При оптимизации параметров торговой системы в ограниченном бюджете вычислений (попыток оценки) вы неизбежно сталкиваетесь с дилеммой: либо алгоритм слишком рано «залипает» в первом найденном локальном оптимуме, либо он длительно исследует пространство и к концу итерационного бюджета не успевает «дожать» перспективные зоны. На практике момент переключения между разведкой (exploration) и доработкой (exploitation) обычно задаётся вручную через статичные гиперпараметры — и этот выбор сильно зависит от конкретной задачи, размерности и сетки дискретизации. В условиях ограниченного числа вызовов функции качества и правила «одна оценка фитнеса за итерацию» нужен механизм, который сам переводит поиск от широкого охвата к точной атаке по мере расходования бюджета, без ручной подстройки порогов.
Harris Hawks Optimization (HHO) предлагает именно такой подход: в основе лежит скалярная «энергия побега» E, детерминированно убывающая от ±2 до 0 по мере прогрессирования итераций, которая автоматом переключает алгоритм между фазами исследования и атаки. В статье мы не ограничились биологической метафорой: мы воспроизвели все режимы HHO (два варианта исследования, четыре стратегии атаки, включая пикирование по закону Леви), адаптировали их под интерфейс унифицированного тестового стенда MQL5 с требованием одной оценки фитнеса на итерацию и реализовали рабочий класс C_AO_HHO. При этом техническое ограничение стенда (невозможность делать два вызова функции качества в одной итерации) вынудило в стратегии с Леви заменить прямую проверку F(Y)<F(X) прокси‑правилом на основе истории улучшений — деталь, важная для воспроизводимости и интерпретации результатов.
Цель статьи — предоставить готовый компонент (Init/Moving/Revision, параметры popSize и β), воспроизводимый протокол тестирования (3 функции: Hilly, Forest, Megacity; три уровня размерности; 10000 вызовов на прогон; 10 повторов) и честную оценку применимости HHO к практическим задачам — в частности, к калибровке торговых систем в условиях ограниченного бюджета.
Реализация алгоритма
В техасских пустынях существует единственный вид хищных птиц, охотящихся стаей — ястреб Харриса. Несколько птиц расходятся веером, прочёсывая широкую территорию в поисках кролика. Как только добыча обнаружена, тактика мгновенно меняется — разведчики превращаются в слаженную команду, которая сжимает кольцо, перекрывая жертве все пути отступления. Именно эту охоту воспроизводит алгоритм оптимизации ястребов Харриса Harris Hawks Optimization, (HHO), предложенный Heidari et al. в 2019 году.
Каждый ястреб — отдельный кандидат на решение задачи оптимизации, точка X_i в D-мерном пространстве поиска. «Кролик» X_prey — лучшее решение, найденное к текущему моменту, его роль исполняет вектор cB[] глобального рекорда. Алгоритм управляет тем, как ястребы двигаются — поодиночке изучая пространство или сообща сжимаясь вокруг находки. В реализации следующая позиция каждого агента накапливается в вектор X_next (cP[]), а затем привязывается к сетке поиска и передаётся фитнес-функции.
Ключевое новшество HHO — энергия побега E, которая убывает от ±2 к нулю по мере исчерпания итераций (Eq. 3):
E = 2 · E₀ · (1 − t / T)
E₀ ~ U(−1, 1) — случайная начальная энергия, независимая для каждого агента и каждой итерации; t — номер текущей итерации; T — общее число итераций. Знак E₀ задаёт направление «борьбы» кролика за выживание, |E| определяет фазу алгоритма.
Параметр E работает как таймер, автоматически переключающий алгоритм между двумя режимами. Пока |E| ≥ 1 — ястребы ведут разведку, разлетаясь по всему пространству и действуя независимо. Когда |E| опускается ниже 1, поиск сужается и начинается охота. Аналогия: пока до конца аукциона далеко, участники изучают самые разные лоты по всему залу; когда таймер приближается к нулю — все сосредоточены на одном лоте.
В режиме разведки (|E| ≥ 1) каждый ястреб выбирает, куда сесть, двумя способами в зависимости от случайного параметра q ~ U(0, 1). При q < 0.5 ориентиром служит случайный сосед X_rand из текущей популяции (Eq. 1a):
X_next[i] = X[rand] − r1 · |X[rand] − 2 · r2 · X[i]|;
где r1, r2 ~ U(0, 1) — независимые для каждой координаты, X[rand] — позиция случайно выбранного агента стаи. При q ≥ 0.5 ястреб использует разность между позицией кролика и средним положением стаи X_mean (Eq. 2):
X_mean = (1/N) · Σ X[i]
и смещается в произвольное место на основе этой разности (Eq. 1b):
X_next[i] = (X_prey − X_mean) − r3 · (LB + r4 · (UB − LB)); r3, r4 ~ U(0, 1);
LB, UB — нижняя и верхняя границы поиска. X_mean вычисляется один раз как снапшот начала итерации — все агенты видят одинаковое среднее, что исключает зависимость от порядка обновления. Оба варианта обеспечивают широкий разброс агентов, снижающий риск застрять в локальном оптимуме.
Когда кролик обнаружен (|E| < 1), в дело вступают четыре стратегии атаки. Их выбор определяется двумя вопросами: насколько кролик истощён и удалось ли ему вырваться. Истощённость определяет |E|: при |E| ≥ 0.5 кролик ещё бодр, при |E| < 0.5 — силы на исходе. Успех побега определяет случайный параметр r ~ U(0, 1), независимый от E.
Мягкая осада (r ≥ 0.5, |E| ≥ 0.5) — ястребы плавно сокращают дистанцию, как пожарные, методично вытесняющие огонь (Eq. 4):
X_next[i] = (X_prey − X[i]) − E · |J · X_prey − X[i]|
J = 2 · (1 − U(0,1)) — сила случайного прыжка кролика в попытке вырваться; разность X_prey − X[i] — вектор сближения. Жёсткая осада (r ≥ 0.5, |E| < 0.5) — кролик истощён, прямой бросок без обходных манёвров (Eq. 6):
X_next[i] = X_prey − E · |X_prey − X[i]|
J в этой стратегии не используется — кролик уже не способен на активный манёвр.
Полёт Леви — это не совсем обычное случайное блуждание. Большинство шагов маленькие, но изредка случается скачок в десятки раз длиннее. Такое распределение встречается в природе повсюду — от маршрутов альбатросов до движений молекул. Шаг Леви вычисляется по формуле Mantegna (Eq. 9):
LF = u / |v|^(1/β), u ~ N(0, σ²), v ~ N(0, 1)
σ = [Γ(1+β)·sin(πβ/2) / (Γ((1+β)/2)·β·2^((β−1)/2))]^(1/β); где β = 1.5 — показатель хвоста распределения, константа, u и v — независимые нормальные сэмплы, генерируются методом Бокса—Мюллера, σ предвычисляется однократно в Init(). Результат LF знаковый: u может быть отрицательным, что обеспечивает двунаправленность прыжка.
В HHO полёт Леви применяется в стратегиях 3 и 4 как запасной манёвр: если направленный шаг к кролику не улучшает позицию ястреба, добавляется случайное Леви-возмущение. Мягкая осада с пикированием (r < 0.5, |E| ≥ 0.5) — кролик ещё бодр и петляет; ястреб сначала пробует направленный шаг Y, при неудаче добавляет Леви (Eq. 7–10):
Y = X_prey − E · |J · X_prey − X[i]|
Z = Y + U(0,1) · LF(D)
X_next[i] = Y если F(Y) < F(X[i]), иначе Z.
Жёсткая осада с пикированием (r < 0.5, |E| < 0.5) — кролик истощён; атака ориентируется на центр масс стаи X_mean, а не на индивидуальную позицию агента (Eq. 11–13):
Y = X_prey − E · |J · X_prey − X_mean|
Z = Y + U(0,1) · LF(D)
X_next[i] = Y если F(Y) < F(X[i]), иначе Z.
Выбор Y или Z требует двух вызовов фитнес-функции внутри одной итерации. В данной реализации фреймворк допускает ровно одну оценку на итерацию, поэтому применяется прокси-критерий: если агент улучшился в прошлой итерации (f[i] > f_prev[i]) — используется Y; иначе — Z. На первой итерации f_prev[i] = −∞, что гарантирует выбор Z и стимулирует начальное разнообразие через Леви-прыжки.
Как детектив, который методично осматривает комнату, но иногда по наитию перебегает на другой этаж: большинство таких прыжков окажутся пустыми, но один из них может обнаружить решение, которое планомерный поиск пропустил бы. Именно в этом состоит роль стратегий 3 и 4 — Леви-прыжок как страховка от преждевременной сходимости. Идея работы алгоритма изображена на иллюстрации ниже.

Рисунок 1. Иллюстрация работы алгоритма HHO
Иллюстрация содержит все ключевые элементы алгоритма. Левая панель показывает фазу исследования: 9 ястребов разлетаются в случайных направлениях по всему пространству, кролик присутствует, но не является фокусом внимания. Правая панель показывает фазу эксплуатации: 4 ястреба атакуют кролика каждый по своей стратегии — прямые синие стрелки для стратегий 1 и 2 (мягкая и жёсткая осада), фиолетовые зигзаги для стратегий 3 и 4 (осада + пикирование Леви, длинный прыжок в конце пути намеренно выделен). Временная шкала внизу показывает убывание E от 2 до 0 с кривой затухания и чёткой границей |E| = 1.
Переходим к написанию псевдокода алгоритма HHO.
Init INIT(LB[], UB[], step[], epochsP): t ← 0 ; T ← epochsP // Eq.(9): σ — предвычисляется однократно через LGamma() num ← exp(LGamma(1+β)) · sin(π·β/2) den ← exp(LGamma((1+β)/2)) · β · 2(β−1)/2 σ ← (den > 1e-15) ? (num/den)1/β : 1 // σ для LevyStep() for i = 0..N−1: for c = 0..D−1: X_next[i][c] ← U(LB[c], UB[c]) // случайная инициализация f_prev[i] ← −∞ // → первая итерация всегда выбирает Z (Lévy) Moving · вызывается перед каждым расчётом фитнеса MOVING(): for i = 0..N−1: for c = 0..D−1: X[i][c] ← snap(X_next[i][c], LB[c], UB[c], step[c]) Revision · вызывается после расчёта f[i] = F(X[i]) внешним кодом REVISION(): t ← t + 1 // ─── 1. Обновление личных и глобального рекордов ───────────── for i = 0..N−1: if f[i] > f_best[i]: f_best[i] ← f[i] ; X_best[i] ← X[i] if f[i] > f_prey: f_prey ← f[i] ; X_prey ← X[i] // «кролик» // ─── 2. Среднее положение стаи по X[i] (снапшот) ── Eq.(2) ── for c = 0..D−1: X_mean[c] ← (1/N) · Σᵢ X[i][c] // ─── 3. Общий энергетический множитель итерации ────────────── E1 ← 2 · (1 − t / T) // ─── 4. Обновление позиции каждого ястреба ─────────────────── for i = 0..N−1: // [L3] прокси «Y лучше X» — фиксируем ДО обновления f_prev improvedLast ← (f[i] > f_prev[i]) f_prev[i] ← f[i] // Eq.(3): E = E1 · E0, E0 ~ U(−1, 1) — inline, отдельная переменная не нужна E ← E1 · (2 · U(0,1) − 1) |E| ← |E| if |E| ≥ 1: // ══ ИССЛЕДОВАНИЕ ══ q ← U(0,1) // генерируется только в этой ветке if q < 0.5: // Eq.(1a): случайное дерево — ориентация на X[ri] ri ← randInt(0, N−1) r1, r2 ← U(0,1) for c = 0..D−1: X_next[i][c] ← clamp( X[ri][c] − r1·|X[ri][c] − 2·r2·X[i][c]|, LB[c], UB[c] ) else: // q ≥ 0.5 — Eq.(1b): ориентация от X_prey через X_mean r3, r4 ← U(0,1) for c = 0..D−1: X_next[i][c] ← clamp( (X_prey[c] − X_mean[c]) − r3·(LB[c] + r4·(UB[c]−LB[c])), LB[c], UB[c] ) else if |E| < 1 and r ≥ 0.5 and |E| ≥ 0.5: // ── Стратегия 1: мягкая осада ── r ← U(0,1) // генерируется первым в блоке эксплуатации J ← 2·(1 − U(0,1)) // Jump_strength for c = 0..D−1: // Eq.(4), ΔX = X_prey − X[i] (Eq.5 inline) X_next[i][c] ← clamp( (X_prey[c]−X[i][c]) − E·|J·X_prey[c] − X[i][c]|, LB[c], UB[c] ) else if |E| < 1 and r ≥ 0.5 and |E| < 0.5: // ── Стратегия 2: жёсткая осада ── // J не нужен в этой стратегии for c = 0..D−1: // Eq.(6) X_next[i][c] ← clamp( X_prey[c] − E·|X_prey[c] − X[i][c]|, LB[c], UB[c] ) else if |E| < 1 and r < 0.5 and |E| ≥ 0.5: // ── Стратегия 3: мягкая осада + Lévy ── J ← 2·(1 − U(0,1)) for c = 0..D−1: // Eq.(7) — кандидат Y Y[c] ← clamp( X_prey[c] − E·|J·X_prey[c] − X[i][c]|, LB[c], UB[c] ) if improvedLast: // прокси F(Y) < F(X[i]) → берём Y [L3] X_next[i] ← Y else: // прокси F(Y) ≥ F(X[i]) → Z = Y + Lévy Eq.(8) for c = 0..D−1: Z[c] ← clamp( Y[c] + U(0,1) · LevyStep(), LB[c], UB[c] ) // rand·LF(D) X_next[i] ← Z else: // |E| < 1, r < 0.5, |E| < 0.5 ── Стратегия 4: жёсткая осада + Lévy ── J ← 2·(1 − U(0,1)) for c = 0..D−1: // Eq.(12) — кандидат Y' (X_mean вместо X[i]) Y[c] ← clamp( X_prey[c] − E·|J·X_prey[c] − X_mean[c]|, LB[c], UB[c] ) if improvedLast: // прокси F(Y') < F(X[i]) → берём Y' [L3] X_next[i] ← Y else: // прокси F(Y') ≥ F(X[i]) → Z' = Y' + Lévy Eq.(13) for c = 0..D−1: Z[c] ← clamp( Y[c] + U(0,1) · LevyStep(), LB[c], UB[c] ) X_next[i] ← Z // end strategy selection // end for i return X_prey, f_prey LevyStep · знаковый скалярный шаг — вызывается per-coordinate LevyStep() → double: // Box-Muller: два независимых N(0,1) из одной пары равномерных чисел r1 ← U(1e-15, 1), r2 ← U(0, 1) // r1 > 0 чтобы ln(r1) не взорвался n1 ← √(−2·ln r1) · cos(2π·r2) // n1 ~ N(0, 1) n2 ← √(−2·ln r1) · sin(2π·r2) // n2 ~ N(0, 1) u ← n1 · σ // u ~ N(0, σ²), как randn()*sigma в оригинале v ← n2 // v ~ N(0, 1), как randn() в оригинале av ← max(|v|, 1e-15) return u / av1/β // знаковый результат — сохраняет знак u, обеспечивает ±направление
Теперь можем перейти непосредственно к реализации.
Класс C_AO_HHO наследует базовый C_AO, предоставляющий унифицированный интерфейс тестового стенда: массив агентов a[], глобальный лучший вектор cB[], счётчики coords и popSize, вспомогательный объект "u" с генераторами случайных чисел. Среди приватных полей класса "t" и "T" хранят текущую итерацию и её максимум — они необходимы для вычисления убывающей энергии E1 = 2·(1 − t/T). Поле sigma вычисляется один раз в Init() и используется функцией LevyStep() на протяжении всего прогона. Временные буферы Xm[] и Ytmp[] размечаются под coords элементов при инициализации и переиспользуются без повторного выделения памяти.
//──────────────────────────────────────────────────────────────────── class C_AO_HHO : public C_AO { public: //------------------------------------------------------------ ~C_AO_HHO () { } C_AO_HHO () { ao_name = "HHO"; ao_desc = "Harris Hawks Optimization"; ao_link = "https://www.mql5.com/ru/articles/21898"; popSize = 50; beta = 1.5; ArrayResize (params, 2); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "beta"; params [1].val = beta; } void SetParams () { popSize = (int)params [0].val; beta = params [1].val; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0); void Moving (); void Revision (); //------------------------------------------------------------------ double beta; // показатель хвоста Леви (β = 1.5 в оригинале) private: //--------------------------------------------------------- int t; // текущая итерация (счёт с 1) int T; // всего итераций double sigma; // σ для формулы Леви (Eq. 9), вычисляется один раз в Init double Xm []; // [coords] среднее положение стаи (Eq. 2) double Ytmp []; // [coords] кандидат Y (стратегии 3, 4) double LGamma (double x); // логарифм Г(x), аппроксимация Ланцоша double LevyStep (); // [L2] скалярный шаг Леви: u~N(0,σ), v~N(0,1) double Clamp (double v, double lo, double hi) { return v < lo ? lo : (v > hi ? hi : v); } }; //————————————————————————————————————————————————————————————————————
Метод Init() выполняет полный сброс состояния алгоритма перед каждым тестовым прогоном. Он вызывает StandardInit(), который размечает массив агентов, копирует границы диапазона и сбрасывает генератор случайных чисел. После этого сохраняет epochsP как "T", обнуляет счётчик "t" и размечает буферы. Предвычисление sigma выполняется по формуле Мантеньи (Eq. 9): числитель — Γ(1+β)·sin(πβ/2), знаменатель — Γ((1+β)/2)·β·2^((β-1)/2); результат возводится в степень 1/β. Вычисление делегировано LGamma() во избежание переполнения при прямом вычислении гамма-функции через факториал. Каждый агент инициализируется равномерно в [LB, UB], а поле a[i].fP сбрасывается в −DBL_MAX — это гарантирует, что на первой итерации improvedLast = false и стратегии 3 и 4 всегда выберут ветку с Lévy-прыжком, стимулируя начальное разнообразие.
//———————————————————————————————————————————————————————————————————— bool C_AO_HHO::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ T = (epochsP > 0) ? epochsP : 1; t = 0; ArrayResize (Xm, coords); ArrayResize (Ytmp, coords); //--- Eq. (9): σ = [Г(1+β)·sin(πβ/2) / (Г((1+β)/2)·β·2^((β-1)/2))]^(1/β) double num = exp (LGamma (1.0 + beta)) * sin (M_PI * beta * 0.5); double den = exp (LGamma ((1.0 + beta) * 0.5)) * beta * pow (2.0, (beta - 1.0) * 0.5); sigma = (den > 1e-15) ? pow (fabs (num / den), 1.0 / beta) : 1.0; //--- начальные позиции: равномерно случайно в [LB, UB] for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { a [i].cP [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); } a [i].fP = -DBL_MAX; // сброс истории улучшений для [L3] } return true; } //————————————————————————————————————————————————————————————————————
Метод Moving() переносит спроецированные позиции из cP[] в c[] с привязкой к дискретной сетке через SeInDiSp(). Вызывается тестовым стендом перед каждым расчётом фитнес-функции. Вся логика вычисления следующего шага сосредоточена в Revision(); Moving() — исключительно операция проекции.
//———————————————————————————————————————————————————————————————————— void C_AO_HHO::Moving () { 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]); } } } //————————————————————————————————————————————————————————————————————
Метод Revision() является основным и вызывается тестовым стендом после того, как внешний код рассчитал фитнес всех агентов и записал результаты в a[i].f. Он выполняет четыре последовательных шага. Первый — обновление лучших позиций: для каждого агента сравнивается текущий фитнес с личным рекордом a[i].fB и с глобальным fB, при улучшении копируются соответствующие координатные векторы. Вектор cB[] всегда хранит позицию кролика. Второй шаг — вычисление среднего положения стаи Xm[] (Eq. 2) по текущим координатам a[i].c[]. Третий шаг — вычисление E1 = 2·(1 − t/T), общего для всей итерации убывающего множителя. Четвёртый шаг — обновление позиции каждого ястреба.
В начале блока фиксируется прокси-признак improvedLast = (a[i].f > a[i].fP), после чего fP обновляется для следующей итерации. Этот признак заменяет двойной вызов фитнес-функции, применяемый в оригинале в стратегиях 3 и 4: если агент улучшился в прошлой итерации — доверяем направленному шагу Y; если нет — добавляем Lévy-возмущение Z. Случайные числа E, q или r и при необходимости J генерируются строго в той ветке кода, где они нужны, без лишних вызовов генератора.
Вспомогательный метод Clamp() ограничивает значение v отрезком [lo, hi] и применяется после каждого вычисления новой позиции агента, гарантируя, что кандидатный вектор cP[] остаётся в допустимом диапазоне поиска независимо от величины шага — включая потенциально большие Lévy-прыжки.
//———————————————————————————————————————————————————————————————————— // Revision(): обновляет X_prey, X_m и вычисляет cP для следующего шага. // // Структура соответствует оригиналу: // |E| >= 1 → исследование: // q < 0.5 → X_rand formula (Eq. 1a) [L1] // q >= 0.5 → X_prey − X_m (Eq. 1b) // |E| < 1 → эксплуатация: // r>=0.5, |E|>=0.5 → стратегия 1: мягкая осада Eq. (4-5) // r>=0.5, |E|< 0.5 → стратегия 2: жёсткая осада Eq. (6) // r< 0.5, |E|>=0.5 → стратегия 3: мягкая осада + Lévy Eq. (7-10) // r< 0.5, |E|< 0.5 → стратегия 4: жёсткая осада + Lévy Eq. (11-13) // // [L3] В оригинале стратегии 3/4 делают два вызова fobj (для Y и Z). // Прокси в нашем фреймворке: если агент улучшился в прошлой итерации // (a[i].f > a[i].fP) — доверяем направленному шагу Y; // иначе добавляем Lévy → Z (как запасной вариант). void C_AO_HHO::Revision () { t++; //─── 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); } } //─── 2. Среднее положение стаи Xm (Eq. 2) ─────────────────────── ArrayInitialize (Xm, 0.0); for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { Xm [c] += a [i].c [c]; } } for (int c = 0; c < coords; c++) { Xm [c] /= (double)popSize; } //─── 3. E1 — общий для всей итерации ───────────── double E1 = 2.0 * (1.0 - (double)t / (double)T); //─── 4. Обновление позиции каждого ястреба ────────────────────── for (int i = 0; i < popSize; i++) { //--- [L3] Фиксируем признак улучшения ДО обновления fP bool improvedLast = (a [i].f > a [i].fP); a [i].fP = a [i].f; // сохраняем для следующей итерации //--- Eq. (3): E = E1·E0, E0 ~ U(−1,1) [E0 не нужен отдельно] double E = E1 * (2.0 * u.RNDfromCI (0.0, 1.0) - 1.0); double absE = fabs (E); if (absE >= 1.0) { //══ ИССЛЕДОВАНИЕ ══════════════════════════════════════════════ // q генерируется только здесь double q = u.RNDfromCI (0.0, 1.0); if (q < 0.5) { //─ [L1] Eq. (1a): ориентация на случайного члена стаи ────── // Xi(t+1) = X_rand − r1·|X_rand − 2·r2·Xi| int ri = u.RNDminusOne (popSize); double r1 = u.RNDfromCI (0.0, 1.0); double r2 = u.RNDfromCI (0.0, 1.0); for (int c = 0; c < coords; c++) { a [i].cP [c] = Clamp (a [ri].c [c] - r1 * fabs (a [ri].c [c] - 2.0 * r2 * a [i].c [c]), rangeMin [c], rangeMax [c]); } } else { //─ Eq. (1b): ориентация на разность X_prey − X_m ─────────── // Xi(t+1) = (X_prey − X_m) − r3·((UB−LB)·r4 + LB) double r3 = u.RNDfromCI (0.0, 1.0); double r4 = u.RNDfromCI (0.0, 1.0); for (int c = 0; c < coords; c++) { a [i].cP [c] = Clamp ((cB [c] - Xm [c]) - r3 * (rangeMin [c] + r4 * (rangeMax [c] - rangeMin [c])), rangeMin [c], rangeMax [c]); } } } else { //══ ЭКСПЛУАТАЦИЯ ══════════════════════════════════════════════ // r генерируется только здесь double r = u.RNDfromCI (0.0, 1.0); if (r >= 0.5 && absE >= 0.5) { //─ Стратегия 1: мягкая осада ───────────────────────────────── // ΔX = X_prey − Xi Eq. (5) // Xi(t+1) = ΔX − E·|J·X_prey − Xi| Eq. (4) // J генерируется здесь (Jump_strength) double J = 2.0 * (1.0 - u.RNDfromCI (0.0, 1.0)); for (int c = 0; c < coords; c++) { a [i].cP [c] = Clamp ((cB [c] - a [i].c [c]) - E * fabs (J * cB [c] - a [i].c [c]), rangeMin [c], rangeMax [c]); } } else { if (r >= 0.5 && absE < 0.5) { //─ Стратегия 2: жёсткая осада ──────────────────────────────── // Xi(t+1) = X_prey − E·|X_prey − Xi| Eq. (6) // J не используется в этой стратегии for (int c = 0; c < coords; c++) { a [i].cP [c] = Clamp (cB [c] - E * fabs (cB [c] - a [i].c [c]), rangeMin [c], rangeMax [c]); } } else { if (r < 0.5 && absE >= 0.5) { //─ Стратегия 3: мягкая осада + пикирование Леви ────────────── // Y = X_prey − E·|J·X_prey − Xi| Eq. (7) // Z = Y + rand·LF(D) Eq. (8) // Xi = Y если F(Y)<F(Xi), иначе Z если F(Z)<F(Xi), // иначе Xi остаётся (stay-put — фреймворк не поддерживает). // [L3] прокси: improvedLast → Y; иначе → Z. // J генерируется здесь double J = 2.0 * (1.0 - u.RNDfromCI (0.0, 1.0)); for (int c = 0; c < coords; c++) { Ytmp [c] = Clamp (cB [c] - E * fabs (J * cB [c] - a [i].c [c]), rangeMin [c], rangeMax [c]); } if (improvedLast) { for (int c = 0; c < coords; c++) a [i].cP [c] = Ytmp [c]; } else { // Z = Y + rand·LF(D), rand(1,dim).*Levy(dim) [L2] for (int c = 0; c < coords; c++) { a [i].cP [c] = Clamp (Ytmp [c] + u.RNDfromCI (0.0, 1.0) * LevyStep (), rangeMin [c], rangeMax [c]); } } } else // r < 0.5 && absE < 0.5 { //─ Стратегия 4: жёсткая осада + пикирование Леви ───────────── // Y' = X_prey − E·|J·X_prey − X_m| Eq. (12) // Z' = Y' + rand·LF(D) Eq. (13) // Xi = Y' если F(Y')<F(Xi), иначе Z' если F(Z')<F(Xi), // иначе Xi остаётся (stay-put — фреймворк не поддерживает). // [L3] прокси: improvedLast → Y'; иначе → Z'. // J генерируется здесь double J = 2.0 * (1.0 - u.RNDfromCI (0.0, 1.0)); for (int c = 0; c < coords; c++) { Ytmp [c] = Clamp (cB [c] - E * fabs (J * cB [c] - Xm [c]), rangeMin [c], rangeMax [c]); } if (improvedLast) { for (int c = 0; c < coords; c++) { a [i].cP [c] = Ytmp [c]; } } else { // Z' = Y' + rand·LF(D) [L2] for (int c = 0; c < coords; c++) { a [i].cP [c] = Clamp (Ytmp [c] + u.RNDfromCI (0.0, 1.0) * LevyStep (), rangeMin [c], rangeMax [c]); } } } } } } } // for i } //————————————————————————————————————————————————————————————————————
Метод LevyStep() возвращает один скалярный шаг полёта Леви по формуле Мантеньи. Два независимых нормальных сэмпла n1 и n2 генерируются методом Бокса—Мюллера из двух равномерных чисел r1 ∈ (1e-15, 1) и r2 ∈ [0, 1). Нижняя граница r1 исключает нулевой аргумент у логарифма. Шаг вычисляется как uu / |vv|^(1/β), где uu = n1·sigma, vv = n2. Результат знаковый — n1 может быть отрицательным, что обеспечивает двунаправленность прыжка. Вызывающий код умножает результат на независимый равномерный множитель u.RNDfromCI(0, 1) : rand(1,dim).*Levy(dim).
//———————————————————————————————————————————————————————————————————— // [L2] Скалярный шаг Леви: // u = randn()*σ, v = randn() // step = u / |v|^(1/β) // Нормальные сэмплы генерируются методом Бокса—Мюллера. // Levy() возвращает вектор — здесь скаляр; вызывающий код // вызывает функцию per-coordinate, rand(1,dim).*Levy(dim). double C_AO_HHO::LevyStep () { // Box-Muller: два независимых N(0,1) double r1 = u.RNDfromCI (1e-15, 1.0); double r2 = u.RNDfromCI (0.0, 1.0); double n1 = sqrt (-2.0 * log (r1)) * MathCos (2.0 * M_PI * r2); double n2 = sqrt (-2.0 * log (r1)) * sin (2.0 * M_PI * r2); double uu = n1 * sigma; // u ~ N(0, sigma^2) double vv = n2; // v ~ N(0, 1) double av = fabs (vv); if (av < 1e-15) av = 1e-15; return fabs (uu / pow (av, 1.0 / beta)); } //————————————————————————————————————————————————————————————————————
Метод LGamma() вычисляет натуральный логарифм гамма-функции ln(Γ(x)) с точностью около 15 значащих цифр для x > 0 по алгоритму Ланцоша с параметрами g = 7, n = 8. Используется исключительно в Init() для вычисления sigma и не влияет на производительность основного цикла. Для x < 0.5 применяется формула отражения Γ(x)·Γ(1−x) = π/sin(πx), что обеспечивает корректную работу при дробных значениях beta, близких к нулю. Возврат логарифма, а не самого значения Γ(x), предотвращает переполнение double при больших аргументах.
//———————————————————————————————————————————————————————————————————— // Логарифм гамма-функции — аппроксимация Ланцоша (g=7, n=8). // Точность ~15 значащих цифр для Re(x) > 0. double C_AO_HHO::LGamma (double x) { static const double cL [8] = { 676.5203681218851, -1259.1392167224028, 771.32342877765313, -176.61502916214059, 12.507343278686905, -0.13857109526572012, 9.9843695780195716e-6, 1.5056327351493116e-7 }; if (x < 0.5) return log (M_PI / sin (M_PI * x)) - LGamma (1.0 - x); x -= 1.0; double ag = 0.99999999999980993; for (int k = 0; k < 8; k++) ag += cL [k] / (x + k + 1.0); double tt = x + 7.5; return 0.5 * log (2.0 * M_PI) + (x + 0.5) * log (tt) - tt + log (ag); } //————————————————————————————————————————————————————————————————————
Результаты тестов
Алгоритм тестировался на трёх стандартных функциях стенда — Hilly, Forest и Megacity — при трёх уровнях размерности: 10, 50 и 1000 координат соответственно, по 10000 вычислений на каждый прогон и 10 повторений. Общий результат впечатляющий, о нём мы ещё поговорим.HHO|Harris Hawks Optimization|50.0|1.5|
=============================
5 Hilly's; Func runs: 10000; result: 0.5845287633538552
25 Hilly's; Func runs: 10000; result: 0.4063647569651291
500 Hilly's; Func runs: 10000; result: 0.34188339366081033
=============================
5 Forest's; Func runs: 10000; result: 0.9294946030062039
25 Forest's; Func runs: 10000; result: 0.960139366657175
500 Forest's; Func runs: 10000; result: 0.9512729220389199
=============================
5 Megacity's; Func runs: 10000; result: 0.6492307692307692
25 Megacity's; Func runs: 10000; result: 0.6255384615384616
500 Megacity's; Func runs: 10000; result: 0.7003076923076924
=============================
All score: 6.14876 (68.32%)
Ниже приведена визуализация работы алгоритма на тестовых функциях: как на используемых для расчёта рейтинга, так и на стандартных исследовательских.

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

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

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

HHO на тестовой функции Skin

HHO на тестовой функции Ackley
По итогам тестирования алгоритм занимает 1 место в рейтинге лучших алгоритмов оптимизации, несмотря на не самые лучшие показатели по функции Hilly.
№ | 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 | HHO | harris_hawks_optimization | 0,58452 | 0,40636 | 0,34188 | 1,33276 | 0,92949 | 0,96013 | 0,95127 | 2,84089 | 0,64923 | 0,62553 | 0,70031 | 1,97507 | 6,149 | 68,32 |
| 2 | 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 |
| 3 | 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 |
| 4 | 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 |
| 5 | (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 |
| 6 | 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 |
| 7 | 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 |
| 8 | 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 |
| 9 | ECBO | enhanced_colliding_bodies_optimization | 0,93479 | 0,75747 | 0,32471 | 2,01697 | 0,97436 | 0,77446 | 0,23037 | 1,97919 | 0,88923 | 0,58061 | 0,15224 | 1,62208 | 5,618 | 62,43 |
| 10 | 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 |
| 11 | 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 |
| 12 | 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 |
| 13 | 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 |
| 14 | 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 |
| 15 | 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 |
| 16 | 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 |
| 17 | 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 |
| 18 | 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 |
| 19 | 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 |
| 20 | 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 |
| 21 | 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 |
| 22 | 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 |
| 23 | 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 |
| 24 | 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 |
| 25 | 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 |
| 26 | BO | bonobo_optimizer | 0,77565 | 0,63805 | 0,32908 | 1,74278 | 0,88088 | 0,76344 | 0,25573 | 1,90005 | 0,61077 | 0,49846 | 0,14246 | 1,25169 | 4,895 | 54,38 |
| 27 | 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 |
| 28 | CSO | competitive_swarm_optimizer | 0,90291 | 0,61887 | 0,29830 | 1,82008 | 0,99982 | 0,64581 | 0,23975 | 1,88538 | 0,63384 | 0,40553 | 0,12852 | 1,16789 | 4,873 | 54,15 |
| 29 | 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 |
| 30 | 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 |
| 31 | 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 |
| 32 | 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 |
| 33 | 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 |
| 34 | 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 |
| 35 | 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 |
| 36 | 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 |
| 37 | (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 |
| 38 | 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 |
| 39 | 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 |
| 40 | 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 |
| 41 | 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 |
| 42 | 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 |
| 43 | 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 |
| 44 | 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 |
| 45 | 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 |
| 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 | |
Выводы
С точки зрения реализации — результат положителен. Мы получили воспроизводимый, совместимый со стендом компонент C_AO_HHO (параметры popSize и β), корректную реализацию Леви‑шагов (метод Бокса‑Мюллера, предвычисленное σ) и адаптацию логики Y/Z под ограничение одной оценки фитнеса за итерацию. Компонент можно подключить к унифицированному тестовому стенду без правок, и сценарий проверяем: 3 функции, 3 размерности, 10000 вызовов, 10 повторов — все метрики и коды доступны.
Результаты тестов дают двойственное заключение. По агрегированному скору HHO занял первое место (68.32%), с выдающимися показателями на функции Forest во всех размерностях. Однако детальный разбор показал, что лидерство обусловлено не универсальной эффективностью, а частичной «совместимостью» механики алгоритма со структурой конкретного бенчмарка. На функции Hilly HHO показал производительность, близкую к случайному поиску: узкая «игла» глобального максимума требует накопления личного опыта агента (компонента личного рекорда), которого в классическом HHO нет — и диагонально‑скоррелированные шаги алгоритма туда, как правило, не попадают. Это указывает на риск «бенчмарк‑специфичности»: высокие места в рейтинге могут отражать совпадение траекторий алгоритма с геометрией тестовой функции, а не общую пригодность для всех классов задач.
Практические выводы для применения в оптимизации торговых систем:
- HHO годится для задач с относительно широкими зонами притяжения и гладкой многомерной структурой — там его автоматическое переключение и Леви‑прыжки дают преимущество.
- Для поиска узких, изолированных пиков (аналогов Hilly) HHO без дополнительных механизмов памяти или адаптации персональных рекордов уступает более ориентированным методам.
- Всегда проверяйте алгоритм на наборе тестов, модифицированных под геометрию вашей задачи (сдвиги пиков, изменение ширины пиков, нарушение симметрии), чтобы исключить ложное ощущение надёжности, вызванное «совпадением» со стандартным бенчмарком.
В следующей работе мы проверим гипотезу о бенчмарк‑специфичности напрямую: модифицируем тестовые функции (сдвиги диагоналей, изменение ширины пиков, нарушение симметрии) и перегоняем весь набор алгоритмов заново. Если HHO сохранит лидирующие позиции — это подтвердит его универсальность; если нет — это даст явное предупреждение об ограничениях метода и необходимости выборочной проверки алгоритмов на задачах, близких к реальной прикладной постановке.

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

Рисунок 3. Гистограмма результатов (0–100; больше — лучше, 100 — теоретический максимум). В архиве — скрипт для расчёта рейтинговой таблицы.
Плюсы и минусы алгоритма HHO
Плюсы:
- Хорошие показатели на высоких размерностях задач на функции Forest.
Минусы:
- Слабая сходимость на функции Hilly.
- Вычислительная сложность.
К статье прикреплён архив с актуальными версиями кодов алгоритмов. Автор статьи не несёт ответственности за абсолютную точность в описании канонических алгоритмов, во многие из них были внесены изменения для улучшения поисковых возможностей. Выводы и суждения, представленные в статьях, основываются на результатах проведённых экспериментов.
Программы, используемые в статье
| # | Имя | Тип | Описание |
|---|---|---|---|
| 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 | Testing AOs.mq5 | Скрипт | Единый испытательный стенд для всех популяционных алгоритмов оптимизации |
| 9 | Simple use of population optimization algorithms.mq5 | Скрипт | Простой пример использования популяционных алгоритмов оптимизации без визуализации |
| 10 | Test_HHO.mq5 | Скрипт | Испытательный стенд для HHO |
Предупреждение: все права на данные материалы принадлежат MetaQuotes Ltd. Полная или частичная перепечатка запрещена.
Данная статья написана пользователем сайта и отражает его личную точку зрения. Компания MetaQuotes Ltd не несет ответственности за достоверность представленной информации, а также за возможные последствия использования описанных решений, стратегий или рекомендаций.
Нейросети в трейдинге: Многодоменная архитектура анализа финансовых данных (Основные компоненты)
Оптимизация и форвард-анализ стратегий (Часть 1): Метод Пардо — базовая модель
Торговые инструменты на MQL5 (Часть 14): Прокручиваемый текстовый холст с пиксельной точностью, сглаживанием и закругленной полосой прокрутки
Создание самооптимизирующихся советников на MQL5 (Часть 8): Анализ нескольких стратегий (2)
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования