Files
Population-optimization-alg…/MQL5/Include/Math/AOs/PopulationAO/AO_ES_EagleStrategy.mqh
T
2025-06-13 00:55:18 +04:00

493 lines
29 KiB
Plaintext

//+——————————————————————————————————————————————————————————————————+
//| C_AO_ES |
//| Copyright 2007-2025, Andrey Dik |
//| https://www.mql5.com/ru/users/joo |
//———————————————————————————————————————————————————————————————————+
//Article: https://www.mql5.com/ru/articles/18460
#include "#C_AO.mqh"
//————————————————————————————————————————————————————————————————————
class C_AO_ES : public C_AO
{
public: //----------------------------------------------------------
~C_AO_ES () { }
C_AO_ES ()
{
ao_name = "ES";
ao_desc = "Eagle Strategy";
ao_link = "https://www.mql5.com/ru/articles/18460";
popSize = 100; // размер популяции
lambda = 1.0; // параметр распределения Леви (1 < λ ≤ 3)
sphereRadius = 0.1; // радиус гиперсферы для локального поиска
localIterations = 20; // количество итераций локального поиска
alpha = 0.1; // параметр рандомизации для Firefly
beta0 = 1.2; // начальная привлекательность
ArrayResize (params, 6);
params [0].name = "popSize"; params [0].val = popSize;
params [1].name = "lambda"; params [1].val = lambda;
params [2].name = "sphereRadius"; params [2].val = sphereRadius;
params [3].name = "localIterations"; params [3].val = localIterations;
params [4].name = "alpha"; params [4].val = alpha;
params [5].name = "beta0"; params [5].val = beta0;
}
void SetParams ()
{
popSize = (int)params [0].val;
lambda = params [1].val;
sphereRadius = params [2].val;
localIterations = (int)params [3].val;
alpha = params [4].val;
beta0 = params [5].val;
}
bool Init (const double &rangeMinP [], // минимальные значения
const double &rangeMaxP [], // максимальные значения
const double &rangeStepP [], // шаг изменения
const int epochsP = 0); // количество эпох
void Moving ();
void Revision ();
//------------------------------------------------------------------
double lambda; // параметр распределения Леви (1 < λ ≤ 3)
double sphereRadius; // радиус гиперсферы для локального поиска
int localIterations; // количество итераций локального поиска
double alpha; // параметр рандомизации
double beta0; // начальная привлекательность
private: //---------------------------------------------------------
double gamma_es; // коэффициент поглощения света
double levyStep []; // массив для шагов Леви
// Отслеживание фаз
bool inLocalSearchPhase; // флаг локального поиска
int localSearchCenter; // центр локального поиска
int localSearchCounter; // счетчик итераций локального поиска
// Отслеживание сходимости
double prevBestFitness; // предыдущее лучшее значение
int stagnationCounter; // счетчик стагнации
// Отслеживание эпох
int epochCurrent; // текущая эпоха
int epochMax; // максимальное количество эпох
// Вспомогательные методы
void GlobalExploration ();
void LocalExploitation ();
void GenerateLevyStep ();
double GenerateGaussian ();
double Gamma (double z);
};
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
bool C_AO_ES::Init (const double &rangeMinP [],
const double &rangeMaxP [],
const double &rangeStepP [],
const int epochsP = 0)
{
if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
//------------------------------------------------------------------
// Инициализация массивов
ArrayResize (levyStep, coords);
// Инициализация отслеживания фаз
inLocalSearchPhase = false;
localSearchCenter = 0;
localSearchCounter = 0;
// Инициализация отслеживания сходимости
prevBestFitness = -DBL_MAX;
stagnationCounter = 0;
// Инициализация отслеживания эпох
epochMax = epochsP;
epochCurrent = 0;
// Фиксированный параметр Firefly
gamma_es = 1.0;
// Инициализация популяции случайным образом
for (int i = 0; i < popSize; i++)
{
for (int c = 0; c < coords; c++)
{
a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
}
a [i].f = -DBL_MAX;
a [i].fB = -DBL_MAX;
}
return true;
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_ES::Moving ()
{
epochCurrent++;
// ПРИНЯТИЕ РЕШЕНИЯ О ФАЗЕ: Чередование между глобальным и локальным поиском
if (!inLocalSearchPhase)
{
// ФАЗА 1: ГЛОБАЛЬНОЕ ИССЛЕДОВАНИЕ с использованием полетов Леви
GlobalExploration ();
// Проверка необходимости переключения на локальный поиск
// Переключаемся, если нашли многообещающую область (улучшение лучшей приспособленности)
if (fB > prevBestFitness)
{
inLocalSearchPhase = true;
localSearchCounter = 0;
prevBestFitness = fB;
stagnationCounter = 0;
// Поиск лучшего агента для центрирования локального поиска
localSearchCenter = 0;
double bestFit = -DBL_MAX;
for (int i = 0; i < popSize; i++)
{
if (a [i].f > bestFit)
{
bestFit = a [i].f;
localSearchCenter = i;
}
}
}
else
{
stagnationCounter++;
// При стагнации увеличиваем исследование
if (stagnationCounter > 5)
{
lambda = MathMax (1.0, lambda - 0.1); // Делаем полеты Леви более агрессивными
}
}
}
else
{
if (u.RNDprobab () < 0.8)
{
// ФАЗА 2: ЛОКАЛЬНАЯ ЭКСПЛУАТАЦИЯ с использованием алгоритма Firefly
LocalExploitation ();
localSearchCounter++;
// Возврат к глобальному поиску после завершения локальных итераций
if (localSearchCounter >= localIterations)
{
inLocalSearchPhase = false;
lambda = params [1].val; // Сброс lambda к исходному значению
}
}
else
{
for (int i = 0; i < popSize; i++)
{
for (int c = 0; c < coords; c++)
{
if (u.RNDprobab () < 0.5)
{
a [i].c [c] = cB [c];
}
}
}
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_ES::Revision ()
{
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, WHOLE_ARRAY);
}
// Обновление глобального лучшего
if (a [i].f > fB)
{
fB = a [i].f;
ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
// ФАЗА 1: Глобальное исследование с использованием полетов Леви
void C_AO_ES::GlobalExploration ()
{
for (int i = 0; i < popSize; i++)
{
// Генерация шага Леви
GenerateLevyStep ();
// Обновление позиции с использованием полета Леви
for (int c = 0; c < coords; c++)
{
double range = rangeMax [c] - rangeMin [c];
// Адаптивный размер шага в зависимости от прогресса поиска
double progress = (epochMax > 0) ? (double)epochCurrent / (double)epochMax : 0.5;
double stepScale = 0.01 + 0.2 * (1.0 - progress); // Начинаем с больших шагов
// Применение шага Леви
a [i].c [c] += levyStep [c] * range * stepScale;
// Ограничения границ
a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
// ФАЗА 2: Локальная эксплуатация с использованием алгоритма Firefly
void C_AO_ES::LocalExploitation ()
{
// Идентификация агентов внутри гиперсферы вокруг лучшего решения
double agents_in_sphere [];
ArrayResize (agents_in_sphere, 0);
for (int i = 0; i < popSize; i++)
{
double normalized_dist = 0.0;
for (int c = 0; c < coords; c++)
{
double diff = (a [i].c [c] - a [localSearchCenter].c [c]) / (rangeMax [c] - rangeMin [c]);
normalized_dist += diff * diff;
}
normalized_dist = MathSqrt (normalized_dist);
// Включаем агентов внутри сферы или сам центр
if (normalized_dist <= sphereRadius || i == localSearchCenter)
{
int size = ArraySize (agents_in_sphere);
ArrayResize (agents_in_sphere, size + 1);
agents_in_sphere [size] = i;
}
}
// Если слишком мало агентов, расширяем до ближайших соседей
if (ArraySize (agents_in_sphere) < 5)
{
ArrayResize (agents_in_sphere, 0);
// Вычисляем расстояния для всех агентов
double distances [];
ArrayResize (distances, popSize);
for (int i = 0; i < popSize; i++)
{
if (i == localSearchCenter)
{
distances [i] = 0.0;
}
else
{
double dist = 0.0;
for (int c = 0; c < coords; c++)
{
double diff = (a [i].c [c] - a [localSearchCenter].c [c]) / (rangeMax [c] - rangeMin [c]);
dist += diff * diff;
}
distances [i] = MathSqrt (dist);
}
}
// Берем ближайших 5 агентов или 30% популяции
int numAgents = MathMin (popSize, MathMax (5, popSize / 3));
ArrayResize (agents_in_sphere, numAgents);
// Простой выбор ближайших агентов
for (int k = 0; k < numAgents; k++)
{
double minDist = DBL_MAX;
int minIdx = -1;
for (int i = 0; i < popSize; i++)
{
bool already_selected = false;
for (int j = 0; j < k; j++)
{
if (agents_in_sphere [j] == i)
{
already_selected = true;
break;
}
}
if (!already_selected && distances [i] < minDist)
{
minDist = distances [i];
minIdx = i;
}
}
agents_in_sphere [k] = minIdx;
}
}
// Выполнение алгоритма Firefly на выбранных агентах
int numLocalAgents = ArraySize (agents_in_sphere);
for (int i = 0; i < numLocalAgents; i++)
{
int idx_i = (int)agents_in_sphere [i];
for (int j = 0; j < numLocalAgents; j++)
{
if (i == j) continue;
int idx_j = (int)agents_in_sphere [j];
// Если агент j лучше агента i, двигаем i к j
if (a [idx_j].f > a [idx_i].f)
{
// Вычисление расстояния
double r_squared = 0.0;
for (int c = 0; c < coords; c++)
{
double diff = (a [idx_j].c [c] - a [idx_i].c [c]) / (rangeMax [c] - rangeMin [c]);
r_squared += diff * diff;
}
// Вычисление привлекательности
double beta = beta0 * MathExp (-gamma_es * r_squared);
// Перемещение агента i к агенту j
for (int c = 0; c < coords; c++)
{
double range = rangeMax [c] - rangeMin [c];
// Уравнение движения Firefly
a [idx_i].c [c] += beta * (a [idx_j].c [c] - a [idx_i].c [c]) +
alpha * (u.RNDfromCI (-0.5, 0.5)) * range * 0.1;
// Применение границ
a [idx_i].c [c] = u.SeInDiSp (a [idx_i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
// Генерация шага Леви с использованием алгоритма Мантенья
void C_AO_ES::GenerateLevyStep ()
{
// Вычисление сигмы для алгоритма Мантенья
double numerator = Gamma (1.0 + lambda) * MathSin (M_PI * lambda / 2.0);
double denominator = Gamma ((1.0 + lambda) / 2.0) * lambda * MathPow (2.0, (lambda - 1.0) / 2.0);
double sigma = MathPow (numerator / denominator, 1.0 / lambda);
for (int c = 0; c < coords; c++)
{
// Генерация u и v из нормальных распределений
double u_val = GenerateGaussian () * sigma;
double v_val = MathAbs (GenerateGaussian ());
// Вычисление шага Леви
if (v_val > 1e-10)
{
levyStep [c] = u_val / MathPow (v_val, 1.0 / lambda);
}
else
{
levyStep [c] = 0.0;
}
// Ограничение экстремальных значений
if (MathAbs (levyStep [c]) > 10.0)
{
levyStep [c] = 10.0 * (levyStep [c] > 0 ? 1.0 : -1.0);
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
// Генерация гауссовского случайного числа с использованием преобразования Бокса-Мюллера
double C_AO_ES::GenerateGaussian ()
{
static bool hasSpare = false;
static double spare;
if (hasSpare)
{
hasSpare = false;
return spare;
}
hasSpare = true;
double u_val = u.RNDfromCI (0.0, 1.0);
double v_val = u.RNDfromCI (0.0, 1.0);
double mag = MathSqrt (-2.0 * MathLog (u_val + 1e-10));
spare = mag * MathCos (2.0 * M_PI * v_val);
return mag * MathSin (2.0 * M_PI * v_val);
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
// Аппроксимация гамма-функции с использованием аппроксимации Ланцоша
double C_AO_ES::Gamma (double z)
{
if (z < 0.5)
{
// Формула отражения для z < 0.5
return M_PI / (MathSin (M_PI * z) * Gamma (1.0 - z));
}
// Коэффициенты Ланцоша
const double g = 7.0;
double coef [] =
{
0.99999999999980993,
676.5203681218851,
-1259.1392167224028,
771.32342877765313,
-176.61502916214059,
12.507343278686905,
-0.13857109526572012,
9.9843695780195716e-6,
1.5056327351493116e-7
};
z -= 1.0;
double x = coef [0];
for (int i = 1; i < 9; i++)
{
x += coef [i] / (z + i);
}
double t = z + g + 0.5;
double sqrt2pi = MathSqrt (2.0 * M_PI);
return sqrt2pi * MathPow (t, z + 0.5) * MathExp (-t) * x;
}
//————————————————————————————————————————————————————————————————————