437 lines
28 KiB
Plaintext
437 lines
28 KiB
Plaintext
//+——————————————————————————————————————————————————————————————————+
|
|
//| C_AO_EO |
|
|
//| Copyright 2007-2025, Andrey Dik |
|
|
//| https://www.mql5.com/ru/users/joo |
|
|
//———————————————————————————————————————————————————————————————————+
|
|
|
|
//Article: https://www.mql5.com/ru/articles/18755
|
|
|
|
#include "#C_AO.mqh"
|
|
/*
|
|
//————————————————————————————————————————————————————————————————————
|
|
class C_AO_EO : public C_AO
|
|
{
|
|
public: //----------------------------------------------------------
|
|
~C_AO_EO () { }
|
|
C_AO_EO ()
|
|
{
|
|
ao_name = "EO";
|
|
ao_desc = "Extremal Optimization";
|
|
ao_link = "https://www.mql5.com/ru/articles/18755";
|
|
|
|
popSize = 50; // Размер популяции
|
|
tau = 1.4; // Параметр степенного распределения (τ)
|
|
greedyStart = 0.5; // Доля агентов с жадной инициализацией
|
|
eliteUpdate = 0.3; // Доля популяции для обновления за итерацию
|
|
|
|
ArrayResize (params, 4);
|
|
|
|
params [0].name = "popSize"; params [0].val = popSize;
|
|
params [1].name = "tau"; params [1].val = tau;
|
|
params [2].name = "greedyStart"; params [2].val = greedyStart;
|
|
params [3].name = "eliteUpdate"; params [3].val = eliteUpdate;
|
|
}
|
|
|
|
void SetParams ()
|
|
{
|
|
popSize = (int)params [0].val;
|
|
tau = params [1].val;
|
|
greedyStart = params [2].val;
|
|
eliteUpdate = params [3].val;
|
|
}
|
|
|
|
bool Init (const double &rangeMinP [],
|
|
const double &rangeMaxP [],
|
|
const double &rangeStepP [],
|
|
const int epochsP = 0);
|
|
|
|
void Moving ();
|
|
void Revision ();
|
|
|
|
//------------------------------------------------------------------
|
|
double tau; // Параметр степенного распределения
|
|
double greedyStart; // Доля жадной инициализации
|
|
double eliteUpdate; // Доля обновляемых агентов
|
|
|
|
private: //---------------------------------------------------------
|
|
// Структуры для ранжирования
|
|
struct RankedComponent
|
|
{
|
|
int agentIdx;
|
|
int componentIdx;
|
|
double fitness; // λi - fitness компонента
|
|
};
|
|
|
|
struct RankedAgent
|
|
{
|
|
int idx;
|
|
double fitness; // общий fitness агента
|
|
};
|
|
|
|
RankedComponent compRanks []; // ранжированные компоненты
|
|
RankedAgent agentRanks []; // ранжированные агенты
|
|
|
|
void ApplyExtremalOptimization ();
|
|
double CalculateComponentFitness (int agentIdx, int componentIdx);
|
|
int SelectRankByPowerLaw (int maxRank);
|
|
};
|
|
//————————————————————————————————————————————————————————————————————
|
|
|
|
//————————————————————————————————————————————————————————————————————
|
|
//--- Инициализация
|
|
bool C_AO_EO::Init (const double &rangeMinP [],
|
|
const double &rangeMaxP [],
|
|
const double &rangeStepP [],
|
|
const int epochsP = 0)
|
|
{
|
|
if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
|
|
|
|
//------------------------------------------------------------------
|
|
ArrayResize (compRanks, coords);
|
|
ArrayResize (agentRanks, popSize);
|
|
|
|
return true;
|
|
}
|
|
//————————————————————————————————————————————————————————————————————
|
|
|
|
//————————————————————————————————————————————————————————————————————
|
|
//--- Основной цикл алгоритма
|
|
void C_AO_EO::Moving ()
|
|
{
|
|
// Начальная инициализация популяции
|
|
if (!revision)
|
|
{
|
|
int greedyCount = (int)(popSize * greedyStart);
|
|
|
|
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;
|
|
}
|
|
|
|
// Применяем Extremal Optimization ---------------------------------
|
|
ApplyExtremalOptimization ();
|
|
}
|
|
//————————————————————————————————————————————————————————————————————
|
|
|
|
//————————————————————————————————————————————————————————————————————
|
|
//--- Применение Extremal Optimization
|
|
void C_AO_EO::ApplyExtremalOptimization ()
|
|
{
|
|
// Количество агентов для обновления на этой итерации
|
|
int numUpdates = MathMax (1, (int)(popSize * eliteUpdate));
|
|
|
|
// Обновляем выбранных агентов по принципу EO
|
|
//for (int update = 0; update < numUpdates; update++)
|
|
for (int update = 0; update < popSize; update++)
|
|
{
|
|
// Шаг 1: Выбираем агента для модификации
|
|
// Используем ранжирование по общему fitness
|
|
int targetAgent;
|
|
|
|
// Ранжируем агентов по fitness (от худшего к лучшему для максимизации)
|
|
for (int i = 0; i < popSize; i++)
|
|
{
|
|
agentRanks [i].idx = i;
|
|
agentRanks [i].fitness = a [i].f;
|
|
}
|
|
|
|
// Сортировка (худшие в начале для максимизации)
|
|
for (int i = 0; i < popSize - 1; i++)
|
|
{
|
|
for (int j = i + 1; j < popSize; j++)
|
|
{
|
|
if (agentRanks [i].fitness > agentRanks [j].fitness)
|
|
{
|
|
RankedAgent temp = agentRanks [i];
|
|
agentRanks [i] = agentRanks [j];
|
|
agentRanks [j] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Выбираем агента согласно степенному распределению
|
|
int rank = SelectRankByPowerLaw (popSize);
|
|
targetAgent = agentRanks [rank].idx;
|
|
|
|
// Шаг 2: Ранжируем компоненты выбранного агента
|
|
for (int c = 0; c < coords; c++)
|
|
{
|
|
compRanks [c].agentIdx = targetAgent;
|
|
compRanks [c].componentIdx = c;
|
|
compRanks [c].fitness = CalculateComponentFitness (targetAgent, c);
|
|
}
|
|
|
|
// Сортировка компонентов (худшие в начале)
|
|
for (int i = 0; i < coords - 1; i++)
|
|
{
|
|
for (int j = i + 1; j < coords; j++)
|
|
{
|
|
if (compRanks [i].fitness > compRanks [j].fitness)
|
|
{
|
|
RankedComponent temp = compRanks [i];
|
|
compRanks [i] = compRanks [j];
|
|
compRanks [j] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Шаг 3: Выбираем компонент для изменения согласно P(n) ∝ n^(-τ)
|
|
int compRank = SelectRankByPowerLaw (coords);
|
|
int compIdx = compRanks [compRank].componentIdx;
|
|
|
|
// Шаг 4: Заменяем выбранный компонент новым случайным значением
|
|
// Это ключевой принцип EO - безусловная замена на случайное
|
|
a [targetAgent].c [compIdx] = u.RNDfromCI (rangeMin [compIdx], rangeMax [compIdx]);
|
|
|
|
// Проверка границ
|
|
a [targetAgent].c [compIdx] = u.SeInDiSp (a [targetAgent].c [compIdx],
|
|
rangeMin [compIdx],
|
|
rangeMax [compIdx],
|
|
rangeStep [compIdx]);
|
|
}
|
|
}
|
|
//————————————————————————————————————————————————————————————————————
|
|
|
|
//————————————————————————————————————————————————————————————————————
|
|
//--- Расчет fitness компонента
|
|
double C_AO_EO::CalculateComponentFitness (int agentIdx, int componentIdx)
|
|
{
|
|
// Для общей задачи оптимизации используем простую метрику
|
|
// λi = относительный вклад компонента в общее качество
|
|
|
|
double fitness = 0.0;
|
|
|
|
double range = rangeMax [componentIdx] - rangeMin [componentIdx];
|
|
if (range > 0)
|
|
{
|
|
// Нормализованное отклонение
|
|
double deviation = MathAbs (a [agentIdx].c [componentIdx] - cB [componentIdx]) / range;
|
|
fitness = 1.0 - deviation; // Инвертируем, чтобы больше = лучше
|
|
}
|
|
|
|
return fitness;
|
|
}
|
|
//————————————————————————————————————————————————————————————————————
|
|
|
|
//————————————————————————————————————————————————————————————————————
|
|
//--- Выбор ранга согласно степенному распределению
|
|
int C_AO_EO::SelectRankByPowerLaw (int maxRank)
|
|
{
|
|
// P(n) ∝ n^(-τ), где n - ранг от 1 до maxRank
|
|
// Используем метод обратного преобразования
|
|
|
|
double r = u.RNDprobab ();
|
|
|
|
if (tau != 1.0)
|
|
{
|
|
// Общий случай: обратное преобразование для P(n) ∝ n^(-τ)
|
|
double norm = (1.0 - MathPow (maxRank + 1.0, 1.0 - tau)) / (1.0 - tau);
|
|
double x = r * norm;
|
|
int rank = (int)MathPow ((1.0 - tau) * x + 1.0, 1.0 / (1.0 - tau)) - 1;
|
|
|
|
if (rank >= maxRank) rank = maxRank - 1;
|
|
if (rank < 0) rank = 0;
|
|
|
|
return rank;
|
|
}
|
|
else
|
|
{
|
|
// Специальный случай τ = 1: P(n) ∝ 1/n
|
|
double norm = MathLog (maxRank + 1.0);
|
|
int rank = (int)(MathExp (r * norm) - 1.0);
|
|
|
|
if (rank >= maxRank) rank = maxRank - 1;
|
|
if (rank < 0) rank = 0;
|
|
|
|
return rank;
|
|
}
|
|
}
|
|
//————————————————————————————————————————————————————————————————————
|
|
|
|
//————————————————————————————————————————————————————————————————————
|
|
//--- Обновление лучших решений
|
|
void C_AO_EO::Revision ()
|
|
{
|
|
// Сортировка популяции для МАКСИМИЗАЦИИ
|
|
static S_AO_Agent aT [];
|
|
ArrayResize (aT, popSize);
|
|
|
|
// Используем встроенную функцию сортировки
|
|
u.Sorting (a, aT, popSize);
|
|
|
|
// Обновление глобального лучшего решения
|
|
if (a [0].f > fB)
|
|
{
|
|
ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY);
|
|
fB = a [0].f;
|
|
}
|
|
}
|
|
//————————————————————————————————————————————————————————————————————
|
|
*/
|
|
|
|
//————————————————————————————————————————————————————————————————————
|
|
class C_AO_EOm : public C_AO
|
|
{
|
|
public: //----------------------------------------------------------
|
|
~C_AO_EOm () { }
|
|
C_AO_EOm ()
|
|
{
|
|
ao_name = "EO";
|
|
ao_desc = "Extremal Optimization";
|
|
ao_link = "https://www.mql5.com/ru/articles/18755";
|
|
|
|
popSize = 50; // Размер популяции
|
|
popRaising = 3; // Повышение самых худших
|
|
mutationRate = 0.1; // Вероятность мутации
|
|
powCh = 2.0; // Степень закона распределения отбора
|
|
powMut = 8.0; // Степень закона распределения мутации
|
|
|
|
ArrayResize (params, 5);
|
|
|
|
params [0].name = "popSize"; params [0].val = popSize;
|
|
params [1].name = "popRaising"; params [1].val = popRaising;
|
|
params [2].name = "mutationRate"; params [2].val = mutationRate;
|
|
params [3].name = "powCh"; params [3].val = powCh;
|
|
params [4].name = "powMut"; params [4].val = powMut;
|
|
}
|
|
|
|
void SetParams ()
|
|
{
|
|
popSize = (int)params [0].val;
|
|
popRaising = (int)params [1].val;
|
|
mutationRate = params [2].val;
|
|
powCh = params [3].val;
|
|
powMut = params [4].val;
|
|
}
|
|
|
|
bool Init (const double &rangeMinP [],
|
|
const double &rangeMaxP [],
|
|
const double &rangeStepP [],
|
|
const int epochsP = 0);
|
|
|
|
void Moving ();
|
|
void Revision ();
|
|
|
|
//------------------------------------------------------------------
|
|
int popRaising; // Повышение самых худших
|
|
double mutationRate; // Вероятность мутации
|
|
double powCh; // Степень закона распределения отбора
|
|
double powMut; // Степень закона распределения мутации
|
|
|
|
private: //---------------------------------------------------------
|
|
int currentEpoch; // текущая эпоха
|
|
int totalEpochs; // общее количество эпох
|
|
|
|
void MutateComponent (int agentIdx, int componentIdx);
|
|
};
|
|
//————————————————————————————————————————————————————————————————————
|
|
|
|
//————————————————————————————————————————————————————————————————————
|
|
//--- Инициализация
|
|
bool C_AO_EOm::Init (const double &rangeMinP [],
|
|
const double &rangeMaxP [],
|
|
const double &rangeStepP [],
|
|
const int epochsP = 0)
|
|
{
|
|
if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
|
|
|
|
//------------------------------------------------------------------
|
|
currentEpoch = 0;
|
|
totalEpochs = epochsP;
|
|
|
|
return true;
|
|
}
|
|
//————————————————————————————————————————————————————————————————————
|
|
|
|
//————————————————————————————————————————————————————————————————————
|
|
//--- Основной цикл алгоритма
|
|
void C_AO_EOm::Moving ()
|
|
{
|
|
currentEpoch++;
|
|
|
|
// Начальная инициализация популяции
|
|
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;
|
|
}
|
|
|
|
//Apply Extremal Optimization---------------------------------------
|
|
static S_AO_Agent aT []; ArrayResize (aT, popSize);
|
|
|
|
for (int i = 0; i < popSize; i++)
|
|
{
|
|
aT [i].Init (coords);
|
|
|
|
for (int c = 0; c < coords; c++)
|
|
{
|
|
double rnd = u.RNDprobab (); rnd = pow (rnd, powCh);
|
|
int ind = (int)u.Scale (rnd, 0.0, 1.0, 0, popSize - 1);
|
|
|
|
// Выбор типа мутации
|
|
double mutType = u.RNDprobab ();
|
|
|
|
if (mutType < mutationRate)
|
|
{
|
|
aT [i].c [c] = u.PowerDistribution (a [ind].c [c], rangeMin [c], rangeMax [c], powMut);
|
|
}
|
|
else
|
|
{
|
|
// Направленное движение к лучшему с шумом
|
|
aT [i].c [c] = a [ind].c [c] + u.RNDprobab () * (cB [c] - a [ind].c [c]);
|
|
}
|
|
|
|
// Проверка границ
|
|
aT [i].c [c] = u.SeInDiSp (aT [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < popSize; i++) ArrayCopy (a [i].c, aT [i].c);
|
|
}
|
|
//————————————————————————————————————————————————————————————————————
|
|
|
|
//————————————————————————————————————————————————————————————————————
|
|
//--- Обновление лучших решений
|
|
void C_AO_EOm::Revision ()
|
|
{
|
|
// Сортировка популяции --------------------------------------------
|
|
static S_AO_Agent aT []; ArrayResize (aT, popSize);
|
|
u.Sorting (a, aT, popSize);
|
|
|
|
// Обновление глобального лучшего решения
|
|
if (a [0].f > fB)
|
|
{
|
|
ArrayCopy (cB, a [0].c, 0, 0, WHOLE_ARRAY);
|
|
fB = a [0].f;
|
|
}
|
|
|
|
fW = a [popSize - 1].f;
|
|
|
|
//------------------------------------------------------------------
|
|
for (int i = 0; i < popRaising; i++)
|
|
{
|
|
a [popSize - 1 - i].f = u.RNDfromCI (fW, fB);
|
|
}
|
|
|
|
u.Sorting (a, aT, popSize);
|
|
}
|
|
//————————————————————————————————————————————————————————————————————
|