Add COA(Coyote)

This commit is contained in:
JQSakaJoo
2026-06-14 22:52:07 +04:00
parent 1ffbc1bc5c
commit b2d0c0066b
4 changed files with 382 additions and 0 deletions
Binary file not shown.
@@ -0,0 +1,380 @@
//+——————————————————————————————————————————————————————————————————+
//| C_AO_COA_Coyote|
//| Copyright 2007-2025, Andrey Dik |
//| https://www.mql5.com/ru/users/joo |
//+——————————————————————————————————————————————————————————————————+
#include "#C_AO.mqh"
//+------------------------------------------------------------------+
//| Coyote Optimization Algorithm (COA). |
//| Pierezan J., Coelho L.S. "Coyote Optimization Algorithm: A new |
//| metaheuristic for global optimization problems", IEEE CEC 2018. |
//| |
//| Структура: популяция разбита на nPacks стай по nCoy койотов. |
//| За эпоху в каждой стае: |
//| 1. определяется альфа (лучший по фитнесу, Eq. 5); |
//| 2. считается «социальная тенденция» — покоординатная медиана |
//| стаи (Eq. 6); |
//| 3. каждый койот делает социальный ход (Eq. 12): |
//| new = x + r1*(alpha - x_rc1) + r2*(tend - x_rc2), |
//| r1, r2 — СКАЛЯРЫ на агента (как в оригинале); |
//| 4. рождается щенок (Eq. 7, Alg. 1): бинарный кроссовер двух |
//| случайных родителей + равномерный шум по части координат; |
//| 5. принятие — только при строгом улучшении (Eq. 14). |
//| Редкий обмен койотами между стаями (Eq. 4, p_leave) и счётчик |
//| возраста (щенок обнуляет возраст слота). |
//| |
//| АДАПТАЦИЯ К БАТЧЕВОЙ СХЕМЕ СТЕНДА (Moving/Revision): |
//| 1. Оригинал последовательный: каждый новый койот оценивается |
//| сразу, плюс отдельная оценка щенка — nCoy+1 вызовов FF на |
//| стаю за «год». Здесь щенок занимает слот ХУДШЕГО койота стаи |
//| (при равенстве фитнеса — старшего по возрасту); социальный |
//| ход этого койота в текущую эпоху пропускается. Итог: ровно |
//| popSize вызовов FF за эпоху, бюджет расходуется честно. |
//| 2. Приживание щенка в оригинале: замена старейшего среди тех, |
//| кто хуже щенка. Здесь слот выбран заранее (худший/старший), |
//| щенок принимается, если строго лучше него — поведенчески |
//| эквивалентное приближение в рамках greedy-acceptance. |
//| 3. Все ходы эпохи считаются от снапшота популяции (в оригинале |
//| обновления внутри стаи частично последовательные). |
//| |
//| ВНИМАНИЕ (для off-diagonal проверки): в социальном ходе r1 и r2 |
//| — скаляры, домножающие целые векторы разностей. Это потенциаль- |
//| ный кандидат на «диагональный чит» на бенче из копий функции — |
//| проверить составным тестом с |x_norm - y_norm| > 0.3. |
//+------------------------------------------------------------------+
class C_AO_COA_Coyote : public C_AO
{
public:
~C_AO_COA_Coyote() {}
C_AO_COA_Coyote()
{
ao_name = "COA(Coyote)";
ao_desc = "Coyote Optimization Algorithm";
ao_link = "https://www.mql5.com/ru/articles/23053";
popSize = 20;
nCoy = 5;
ArrayResize(params, 2);
params [0].name = "popSize";
params [0].val = popSize;
params [1].name = "nCoy";
params [1].val = nCoy;
nPacks = popSize / nCoy;
}
void SetParams()
{
popSize = (int)params [0].val;
nCoy = (int)params [1].val;
nPacks = popSize / nCoy;
}
bool Init(const double &rangeMinP [],
const double &rangeMaxP [],
const double &rangeStepP [],
const int epochsP = 0);
void Moving();
void Revision();
//--- видимые параметры
int nCoy; // койотов в стае (оригинал: 5)
private:
int nPacks; // число стай = popSize / nCoy
double pLeave; // вероятность обмена между стаями: 0.005*nCoy^2
double probPup; // (1 - Ps)/2, Ps = 1/coords
int packs []; // [nPacks * nCoy] — индексы агентов по стаям
int ages []; // [popSize] — возраст койота
bool isPup []; // [popSize] — слот щенка в текущую эпоху
//--- snapshot для greedy acceptance
double snap_c []; // [popSize * coords]
double snap_f []; // [popSize]
//--- рабочие буферы
double medBuf []; // [nCoy] — для медианы
double tend []; // [coords] — социальная тенденция стаи
int pdr []; // [coords] — перестановка измерений (щенок)
int mask []; // [coords] — 0: шум, 1: родитель 1, 2: родитель 2
};
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Init |
//+------------------------------------------------------------------+
bool C_AO_COA_Coyote::Init(const double &rangeMinP [],
const double &rangeMaxP [],
const double &rangeStepP [],
const int epochsP = 0)
{
if(!StandardInit(rangeMinP, rangeMaxP, rangeStepP))
return false;
//--- начальная случайная популяция в cP[] (Eq. 2)
for(int i = 0; i < popSize; i++)
for(int c = 0; c < coords; c++)
a [i].cP [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]);
//--- параметры, зависящие от nCoy и coords
pLeave = 0.005 * (double)nCoy * (double)nCoy; // Eq. 4
probPup = (coords > 0) ? (1.0 - 1.0 / (double)coords) * 0.5 : 0.0; // (1-Ps)/2
//--- буферы
ArrayResize(snap_c, popSize * coords);
ArrayResize(snap_f, popSize);
ArrayResize(packs, nPacks * nCoy);
ArrayResize(ages, popSize);
ArrayResize(isPup, popSize);
ArrayResize(medBuf, nCoy);
ArrayResize(tend, coords);
ArrayResize(pdr, coords);
ArrayResize(mask, coords);
ArrayInitialize(ages, 0);
ArrayInitialize(isPup, false);
//--- случайное распределение койотов по стаям (FisherYates)
for(int i = 0; i < popSize; i++)
packs [i] = i;
for(int j = popSize - 1; j > 0; j--)
{
int r = u.RNDminusOne(j + 1);
int tmp = packs [j];
packs [j] = packs [r];
packs [r] = tmp;
}
return true;
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Moving |
//| |
//| Структура эпохи: |
//| 1. snapshot популяции; |
//| 2. редкий обмен койотами между стаями (Eq. 4); |
//| 3. для каждой стаи: альфа, тенденция-медиана, слот щенка; |
//| 4. социальные ходы (Eq. 12) для всех, кроме слота щенка; |
//| 5. в слот щенка — кроссовер двух родителей + шум (Alg. 1). |
//+------------------------------------------------------------------+
void C_AO_COA_Coyote::Moving()
{
//--- первый прогон: cP -> c, чтобы внешний цикл оценил FF
if(!revision)
{
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]);
return;
}
//--- snapshot текущей популяции
for(int i = 0; i < popSize; i++)
{
for(int c = 0; c < coords; c++)
snap_c [i * coords + c] = a [i].c [c];
snap_f [i] = a [i].f;
}
//--- обмен койотами между стаями (Eq. 4)
if(nPacks > 1 && u.RNDprobab() < pLeave)
{
int p1 = u.RNDminusOne(nPacks);
int p2 = p1;
while(p2 == p1)
p2 = u.RNDminusOne(nPacks);
int c1 = u.RNDminusOne(nCoy);
int c2 = u.RNDminusOne(nCoy);
int tmp = packs [p1 * nCoy + c1];
packs [p1 * nCoy + c1] = packs [p2 * nCoy + c2];
packs [p2 * nCoy + c2] = tmp;
}
ArrayInitialize(isPup, false);
//--- операции внутри каждой стаи
for(int p = 0; p < nPacks; p++)
{
int base = p * nCoy;
//--- альфа стаи (Eq. 5): максимум фитнеса (стенд максимизирует)
int aIdx = packs [base];
for(int k = 1; k < nCoy; k++)
{
int idx = packs [base + k];
if(snap_f [idx] > snap_f [aIdx])
aIdx = idx;
}
//--- социальная тенденция (Eq. 6): покоординатная медиана стаи
for(int c = 0; c < coords; c++)
{
for(int k = 0; k < nCoy; k++)
medBuf [k] = snap_c [packs [base + k] * coords + c];
ArraySort(medBuf);
tend [c] = (nCoy % 2 == 1) ? medBuf [nCoy / 2]
: 0.5 * (medBuf [nCoy / 2 - 1] + medBuf [nCoy / 2]);
}
//--- слот щенка: худший фитнес, при равенстве — старший возраст
int wK = 0;
for(int k = 1; k < nCoy; k++)
{
int idx = packs [base + k];
int wIdx = packs [base + wK];
if(snap_f [idx] < snap_f [wIdx] ||
(snap_f [idx] == snap_f [wIdx] && ages [idx] > ages [wIdx]))
wK = k;
}
int pupIdx = packs [base + wK];
isPup [pupIdx] = true;
//--- движение койотов стаи
for(int k = 0; k < nCoy; k++)
{
int i = packs [base + k];
if(i == pupIdx)
{
//--- РОЖДЕНИЕ ЩЕНКА (Eq. 7, Alg. 1) -------------------------
// два случайных различных родителя из стаи
int k1 = u.RNDminusOne(nCoy);
int k2 = k1;
while(k2 == k1)
k2 = u.RNDminusOne(nCoy);
int par1 = packs [base + k1];
int par2 = packs [base + k2];
//--- перестановка измерений (FisherYates)
for(int c = 0; c < coords; c++)
pdr [c] = c;
for(int j = coords - 1; j > 0; j--)
{
int r = u.RNDminusOne(j + 1);
int tmp = pdr [j];
pdr [j] = pdr [r];
pdr [r] = tmp;
}
//--- маска: гарантированно по одной координате от каждого
// родителя, остальные — родитель 1 / родитель 2 / шум
ArrayInitialize(mask, 0);
mask [pdr [0]] = 1;
if(coords > 1)
mask [pdr [1]] = 2;
for(int j = 2; j < coords; j++)
{
double r = u.RNDprobab();
if(r < probPup)
mask [pdr [j]] = 1;
else
if(r > 1.0 - probPup)
mask [pdr [j]] = 2;
// иначе 0 — равномерный шум по координате
}
for(int c = 0; c < coords; c++)
{
double v;
if(mask [c] == 1)
v = snap_c [par1 * coords + c];
else
if(mask [c] == 2)
v = snap_c [par2 * coords + c];
else
v = u.RNDfromCI(rangeMin [c], rangeMax [c]);
a [i].c [c] = u.SeInDiSp(v, rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
else
{
//--- СОЦИАЛЬНЫЙ ХОД (Eq. 12) --------------------------------
// rc1, rc2 — различные, не совпадающие с k
int k1 = k;
while(k1 == k)
k1 = u.RNDminusOne(nCoy);
int k2 = k;
while(k2 == k || k2 == k1)
k2 = u.RNDminusOne(nCoy);
int rc1 = packs [base + k1];
int rc2 = packs [base + k2];
//--- r1, r2 — скаляры на агента, как в оригинале
double r1 = u.RNDprobab();
double r2 = u.RNDprobab();
for(int c = 0; c < coords; c++)
{
double v = snap_c [i * coords + c] +
r1 * (snap_c [aIdx * coords + c] - snap_c [rc1 * coords + c]) +
r2 * (tend [c] - snap_c [rc2 * coords + c]);
a [i].c [c] = u.SeInDiSp(v, rangeMin [c], rangeMax [c], rangeStep [c]);
}
}
} // k
} // p
}
//+------------------------------------------------------------------+
//+------------------------------------------------------------------+
//| Revision |
//| |
//| Стандартное обновление fB/cB + адаптация (Eq. 14): кандидат |
//| принимается только при СТРОГОМ улучшении, иначе откат к |
//| снапшоту (касается и социальных ходов, и щенка). Возраст всех |
//| койотов растёт на 1 за эпоху; прижившийся щенок обнуляет |
//| возраст слота. |
//+------------------------------------------------------------------+
void C_AO_COA_Coyote::Revision()
{
//--- обновление личных и глобального лучших
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);
}
}
if(!revision)
{
revision = true;
return;
}
//--- адаптация (Eq. 14): принятие только при строгом улучшении
for(int i = 0; i < popSize; i++)
{
bool accepted = (a [i].f > snap_f [i]);
if(!accepted)
{
for(int c = 0; c < coords; c++)
a [i].c [c] = snap_c [i * coords + c];
a [i].f = snap_f [i];
}
//--- возраст
ages [i]++;
if(isPup [i] && accepted)
ages [i] = 0;
}
}
//+------------------------------------------------------------------+
@@ -57,6 +57,7 @@
<tr><td>CTA</td><td><a href="https://www.mql5.com/ru/articles/14841" target="_blank">comet tail algorithm (joo)</a></td></tr>
<tr><td>TETA</td><td><a href="https://www.mql5.com/ru/articles/16963" target="_blank">time evolution travel algorithm (joo)</a></td></tr>
<tr><td>SDSm</td><td><a href="https://www.mql5.com/ru/articles/13540" target="_blank">stochastic diffusion search M</a></td></tr>
<tr><td>COA(Coyote)</td><td><a href="https://www.mql5.com/ru/articles/23053" target="_blank">coyote optimization algorithm</a></td></tr>
<tr><td>ECBO</td><td><a href="https://www.mql5.com/ru/articles/21147" target="_blank">enhanced colliding bodies optimization</a></td></tr>
<tr><td>BOAm</td><td><a href="https://www.mql5.com/ru/articles/17325" target="_blank">billiards optimization algorithm M</a></td></tr>
<tr><td>AAm</td><td><a href="https://www.mql5.com/ru/articles/15782" target="_blank">archery algorithm M</a></td></tr>
+1
View File
@@ -10,6 +10,7 @@ P_O_ES ((P+O) evolution strategies)
CTA (Comet Tail Algorithm, joo)
TETA (time evolution travel algorithm, joo)
SDSm (stochastic diffusion search M)
COA(Coyote)(coyote optimization algorithm)
ECBO (enhanced colliding bodies optimization)
BOAm (billiards optimization algorithm M)
AAm (archery algorithm M)