493 lines
29 KiB
Plaintext
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;
|
|
}
|
|
//———————————————————————————————————————————————————————————————————— |