328 lines
19 KiB
Plaintext
328 lines
19 KiB
Plaintext
//+——————————————————————————————————————————————————————————————————+
|
||
//| C_AO_DO |
|
||
//| Copyright 2007-2025, Andrey Dik |
|
||
//| https://www.mql5.com/ru/users/joo |
|
||
//+——————————————————————————————————————————————————————————————————+
|
||
|
||
#include "#C_AO.mqh"
|
||
|
||
//————————————————————————————————————————————————————————————————————
|
||
struct S_DO_Coord
|
||
{
|
||
double v;
|
||
};
|
||
//————————————————————————————————————————————————————————————————————
|
||
|
||
//————————————————————————————————————————————————————————————————————
|
||
class C_AO_DO : public C_AO
|
||
{
|
||
public:
|
||
~C_AO_DO () { }
|
||
|
||
C_AO_DO ()
|
||
{
|
||
ao_name = "DO";
|
||
ao_desc = "Dandelion Optimizer";
|
||
ao_link = "https://www.mql5.com/ru/articles/20540";
|
||
|
||
popSize = 50;
|
||
|
||
ArrayResize (params, 1);
|
||
params [0].name = "popSize"; params [0].val = popSize;
|
||
}
|
||
|
||
void SetParams ()
|
||
{
|
||
popSize = (int)params [0].val;
|
||
}
|
||
|
||
bool Init (const double &rangeMinP [],
|
||
const double &rangeMaxP [],
|
||
const double &rangeStepP [],
|
||
const int epochsP);
|
||
|
||
void Moving ();
|
||
void Revision ();
|
||
|
||
private: //—————————————————————————————————————————————————————————
|
||
int epochs; // maximum iterations
|
||
int currentEpoch; // current iteration
|
||
double sigma_u; // precomputed Levy sigma for beta=1.5
|
||
|
||
S_DO_Coord mean []; // mean position
|
||
S_DO_Coord levy []; // levy flight steps
|
||
S_DO_Coord center []; // center of search range
|
||
S_DO_Coord range []; // range width
|
||
|
||
void LevyFlight ();
|
||
double LognormalPDF (double x, double mu, double sigma);
|
||
void BoundaryControl (int idx);
|
||
};
|
||
//————————————————————————————————————————————————————————————————————
|
||
|
||
//————————————————————————————————————————————————————————————————————
|
||
bool C_AO_DO::Init (const double &rangeMinP [],
|
||
const double &rangeMaxP [],
|
||
const double &rangeStepP [],
|
||
const int epochsP)
|
||
{
|
||
if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
|
||
|
||
//------------------------------------------------------------------
|
||
epochs = epochsP;
|
||
currentEpoch = 0;
|
||
|
||
ArrayResize (mean, coords);
|
||
ArrayResize (levy, coords);
|
||
ArrayResize (center, coords);
|
||
ArrayResize (range, coords);
|
||
|
||
// Предвычисление центра и ширины диапазона для каждой координаты
|
||
for (int c = 0; c < coords; c++)
|
||
{
|
||
center [c].v = (rangeMax [c] + rangeMin [c]) * 0.5;
|
||
range [c].v = rangeMax [c] - rangeMin [c];
|
||
}
|
||
|
||
// Предвычисление sigma_u для Levy flight с beta=1.5
|
||
sigma_u = 0.6966;
|
||
|
||
return true;
|
||
}
|
||
//————————————————————————————————————————————————————————————————————
|
||
|
||
//————————————————————————————————————————————————————————————————————
|
||
void C_AO_DO::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]);
|
||
}
|
||
}
|
||
|
||
revision = true;
|
||
return;
|
||
}
|
||
|
||
//------------------------------------------------------------------
|
||
currentEpoch++;
|
||
double t = (double)currentEpoch;
|
||
double T = (double)epochs;
|
||
|
||
// Параметр alpha (eq. 8): alpha = rand * (t?/T? - 2t/T + 1)
|
||
double alpha = u.RNDfromCI (0.0, 1.0) * ((t * t) / (T * T) - 2.0 * t / T + 1.0);
|
||
|
||
// Параметры для k (eq. 11)
|
||
double denom = T * T - 2.0 * T + 1.0;
|
||
if (MathAbs (denom) < 1e-10) denom = 1e-10;
|
||
double aa = 1.0 / denom;
|
||
double bb = -2.0 * aa;
|
||
double cc = 1.0 - aa - bb;
|
||
double k = 1.0 - u.RNDfromCI (0.0, 1.0) * (cc + aa * t * t + bb * t);
|
||
|
||
//==================================================================
|
||
// Rising stage (Фаза подъёма)
|
||
//==================================================================
|
||
if (u.RNDprobab () < 0.8)
|
||
{
|
||
// Вихревой подъём (eq. 5)
|
||
for (int i = 0; i < popSize; i++)
|
||
{
|
||
double theta = (2.0 * u.RNDfromCI (0.0, 1.0) - 1.0) * M_PI;
|
||
double row = 1.0 / MathExp (theta);
|
||
double vx = row * MathCos (theta);
|
||
double vy = row * MathSin (theta);
|
||
double vxvy = vx * vy;
|
||
|
||
for (int c = 0; c < coords; c++)
|
||
{
|
||
double lamb = MathAbs (u.GaussDistribution (0, 1, -3, 3));
|
||
double lognPDF = LognormalPDF (lamb, 0.0, 1.0);
|
||
double NEW = u.RNDfromCI (rangeMin [c], rangeMax [c]);
|
||
|
||
a [i].c [c] = a [i].c [c] + alpha * vxvy * lognPDF * (NEW - a [i].c [c]);
|
||
}
|
||
|
||
BoundaryControl (i);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// Линейный подъём (eq. 10) - масштабирование относительно центра диапазона
|
||
for (int i = 0; i < popSize; i++)
|
||
{
|
||
for (int c = 0; c < coords; c++)
|
||
{
|
||
// Смещение относительно центра, масштабирование, возврат
|
||
double offset = a [i].c [c] - center [c].v;
|
||
a [i].c [c] = center [c].v + offset * k;
|
||
}
|
||
|
||
BoundaryControl (i);
|
||
}
|
||
}
|
||
|
||
//==================================================================
|
||
// Decline stage (Фаза снижения)
|
||
//==================================================================
|
||
// Вычисление среднего положения (eq. 14)
|
||
for (int c = 0; c < coords; c++)
|
||
{
|
||
mean [c].v = 0.0;
|
||
}
|
||
|
||
for (int i = 0; i < popSize; i++)
|
||
{
|
||
for (int c = 0; c < coords; c++)
|
||
{
|
||
mean [c].v += a [i].c [c];
|
||
}
|
||
}
|
||
|
||
for (int c = 0; c < coords; c++)
|
||
{
|
||
mean [c].v /= popSize;
|
||
}
|
||
|
||
// Обновление позиций (eq. 13)
|
||
for (int i = 0; i < popSize; i++)
|
||
{
|
||
for (int c = 0; c < coords; c++)
|
||
{
|
||
// Ограничиваем beta типичным диапазоном нормального распределения
|
||
double beta = u.GaussDistribution (0, 1, -3, 3);
|
||
double betaAlpha = beta * alpha;
|
||
|
||
double delta = -betaAlpha * (mean [c].v - betaAlpha * a [i].c [c]);
|
||
|
||
// Ограничиваем смещение
|
||
double maxDelta = range [c].v * 0.3;
|
||
if (delta > maxDelta) delta = maxDelta;
|
||
if (delta < -maxDelta) delta = -maxDelta;
|
||
|
||
a [i].c [c] = a [i].c [c] + delta;
|
||
}
|
||
|
||
BoundaryControl (i);
|
||
}
|
||
|
||
//==================================================================
|
||
// Landing stage (Фаза приземления)
|
||
//==================================================================
|
||
double ratio = 2.0 * t / T;
|
||
if (ratio > 2.0) ratio = 2.0; // Ограничение ratio
|
||
|
||
for (int i = 0; i < popSize; i++)
|
||
{
|
||
LevyFlight ();
|
||
|
||
for (int c = 0; c < coords; c++)
|
||
{
|
||
double elite = cB [c];
|
||
double current = a [i].c [c];
|
||
|
||
// eq. 15: x = Elite + levy * alpha * (Elite - x * ratio)
|
||
double delta = levy [c].v * alpha * (elite - current * ratio);
|
||
|
||
// Ограничиваем итоговое смещение половиной диапазона
|
||
double maxDelta = range [c].v * 0.5;
|
||
|
||
if (delta > maxDelta) delta = maxDelta;
|
||
if (delta < -maxDelta) delta = -maxDelta;
|
||
|
||
a [i].c [c] = elite + delta;
|
||
}
|
||
|
||
BoundaryControl (i);
|
||
}
|
||
}
|
||
//————————————————————————————————————————————————————————————————————
|
||
|
||
//————————————————————————————————————————————————————————————————————
|
||
void C_AO_DO::LevyFlight ()
|
||
{
|
||
// Levy flight с beta = 1.5
|
||
for (int c = 0; c < coords; c++)
|
||
{
|
||
double uu = u.GaussDistribution (0, sigma_u, -3.0 * sigma_u, 3.0 * sigma_u);
|
||
double vv = u.GaussDistribution (0, 1, -3, 3);
|
||
|
||
if (MathAbs (vv) < 1e-10) vv = 1e-10;
|
||
|
||
levy [c].v = uu / MathPow (MathAbs (vv), 0.6667);
|
||
}
|
||
}
|
||
//————————————————————————————————————————————————————————————————————
|
||
|
||
//————————————————————————————————————————————————————————————————————
|
||
double C_AO_DO::LognormalPDF (double x, double mu, double sigma)
|
||
{
|
||
if (x <= 0.0) return 0.0;
|
||
|
||
double logx = MathLog (x);
|
||
double diff = logx - mu;
|
||
double coeff = 1.0 / (x * sigma * MathSqrt (2.0 * M_PI));
|
||
double expon = MathExp (-diff * diff / (2.0 * sigma * sigma));
|
||
|
||
return coeff * expon;
|
||
}
|
||
//————————————————————————————————————————————————————————————————————
|
||
|
||
//————————————————————————————————————————————————————————————————————
|
||
void C_AO_DO::BoundaryControl (int idx)
|
||
{
|
||
for (int c = 0; c < coords; c++)
|
||
{
|
||
double val = a [idx].c [c];
|
||
double min = rangeMin [c];
|
||
double max = rangeMax [c];
|
||
|
||
// Итеративное отражение от границ (максимум 10 итераций)
|
||
int iter = 0;
|
||
while ((val < min || val > max) && iter < 10)
|
||
{
|
||
if (val < min) val = min + (min - val);
|
||
if (val > max) val = max - (val - max);
|
||
iter++;
|
||
}
|
||
|
||
// Если отражение не помогло, случайная позиция
|
||
if (val < min || val > max)
|
||
{
|
||
val = u.RNDfromCI (min, max);
|
||
}
|
||
|
||
a [idx].c [c] = u.SeInDiSp (val, min, max, rangeStep [c]);
|
||
}
|
||
}
|
||
//————————————————————————————————————————————————————————————————————
|
||
|
||
//————————————————————————————————————————————————————————————————————
|
||
void C_AO_DO::Revision ()
|
||
{
|
||
int bestIdx = 0;
|
||
double bestFit = a [0].f;
|
||
|
||
for (int i = 1; i < popSize; i++)
|
||
{
|
||
if (a [i].f > bestFit)
|
||
{
|
||
bestFit = a [i].f;
|
||
bestIdx = i;
|
||
}
|
||
}
|
||
|
||
if (bestFit > fB)
|
||
{
|
||
fB = bestFit;
|
||
ArrayCopy (cB, a [bestIdx].c, 0, 0, coords);
|
||
}
|
||
}
|
||
//———————————————————————————————————————————————————————————————————— |