Files
Population-optimization-alg…/MQL5/Include/Math/AOs/PopulationAO/AO_ECOc_EcologicalCycleOptimizer.mqh
T
2026-01-12 00:16:29 +04:00

432 lines
25 KiB
Plaintext

//+——————————————————————————————————————————————————————————————————+
//| C_AO_ECOc |
//| Copyright 2007-2025, Andrey Dik |
//| https://www.mql5.com/ru/users/joo |
//+——————————————————————————————————————————————————————————————————+
#include "#C_AO.mqh"
//————————————————————————————————————————————————————————————————————
class C_AO_ECOc : public C_AO
{
public:
~C_AO_ECOc () { }
C_AO_ECOc ()
{
ao_name = "ECOc";
ao_desc = "Ecological Cycle Optimizer";
ao_link = "https://www.mql5.com/ru/articles/20611";
popSize = 50;
ratioProd = 0.2;
ratioHerb = 0.3;
ratioCarn = 0.3;
ratioOmni = 0.2;
ArrayResize (params, 5);
params [0].name = "popSize"; params [0].val = popSize;
params [1].name = "ratioProd"; params [1].val = ratioProd;
params [2].name = "ratioHerb"; params [2].val = ratioHerb;
params [3].name = "ratioCarn"; params [3].val = ratioCarn;
params [4].name = "ratioOmni"; params [4].val = ratioOmni;
}
void SetParams ()
{
popSize = (int)params [0].val;
ratioProd = params [1].val;
ratioHerb = params [2].val;
ratioCarn = params [3].val;
ratioOmni = params [4].val;
}
bool Init (const double &rangeMinP [],
const double &rangeMaxP [],
const double &rangeStepP [],
const int epochsP);
void Moving ();
void Revision ();
//------------------------------------------------------------------
double ratioProd;
double ratioHerb;
double ratioCarn;
double ratioOmni;
private: //—————————————————————————————————————————————————————————
int maxIter;
int currIter;
int numProd;
int numHerb;
int numCarn;
int numOmni;
// Индексы групп в общем массиве a[]
int prodEnd;
int herbEnd;
int carnEnd;
// Коэффициент хищничества
double huntCoef [];
// Временный массив для сортировки
S_AO_Agent aT [];
// Вспомогательные методы
void SelectFromGroup (int startIdx, int endIdx, int selCount, int &indices []);
double VectorNorm (int idx1, int idx2);
};
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
bool C_AO_ECOc::Init (const double &rangeMinP [],
const double &rangeMaxP [],
const double &rangeStepP [],
const int epochsP)
{
if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
//------------------------------------------------------------------
maxIter = epochsP;
currIter = 0;
// Вычисление размеров групп
numProd = (int)MathRound (popSize * ratioProd);
numHerb = (int)MathRound (popSize * ratioHerb);
numCarn = (int)MathRound (popSize * ratioCarn);
numOmni = popSize - numProd - numHerb - numCarn;
if (numProd < 1) numProd = 1;
if (numHerb < 1) numHerb = 1;
if (numCarn < 1) numCarn = 1;
if (numOmni < 1) numOmni = 1;
// Корректировка numOmni если сумма не равна popSize
numOmni = popSize - numProd - numHerb - numCarn;
// Индексы групп (начало группы = конец предыдущей)
prodEnd = numProd;
herbEnd = prodEnd + numHerb;
carnEnd = herbEnd + numCarn;
// Выделяем память
ArrayResize (huntCoef, coords);
ArrayResize (aT, popSize);
ArrayResize (u.roulette, popSize);
for (int i = 0; i < popSize; i++) aT [i].Init (coords);
return true;
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_ECOc::Moving ()
{
//------------------------------------------------------------------
// Первая итерация: случайная инициализация
if (!revision)
{
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]);
// Сохраняем в cP для сравнения в Revision
a [i].cP [c] = a [i].c [c];
}
a [i].f = -DBL_MAX;
a [i].fP = -DBL_MAX;
}
revision = true;
return;
}
//------------------------------------------------------------------
currIter++;
double t = (double)currIter;
double T = (double)maxIter;
// Обновление коэффициента хищничества G
for (int c = 0; c < coords; c++)
{
int sign = (u.RNDprobab () < 0.5) ? -1 : 1;
huntCoef [c] = 1.0 + 2.0 * u.RNDfromCI (0.0, 1.0) * MathExp (-9.0 * MathPow (t / T, 3)) * sign;
}
//==================================================================
// (1) Стратегия продуцентов - сортировка популяции по фитнесу
//==================================================================
// Используем готовую сортировку из утилит
u.Sorting (a, aT, popSize);
// Обновляем лучшее решение
if (a [0].f > fB)
{
fB = a [0].f;
ArrayCopy (cB, a [0].c, 0, 0, coords);
}
//==================================================================
// (2) Стратегия травоядных - движение к продуцентам
//==================================================================
int selProd [];
SelectFromGroup (0, prodEnd, 3, selProd);
for (int i = prodEnd; i < herbEnd; i++)
{
double r1 = u.RNDfromCI (0.0, 1.0);
double r2 = u.RNDfromCI (0.0, 1.0);
double r3 = u.RNDfromCI (0.0, 1.0);
for (int c = 0; c < coords; c++)
{
double move = r1 * (a [selProd [0]].c [c] - a [i].c [c]) +
r2 * (a [selProd [1]].c [c] - a [i].c [c]) +
r3 * (a [selProd [2]].c [c] - a [i].c [c]);
a [i].c [c] = a [i].c [c] + huntCoef [c] * move;
}
}
//==================================================================
// (3) Стратегия плотоядных - движение к травоядным
//==================================================================
int selHerb [];
SelectFromGroup (prodEnd, herbEnd, 3, selHerb);
for (int i = herbEnd; i < carnEnd; i++)
{
double r1 = u.RNDfromCI (0.0, 1.0);
double r2 = u.RNDfromCI (0.0, 1.0);
double r3 = u.RNDfromCI (0.0, 1.0);
for (int c = 0; c < coords; c++)
{
double move = r1 * (a [selHerb [0]].c [c] - a [i].c [c]) +
r2 * (a [selHerb [1]].c [c] - a [i].c [c]) +
r3 * (a [selHerb [2]].c [c] - a [i].c [c]);
a [i].c [c] = a [i].c [c] + huntCoef [c] * move;
}
}
//==================================================================
// (4) Стратегия всеядных - движение ко всем группам
//==================================================================
int selProd1 [];
int selHerb1 [];
int selCarn2 [];
SelectFromGroup (0, prodEnd, 1, selProd1);
SelectFromGroup (prodEnd, herbEnd, 1, selHerb1);
SelectFromGroup (herbEnd, carnEnd, 2, selCarn2);
for (int i = carnEnd; i < popSize; i++)
{
double r1 = u.RNDfromCI (0.0, 1.0);
double r2 = u.RNDfromCI (0.0, 1.0);
double r3 = u.RNDfromCI (0.0, 1.0);
double r4 = u.RNDfromCI (0.0, 1.0);
for (int c = 0; c < coords; c++)
{
double move = r1 * (a [selProd1 [0]].c [c] - a [i].c [c]) +
r2 * (a [selHerb1 [0]].c [c] - a [i].c [c]) +
r3 * (a [selCarn2 [0]].c [c] - a [i].c [c]) +
r4 * (a [selCarn2 [1]].c [c] - a [i].c [c]);
a [i].c [c] = a [i].c [c] + huntCoef [c] * move;
}
}
//==================================================================
// (5) Стратегия декомпозиторов
//==================================================================
// Сохраняем текущие позиции в cP перед декомпозицией
for (int i = 0; i < popSize; i++)
{
for (int c = 0; c < coords; c++)
{
a [i].cP [c] = a [i].c [c];
}
a [i].fP = a [i].f;
}
// Лучший агент - индекс 0 после сортировки
int bestIdx = 0;
// Декомпозиция
for (int i = 0; i < popSize; i++)
{
double rnd = u.RNDfromCI (0.0, 1.0);
if (rnd < 0.5)
{
//--- Оптимальная декомпозиция ---
double coef = 0.4 * u.RNDfromCI (0.0, 1.0) - 0.2;
for (int c = 0; c < coords; c++)
{
double randC = u.RNDfromCI (0.0, 1.0);
double bestNeighbor = a [bestIdx].cP [c] * randC;
a [i].c [c] = bestNeighbor + coef * (bestNeighbor - a [i].cP [c]);
}
}
else
if (rnd < 0.75)
{
//--- Локальная случайная декомпозиция ---
double dist = VectorNorm (bestIdx, i);
// Генерация случайного единичного вектора
double randDir [];
ArrayResize (randDir, coords);
double norm = 0.0;
for (int c = 0; c < coords; c++)
{
randDir [c] = 2.0 * u.RNDfromCI (0.0, 1.0) - 1.0;
norm += randDir [c] * randDir [c];
}
norm = MathSqrt (norm) + 1e-10;
double randMult = u.RNDfromCI (0.0, 1.0);
for (int c = 0; c < coords; c++)
{
randDir [c] /= norm;
a [i].c [c] = a [i].cP [c] + randMult * dist * randDir [c];
}
}
else
{
//--- Глобальная случайная декомпозиция ---
double tRatio = t / (1.5 * T);
if (tRatio > 1.0) tRatio = 1.0;
double H = MathPow (1.0 - tRatio, 5.0 * t / T) * MathCos (M_PI * u.RNDfromCI (0.0, 1.0));
// min(Low - Up)
double minRange = rangeMin [0] - rangeMax [0];
for (int c = 1; c < coords; c++)
{
double diff = rangeMin [c] - rangeMax [c];
if (diff < minRange) minRange = diff;
}
double randWalk = (2.0 / 3.0) * H * u.RNDfromCI (0.0, 1.0) * minRange;
double weight = u.RNDfromCI (0.0, 1.0);
for (int c = 0; c < coords; c++)
{
a [i].c [c] = weight * a [i].cP [c] + (1.0 - weight) * randWalk;
}
}
// Проверка границ и дискретизация
for (int c = 0; c < coords; c++)
{
if (a [i].c [c] < rangeMin [c] || a [i].c [c] > rangeMax [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]);
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_ECOc::Revision ()
{
for (int i = 0; i < popSize; i++)
{
// Если новая позиция хуже - откат к предыдущей
if (a [i].f <= a [i].fP)
{
a [i].f = a [i].fP;
ArrayCopy (a [i].c, a [i].cP, 0, 0, coords);
}
// Обновление лучшего глобального решения
if (a [i].f > fB)
{
fB = a [i].f;
ArrayCopy (cB, a [i].c, 0, 0, coords);
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_ECOc::SelectFromGroup (int startIdx, int endIdx, int selCount, int &indices [])
{
ArrayResize (indices, selCount);
int groupSize = endIdx - startIdx;
if (groupSize <= 0)
{
for (int s = 0; s < selCount; s++) indices [s] = startIdx;
return;
}
if (groupSize < selCount)
{
for (int s = 0; s < selCount; s++) indices [s] = startIdx + (s % groupSize);
return;
}
// Подготовка рулетки для группы
double minFit = a [startIdx].f;
for (int i = startIdx + 1; i < endIdx; i++)
{
if (a [i].f < minFit) minFit = a [i].f;
}
// Заполняем структуру рулетки
double cumSum = 0.0;
for (int i = 0; i < groupSize; i++)
{
u.roulette [i].start = cumSum;
double prob = a [startIdx + i].f - minFit + 1e-10;
cumSum += prob;
u.roulette [i].end = cumSum;
}
// Выбор
for (int s = 0; s < selCount; s++)
{
double r = u.RNDfromCI (0.0, cumSum);
indices [s] = startIdx;
for (int i = 0; i < groupSize; i++)
{
if (r >= u.roulette [i].start && r < u.roulette [i].end)
{
indices [s] = startIdx + i;
break;
}
}
}
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
double C_AO_ECOc::VectorNorm (int idx1, int idx2)
{
double sum = 0.0;
for (int c = 0; c < coords; c++)
{
double diff = a [idx1].cP [c] - a [idx2].cP [c];
sum += diff * diff;
}
return MathSqrt (sum);
}
//————————————————————————————————————————————————————————————————————