432 lines
25 KiB
Plaintext
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);
|
|
}
|
|
//———————————————————————————————————————————————————————————————————— |