//+------------------------------------------------------------------+ //| C_AO_CSO | //| Copyright 2007-2026, Andrey Dik | //| https://www.mql5.com/ru/users/joo | //+------------------------------------------------------------------+ #include "#C_AO.mqh" //———————————————————————————————————————————————————————————————————— class C_AO_CSO : public C_AO { public: //---------------------------------------------------------- ~C_AO_CSO () { } C_AO_CSO () { ao_name = "CSO"; ao_desc = "Competitive Swarm Optimizer"; ao_link = "https://www.mql5.com/ru/articles/21727"; popSize = 50; phi = 0.2; ArrayResize (params, 2); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "phi"; params [1].val = phi; } void SetParams () { popSize = (int)params [0].val; phi = params [1].val; } bool Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0); void Moving (); void Revision (); //------------------------------------------------------------------ double phi; // коэффициент притяжения к центру роя φ ∈ [0, 1] private: //--------------------------------------------------------- double V []; // скорости частиц [popSize * coords] double Vmax []; // ограничение скорости [coords] double center []; // центр роя [coords] int rlist []; // вспомогательный массив перемешивания [popSize] int nPairs; // число пар = popSize / 2 bool initialized; int Idx (int i, int c) { return i * coords + c; } double Clamp (double v, double lo, double hi) { return v < lo ? lo : v > hi ? hi : v; } void Shuffle (); // Fisher-Yates перемешивание rlist }; //———————————————————————————————————————————————————————————————————— //———————————————————————————————————————————————————————————————————— bool C_AO_CSO::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //------------------------------------------------------------------ nPairs = popSize / 2; // нечётный агент не участвует в соревновании ArrayResize (V, popSize * coords); ArrayResize (Vmax, coords); ArrayResize (center, coords); ArrayResize (rlist, popSize); // Vmax = половина диапазона по каждой оси for (int c = 0; c < coords; c++) Vmax [c] = (rangeMax [c] - rangeMin [c]) * 0.5; // Начальные позиции — случайные; скорости = 0 (как в оригинальной статье) for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { a [i].cP [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); V [Idx (i, c)] = 0.0; } a [i].fB = -DBL_MAX; } initialized = false; return true; } //———————————————————————————————————————————————————————————————————— //———————————————————————————————————————————————————————————————————— // Moving: копируем текущие позиции cP → c для внешней оценки фитнеса void C_AO_CSO::Moving () { for (int i = 0; i < popSize; i++) for (int c = 0; c < coords; c++) a [i].c [c] = u.SeInDiSp (a [i].cP [c], rangeMin [c], rangeMax [c], rangeStep [c]); } //———————————————————————————————————————————————————————————————————— //———————————————————————————————————————————————————————————————————— // Revision: // 1. Обновить личные / глобальный лучшие // 2. Вычислить центр роя // 3. Сформировать случайные пары, определить победителей / проигравших // 4. Обновить скорости и позиции проигравших (формулы из статьи) void C_AO_CSO::Revision () { //--- 1. Обновление лучших ---------------------------------------- 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, coords); } if (a [i].f > fB) { fB = a [i].f; ArrayCopy (cB, a [i].c, 0, 0, coords); } } initialized = true; //--- 2. Центр роя: center = (1/m) * Σ cP_i --------------------- ArrayInitialize (center, 0.0); for (int i = 0; i < popSize; i++) for (int c = 0; c < coords; c++) center [c] += a [i].cP [c]; for (int c = 0; c < coords; c++) center [c] /= (double)popSize; //--- 3. Случайные пары ------------------------------------------ Shuffle (); //--- 4. Попарные соревнования и обновление проигравших ---------- for (int k = 0; k < nPairs; k++) { int ai = rlist [k]; int bi = rlist [k + nPairs]; // Максимизация: проигравший = агент с меньшим фитнесом int li = (a [ai].f < a [bi].f) ? ai : bi; // loser int wi = (a [ai].f < a [bi].f) ? bi : ai; // winner // Формула скорости (Eq. 3 из статьи): // v_l(t+1) = r1⊙v_l(t) + r2⊙(x_w(t)−x_l(t)) + φ·r3⊙(x̄(t)−x_l(t)) // // Формула позиции: // x_l(t+1) = x_l(t) + v_l(t+1) for (int c = 0; c < coords; c++) { double r1 = u.RNDfromCI (0.0, 1.0); double r2 = u.RNDfromCI (0.0, 1.0); double r3 = u.RNDfromCI (0.0, 1.0); int idx = Idx (li, c); double vNew = r1 * V [idx] + r2 * (a [wi].cP [c] - a [li].cP [c]) + phi * r3 * (center [c] - a [li].cP [c]); vNew = Clamp (vNew, -Vmax [c], Vmax [c]); double xNew = Clamp (a [li].cP [c] + vNew, rangeMin [c], rangeMax [c]); V [idx] = vNew; a [li].cP [c] = xNew; // Позиция победителя cP[wi] не изменяется } } } //———————————————————————————————————————————————————————————————————— //———————————————————————————————————————————————————————————————————— // Fisher-Yates перемешивание массива rlist void C_AO_CSO::Shuffle () { for (int i = 0; i < popSize; i++) rlist [i] = i; for (int i = popSize - 1; i > 0; i--) { int j = (int)(u.RNDfromCI (0.0, 1.0) * (i + 1)); if (j > i) j = i; int tmp = rlist [i]; rlist [i] = rlist [j]; rlist [j] = tmp; } } //————————————————————————————————————————————————————————————————————