Files
2026-03-20 20:35:26 +04:00

469 lines
32 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//+——————————————————————————————————————————————————————————————————+
//| C_AO_FBL |
//| Copyright 2007-2025, Andrey Dik |
//| https://www.mql5.com/ru/users/joo |
//+——————————————————————————————————————————————————————————————————+
#include "#C_AO.mqh"
//────────────────────────────────────────────────────────────────────
class C_AO_FBL : public C_AO
{
public: //------------------------------------------------------------
~C_AO_FBL () { }
C_AO_FBL ()
{
ao_name = "FBL";
ao_desc = "Flock by Leader";
ao_link = "https://www.mql5.com/ru/articles/21821";
popSize = 50;
k = 5;
phi = 0.5; // [F1]
wSep = 0.15; // [F6]
ArrayResize (params, 4);
params [0].name = "popSize"; params [0].val = popSize;
params [1].name = "k"; params [1].val = k;
params [2].name = "phi"; params [2].val = phi;
params [3].name = "wSep"; params [3].val = wSep;
}
void SetParams ()
{
popSize = (int)params [0].val;
k = (int)params [1].val;
phi = params [2].val;
wSep = params [3].val;
}
bool Init (const double &rangeMinP [],
const double &rangeMaxP [],
const double &rangeStepP [],
const int epochsP = 0);
void Moving ();
void Revision ();
//------------------------------------------------------------------
int k; // k ближайших соседей для DkNB / DR-kNB / ARF
double phi; // коэффициент притяжения к gBest
double wSep; // вес вектора separation [F6]
private: //---------------------------------------------------------
double V []; // скорости [popSize * coords]
double Vsnap []; // снапшот скоростей [popSize * coords] [F4]
double Vmax []; // ограничение скорости [coords]
double cPsnap []; // снапшот позиций [popSize * coords]
double sepVec []; // вектор separation [coords]
double distM []; // норм. матрица расст. [popSize × popSize]
double dMax []; // d_max^i [popSize]
int dknbSz []; // |DkNB_t(x_i)| [popSize]
int drknbSz []; // |DR-kNB_t(x_i)| [popSize]
double arf []; // ARF_t(A_i) [popSize]
int knnIdx []; // k-NN индексы [popSize * kEff]
int subflockId []; // субфлок агента (-1=выброс) [popSize]
int leaderIdx []; // индекс лидера субфлока [popSize]
int role []; // 0=лидер 1=послед. 2=выброс [popSize]
int prevRole []; // роль предыдущей итерации [popSize]
int centroidBuf []; // центроиды, sorted by ARF↓ [popSize]
int subflockLeader []; // лидер субфлока s [popSize]
double subflockBestF []; // лучший fB в субфлоке s [popSize]
double rankBuf []; // Rank_t(A_i) Formula 5 [popSize] [F7]
double tmpDist []; // буфер k-NN сортировки [popSize]
int tmpIdx_ []; // буфер k-NN индексов [popSize]
int kEff; // min(k, popSize-1)
int nCentroids; // число субфлоков на итерации
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 CalcSep (int i); // вычислить sepVec[] для агента i
};
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
bool C_AO_FBL::Init (const double &rangeMinP [],
const double &rangeMaxP [],
const double &rangeStepP [],
const int epochsP = 0)
{
if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
kEff = MathMin (k, popSize - 1);
if (kEff < 1) kEff = 1;
ArrayResize (V, popSize * coords);
ArrayResize (Vsnap, popSize * coords); // [F4]
ArrayResize (Vmax, coords);
ArrayResize (cPsnap, popSize * coords);
ArrayResize (sepVec, coords);
ArrayResize (distM, popSize * popSize);
ArrayResize (dMax, popSize);
ArrayResize (dknbSz, popSize);
ArrayResize (drknbSz, popSize);
ArrayResize (arf, popSize);
ArrayResize (knnIdx, popSize * kEff);
ArrayResize (subflockId, popSize);
ArrayResize (leaderIdx, popSize);
ArrayResize (role, popSize);
ArrayResize (prevRole, popSize);
ArrayResize (centroidBuf, popSize);
ArrayResize (subflockLeader, popSize);
ArrayResize (subflockBestF, popSize);
ArrayResize (rankBuf, popSize);
ArrayResize (tmpDist, popSize);
ArrayResize (tmpIdx_, popSize);
for (int c = 0; c < coords; c++)
Vmax [c] = (rangeMax [c] - rangeMin [c]) * 0.5;
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;
Vsnap[Idx (i, c)] = 0.0; // [F4]
}
a [i].fB = -DBL_MAX;
role [i] = 1;
prevRole [i] = 1;
leaderIdx[i] = -1;
}
nCentroids = 0;
return true;
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_FBL::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]);
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
// [O2][F6] Separation-вектор для агента i → sepVec[].
// Отталкивание от k-NN соседей ближе dSep = 0.5 * dMax[i].
// sepVec[] в абсолютных единицах позиций, масштаб задаёт wSep.
void C_AO_FBL::CalcSep (int i)
{
ArrayInitialize (sepVec, 0.0);
double dSep = 0.5 * dMax [i];
if (dSep < 1e-14) return;
for (int m = 0; m < kEff; m++)
{
int j = knnIdx [i * kEff + m];
double normD = distM [i * popSize + j];
if (normD >= dSep) continue;
double w = (normD > 1e-12) ? (dSep - normD) / dSep : 1.0;
for (int c = 0; c < coords; c++)
sepVec [c] += w * (cPsnap [Idx (i, c)] - cPsnap [Idx (j, c)]);
}
for (int c = 0; c < coords; c++) sepVec [c] *= wSep; // [F6]
}
//————————————————————————————————————————————————————————————————————
//————————————————————————————————————————————————————————————————————
void C_AO_FBL::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);
}
}
//─── 2. Нормированная матрица расстояний ─────────────────────────
for (int i = 0; i < popSize; i++)
{
distM [i * popSize + i] = 0.0;
for (int j = i + 1; j < popSize; j++)
{
double d = 0.0;
for (int c = 0; c < coords; c++)
{
double rng = rangeMax [c] - rangeMin [c];
double df = (rng > 0.0) ? (a [i].cP [c] - a [j].cP [c]) / rng : 0.0;
d += df * df;
}
d = MathSqrt (d);
distM [i * popSize + j] = d;
distM [j * popSize + i] = d;
}
}
//─── 3. k-NN → d_max → |DkNB| (Def 5, Formula 3) ───────────────
for (int i = 0; i < popSize; i++)
{
int cnt = 0;
for (int j = 0; j < popSize; j++)
{
if (j == i) continue;
tmpDist [cnt] = distM [i * popSize + j];
tmpIdx_ [cnt] = j;
cnt++;
}
for (int m = 0; m < kEff; m++)
{
int minPos = m;
for (int n = m + 1; n < cnt; n++)
if (tmpDist [n] < tmpDist [minPos]) minPos = n;
if (minPos != m)
{
double td = tmpDist [m];
tmpDist [m] = tmpDist [minPos];
tmpDist [minPos] = td;
int ti = tmpIdx_ [m];
tmpIdx_ [m] = tmpIdx_ [minPos];
tmpIdx_ [minPos] = ti;
}
}
dMax [i] = tmpDist [kEff - 1];
for (int m = 0; m < kEff; m++) knnIdx [i * kEff + m] = tmpIdx_ [m];
dknbSz [i] = 0;
for (int j = 0; j < popSize; j++)
{
if (j == i) continue;
if (distM [i * popSize + j] <= dMax [i]) dknbSz [i]++;
}
}
//─── 4. |DR-kNB| (Definition 6) ─────────────────────────────────
ArrayInitialize (drknbSz, 0);
for (int i = 0; i < popSize; i++)
for (int j = 0; j < popSize; j++)
{
if (j == i) continue;
if (distM [i * popSize + j] <= dMax [j]) drknbSz [i]++;
}
//─── 5. ARF (Formula 4) ──────────────────────────────────────────
for (int i = 0; i < popSize; i++)
{
int denom = drknbSz [i] + dknbSz [i];
arf [i] = (denom > 0) ? (double)drknbSz [i] / (double)denom : 0.5;
}
//─── 6. Центроиды: ARF >= 0.5, сортировка по Rank↓ (Formula 5) ──
nCentroids = 0;
for (int i = 0; i < popSize; i++)
if (arf [i] >= 0.5) centroidBuf [nCentroids++] = i;
if (nCentroids == 0)
{
int bestI = 0;
for (int i = 1; i < popSize; i++) if (arf [i] > arf [bestI]) bestI = i;
centroidBuf [0] = bestI;
nCentroids = 1;
}
// [F7] Formula 5: Rank_t(A_i) = Log(|N_i,t| / |N_t| * 10) * ARF_t(A_i)
// |N_i,t| = dknbSz[i] (число соседей агента i)
// |N_t| = popSize (число всех агентов)
// Аргумент Log защищён от нуля: если dknbSz == 0 → rank = 0.
for (int i = 0; i < popSize; i++)
{
double ratio = (double)dknbSz [i] / (double)popSize * 10.0;
rankBuf [i] = (ratio > 1e-12) ? MathLog (ratio) * arf [i] : 0.0;
}
// Insertion sort центроидов по Rank↓
for (int a_ = 1; a_ < nCentroids; a_++)
{
int key = centroidBuf [a_];
double keyR = rankBuf [key];
int b_ = a_ - 1;
while (b_ >= 0 && rankBuf [centroidBuf [b_]] < keyR)
{
centroidBuf [b_ + 1] = centroidBuf [b_];
b_--;
}
centroidBuf [b_ + 1] = key;
}
// [F9] Обрезка: оставляем не более popSize/k центроидов.
// Сортировка уже выполнена → в начале массива самые значимые лидеры.
int maxCentroids = MathMax (1, popSize / kEff);
if (nCentroids > maxCentroids) nCentroids = maxCentroids;
//─── 7. Назначение агентов в субфлоки ────────────────────────────
for (int i = 0; i < popSize; i++) subflockId [i] = -1;
for (int s = 0; s < nCentroids; s++)
subflockId [centroidBuf [s]] = s;
for (int i = 0; i < popSize; i++)
{
if (subflockId [i] >= 0) continue;
for (int s = 0; s < nCentroids; s++)
{
if (distM [i * popSize + centroidBuf [s]] <= dMax [centroidBuf [s]])
{
subflockId [i] = s;
break;
}
}
}
//─── 8. Лидер субфлока = агент с наилучшим fB в субфлоке ─────────
// [F2] Используем fB (исторический лучший), а не f (текущий шумный).
for (int s = 0; s < nCentroids; s++)
{
subflockLeader [s] = -1;
subflockBestF [s] = -DBL_MAX;
}
for (int i = 0; i < popSize; i++)
{
int s = subflockId [i];
if (s < 0) continue;
if (a [i].fB > subflockBestF [s]) // [F2] fB вместо f
{
subflockBestF [s] = a [i].fB;
subflockLeader [s] = i;
}
}
// Страховка: если ни один агент субфлока не имел fB > -DBL_MAX
// (например, первая итерация), назначаем центроид лидером,
// чтобы leaderIdx последователей никогда не был -1.
for (int s = 0; s < nCentroids; s++)
if (subflockLeader [s] < 0) subflockLeader [s] = centroidBuf [s];
//─── 9. Роли агентов ─────────────────────────────────────────────
for (int i = 0; i < popSize; i++)
{
int s = subflockId [i];
if (s < 0)
{
role [i] = 2;
leaderIdx [i] = -1;
}
else if (subflockLeader [s] == i)
{
role [i] = 0;
leaderIdx [i] = -1;
}
else
{
role [i] = 1;
leaderIdx [i] = subflockLeader [s];
}
}
//─── 10. Снапшоты позиций и скоростей перед обновлением ──────────
// [F4] Vsnap снимается здесь — Pass 2 использует согласованные
// старые скорости лидеров, а не уже изменённые в Pass 1.
for (int i = 0; i < popSize; i++)
for (int c = 0; c < coords; c++)
{
cPsnap [Idx (i, c)] = a [i].cP [c];
Vsnap [Idx (i, c)] = V [Idx (i, c)]; // [F4]
}
//─── 11. Pass 1: ЛИДЕРЫ ──────────────────────────────────────────
// v = w·v + r1·(cB_i cP_i) + φ·r2·(gBest cP_i) + sep
// [F10] w=0.729 — коэффициент сужения Clerc-Kennedy (фиксированный).
for (int i = 0; i < popSize; i++)
{
if (role [i] != 0) continue;
// [F3] Смягчённый сброс скорости при первом появлении в роли лидера.
// Накопленный импульс приглушается вдвое, а не обнуляется.
if (prevRole [i] != 0)
for (int c = 0; c < coords; c++) V [Idx (i, c)] *= 0.5; // [F3]
CalcSep (i);
for (int c = 0; c < coords; c++)
{
double r1 = u.RNDfromCI (0.0, 1.0);
double r2 = u.RNDfromCI (0.0, 1.0);
int idx = Idx (i, c);
double vNew = 0.729 * V [idx] // [F10] инерция
+ r1 * (a [i].cB [c] - cPsnap [idx]) // когнитивный
+ phi * r2 * (cB [c] - cPsnap [idx]) // глобальный
+ sepVec [c]; // separation
vNew = Clamp (vNew, -Vmax [c], Vmax [c]);
a [i].cP [c] = Clamp (cPsnap [idx] + vNew, rangeMin [c], rangeMax [c]);
V [idx] = vNew;
}
}
//─── 12. Pass 2: ПОСЛЕДОВАТЕЛИ ───────────────────────────────────
// v = w·v + r1·(cP_L cP_i) + r2·(Vsnap_L Vsnap_i)
// + r3·(cB_i cP_i) + φ·r4·(gBest cP_i) + sep
// [F4] Выравнивание идёт по Vsnap лидера (старая скорость).
// [F10] w=0.729 — коэффициент сужения Clerc-Kennedy (фиксированный).
for (int i = 0; i < popSize; i++)
{
if (role [i] != 1) continue;
int wi = leaderIdx [i];
CalcSep (i);
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);
double r4 = u.RNDfromCI (0.0, 1.0);
int idx = Idx (i, c);
int idxL = Idx (wi, c);
double vNew = 0.729 * V [idx] // [F10] инерция
+ r1 * (cPsnap [idxL] - cPsnap [idx]) // Cohesion
+ r2 * (Vsnap [idxL] - Vsnap [idx]) // Alignment [F4]
+ r3 * (a [i].cB [c] - cPsnap [idx]) // Когнитивный
+ phi * r4 * (cB [c] - cPsnap [idx]) // Глобальный
+ sepVec [c]; // Separation
vNew = Clamp (vNew, -Vmax [c], Vmax [c]);
a [i].cP [c] = Clamp (cPsnap [idx] + vNew, rangeMin [c], rangeMax [c]);
V [idx] = vNew;
}
}
//─── 13. Pass 3: ВЫБРОСЫ ─────────────────────────────────────────
// [F5] r независим для каждой координаты — в многомерном
// пространстве координаты прыгают независимо.
for (int i = 0; i < popSize; i++)
{
if (role [i] != 2) continue;
for (int c = 0; c < coords; c++)
{
double r = u.RNDfromCI (0.0, 1.0); // [F5] per-coordinate
double xNew = (r > 0.5)
? cB [c] + u.RNDfromCI (-1.0, 1.0) * (rangeMax [c] - rangeMin [c]) * 0.2
: u.RNDfromCI (rangeMin [c], rangeMax [c]);
a [i].cP [c] = Clamp (xNew, rangeMin [c], rangeMax [c]);
V [Idx (i, c)] = 0.0;
}
}
//─── 14. Сохранение ролей ────────────────────────────────────────
for (int i = 0; i < popSize; i++) prevRole [i] = role [i];
}
//————————————————————————————————————————————————————————————————————