//+——————————————————————————————————————————————————————————————————+ //| 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); } //————————————————————————————————————————————————————————————————————