Files
Population-optimization-alg…/MQL5/Include/Math/AOs/PopulationAO/AO_CEO_CrystalEnergyOptimizer.mqh
T
2025-10-30 22:22:36 +04:00

461 lines
27 KiB
Plaintext

//+——————————————————————————————————————————————————————————————————+
//| C_AO_BRO |
//| Copyright 2007-2025, Andrey Dik |
//| https://www.mql5.com/ru/users/joo |
//+——————————————————————————————————————————————————————————————————+
#include "#C_AO.mqh"
//————————————————————————————————————————————————————————————————————
// Упрощённая структура для CEO (адаптация к непрерывной оптимизации)
struct S_CEO_Crystal
{
bool isElite; // элитный кристалл
int stagnationCnt; // счётчик стагнации
double radius; // радиус влияния
void Init ()
{
isElite = false;
stagnationCnt = 0;
radius = 0.0;
}
};
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
class C_AO_CEO : public C_AO
{
public:
~C_AO_CEO () { }
C_AO_CEO ()
{
ao_name = "CEO";
ao_desc = "Crystal Energy Optimizer";
ao_link = "https://www.mql5.com/ru/articles/20052";
popSize = 50;
eliteSize = 10; // элитных (аналог замёрзших)
explorationRate = 0.5; // баланс исследование/эксплуатация
windProbability = 0.1; // вероятность "ветра" на итерацию
ArrayResize (params, 4);
params [0].name = "popSize"; params [0].val = popSize;
params [1].name = "eliteSize"; params [1].val = eliteSize;
params [2].name = "explorationRate"; params [2].val = explorationRate;
params [3].name = "windProbability"; params [3].val = windProbability;
}
void SetParams ()
{
popSize = (int)params [0].val;
eliteSize = (int)params [1].val;
explorationRate = params [2].val;
windProbability = params [3].val;
if (eliteSize < 2) eliteSize = 2;
if (eliteSize > popSize / 2) eliteSize = popSize / 2;
if (explorationRate < 0.1) explorationRate = 0.1;
if (explorationRate > 0.9) explorationRate = 0.9;
if (windProbability < 0.0) windProbability = 0.0;
if (windProbability > 0.5) windProbability = 0.5;
}
bool Init (const double &rangeMinP [],
const double &rangeMaxP [],
const double &rangeStepP [],
const int epochsP);
void Moving ();
void Revision ();
//------------------------------------------------------------------
int eliteSize;
double explorationRate;
double windProbability;
private: //---------------------------------------------------------
S_CEO_Crystal crystalData [];
int iterationCount;
int noImprovementCount;
double centerMass []; // центр масс элитных
void InitializePopulation ();
void UpdateEliteStatus ();
void CrystalGrowth (); // аналог осаждения
void WindEffect (); // аналог wind-blow
void MoveTowardsBest (int idx, double strength);
void MoveTowardsEliteCluster (int idx);
void ExploratoryMove (int idx);
int FindNearestElite (int idx);
void CalculateEliteRadii ();
double GetDynamicExplorationRate ();
};
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
bool C_AO_CEO::Init (const double &rangeMinP [],
const double &rangeMaxP [],
const double &rangeStepP [],
const int epochsP)
{
if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
//------------------------------------------------------------------
ArrayResize (crystalData, popSize);
ArrayResize (centerMass, coords);
for (int i = 0; i < popSize; i++) crystalData [i].Init ();
iterationCount = 0;
noImprovementCount = 0;
return true;
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_CEO::Moving ()
{
if (!revision)
{
InitializePopulation ();
revision = true;
return;
}
//------------------------------------------------------------------
iterationCount++;
// Вычисляем центр масс элитных
ArrayInitialize (centerMass, 0.0);
int eliteCount = 0;
for (int i = 0; i < popSize; i++)
{
if (crystalData [i].isElite)
{
for (int c = 0; c < coords; c++) centerMass [c] += a [i].c [c];
eliteCount++;
}
}
if (eliteCount > 0)
{
for (int c = 0; c < coords; c++) centerMass [c] /= (double)eliteCount;
}
else
{
ArrayCopy (centerMass, cB, 0, 0, coords);
}
// Рассчитываем радиусы влияния элитных
CalculateEliteRadii ();
// Основная фаза: рост кристаллов
CrystalGrowth ();
// Периодический эффект ветра
if (u.RNDfromCI (0.0, 1.0) < windProbability)
{
WindEffect ();
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_CEO::InitializePopulation ()
{
for (int i = 0; i < popSize; i++)
{
for (int j = 0; j < coords; j++)
{
a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]);
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_CEO::UpdateEliteStatus ()
{
S_AO_Agent temp;
S_CEO_Crystal tempData;
// Сортируем по фитнесу
for (int i = 0; i < popSize - 1; i++)
{
for (int j = 0; j < popSize - i - 1; j++)
{
if (a [j].f < a [j + 1].f)
{
temp = a [j];
a [j] = a [j + 1];
a [j + 1] = temp;
tempData = crystalData [j];
crystalData [j] = crystalData [j + 1];
crystalData [j + 1] = tempData;
}
}
}
// Обновляем элитный статус
for (int i = 0; i < popSize; i++)
{
crystalData [i].isElite = (i < eliteSize);
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_CEO::CrystalGrowth ()
{
double explRate = GetDynamicExplorationRate ();
for (int i = 0; i < popSize; i++)
{
if (crystalData [i].isElite)
{
// Элитные делают локальный поиск
for (int c = 0; c < coords; c++)
{
double range = rangeMax [c] - rangeMin [c];
double stepSize = range * 0.05 * explRate;
double step = u.RNDfromCI (-stepSize, stepSize);
a [i].c [c] = a [i].c [c] + step;
a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
else
{
// Не-элитные: комбинация стратегий
double strategy = u.RNDfromCI (0.0, 1.0);
if (strategy < 0.4)
{
// Движение к лучшему (40%)
MoveTowardsBest (i, 0.3 + 0.4 * (1.0 - explRate));
}
else
if (strategy < 0.7)
{
// Движение к ближайшему элитному (30%)
MoveTowardsEliteCluster (i);
}
else
{
// Исследование (30%)
ExploratoryMove (i);
}
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_CEO::MoveTowardsBest (int idx, double strength)
{
for (int c = 0; c < coords; c++)
{
double direction = cB [c] - a [idx].c [c];
double move = strength * direction;
a [idx].c [c] = a [idx].c [c] + move;
a [idx].c [c] = u.SeInDiSp (a [idx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_CEO::MoveTowardsEliteCluster (int idx)
{
int nearestElite = FindNearestElite (idx);
if (nearestElite < 0) return;
for (int c = 0; c < coords; c++)
{
// Движение к ближайшему элитному
double toElite = a [nearestElite].c [c] - a [idx].c [c];
// Движение к центру масс элитных
double toCenter = centerMass [c] - a [idx].c [c];
double r1 = u.RNDfromCI (0.0, 1.0);
double r2 = u.RNDfromCI (0.0, 1.0);
a [idx].c [c] = a [idx].c [c] + r1 * 0.3 * toElite + r2 * 0.2 * toCenter;
a [idx].c [c] = u.SeInDiSp (a [idx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_CEO::ExploratoryMove (int idx)
{
for (int c = 0; c < coords; c++)
{
double range = rangeMax [c] - rangeMin [c];
// Простой но эффективный метод для исследования
double r = u.RNDfromCI (0.0, 1.0);
double move;
if (r < 0.7)
{
// 70% - небольшие шаги (локальное исследование)
move = u.RNDfromCI (-1.0, 1.0) * range * explorationRate * 0.1;
}
else
{
// 30% - большие прыжки (глобальное исследование)
move = u.RNDfromCI (-1.0, 1.0) * range * explorationRate * 0.5;
}
a [idx].c [c] = a [idx].c [c] + move;
a [idx].c [c] = u.SeInDiSp (a [idx].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_CEO::WindEffect ()
{
// "Ветер" разрушает худшие не-элитные и переносит их
int worstNonElite = -1;
double worstFitness = DBL_MAX;
for (int i = 0; i < popSize; i++)
{
if (!crystalData [i].isElite && a [i].f < worstFitness)
{
worstFitness = a [i].f;
worstNonElite = i;
}
}
if (worstNonElite < 0) return;
// Перемещаем в новое место
double strategy = u.RNDfromCI (0.0, 1.0);
if (strategy < 0.5)
{
// Около лучшего
for (int c = 0; c < coords; c++)
{
double range = rangeMax [c] - rangeMin [c];
double noise = u.RNDfromCI (-1.0, 1.0) * range * 0.3;
a [worstNonElite].c [c] = cB [c] + noise;
if (a [worstNonElite].c [c] < rangeMin [c]) a [worstNonElite].c [c] = rangeMin [c];
if (a [worstNonElite].c [c] > rangeMax [c]) a [worstNonElite].c [c] = rangeMax [c];
a [worstNonElite].c [c] = u.SeInDiSp (a [worstNonElite].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
else
{
// Полностью случайно
for (int c = 0; c < coords; c++)
{
double xi = u.RNDfromCI (0.0, 1.0);
a [worstNonElite].c [c] = rangeMin [c] + xi * (rangeMax [c] - rangeMin [c]);
a [worstNonElite].c [c] = u.SeInDiSp (a [worstNonElite].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
int C_AO_CEO::FindNearestElite (int idx)
{
int nearest = -1;
double minDist = DBL_MAX;
for (int i = 0; i < popSize; i++)
{
if (i == idx) continue;
if (!crystalData [i].isElite) continue;
double dist = 0.0;
for (int c = 0; c < coords; c++)
{
double diff = a [idx].c [c] - a [i].c [c];
dist += diff * diff;
}
dist = MathSqrt (dist);
if (dist < minDist)
{
minDist = dist;
nearest = i;
}
}
return nearest;
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_CEO::CalculateEliteRadii ()
{
// Вычисляем радиусы влияния элитных на основе их взаимных расстояний
for (int i = 0; i < popSize; i++)
{
if (!crystalData [i].isElite) continue;
double minDistToOtherElite = DBL_MAX;
for (int j = 0; j < popSize; j++)
{
if (i == j) continue;
if (!crystalData [j].isElite) continue;
double dist = 0.0;
for (int c = 0; c < coords; c++)
{
double diff = a [i].c [c] - a [j].c [c];
dist += diff * diff;
}
dist = MathSqrt (dist);
if (dist < minDistToOtherElite)
{
minDistToOtherElite = dist;
}
}
crystalData [i].radius = minDistToOtherElite / 2.0;
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
double C_AO_CEO::GetDynamicExplorationRate ()
{
// Адаптивная интенсивность исследования
// Начинаем с высокой, уменьшаем со временем
double progress = (double)iterationCount / 100.0;
if (progress > 1.0) progress = 1.0;
return explorationRate * (1.0 - 0.7 * progress);
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_CEO::Revision ()
{
double prevBest = fB;
// Обновляем статус элитных кристаллов
UpdateEliteStatus ();
fB = a [0].f;
ArrayCopy (cB, a [0].c, 0, 0, coords);
// Отслеживаем стагнацию
if (MathAbs (fB - prevBest) < 1e-10) noImprovementCount++;
else noImprovementCount = 0;
}
//————————————————————————————————————————————————————————————————————