diff --git a/MQL5/Include/Math/AOs/PopulationAO/#C_AO_enum.mqh b/MQL5/Include/Math/AOs/PopulationAO/#C_AO_enum.mqh index 978ccb9..6211f52 100644 Binary files a/MQL5/Include/Math/AOs/PopulationAO/#C_AO_enum.mqh and b/MQL5/Include/Math/AOs/PopulationAO/#C_AO_enum.mqh differ diff --git a/MQL5/Include/Math/AOs/PopulationAO/AO_(P_O)ES_Evolution_Strategies.mqh b/MQL5/Include/Math/AOs/PopulationAO/AO_(P_O)ES_Evolution_Strategies.mqh index ddf6483..6321376 100644 Binary files a/MQL5/Include/Math/AOs/PopulationAO/AO_(P_O)ES_Evolution_Strategies.mqh and b/MQL5/Include/Math/AOs/PopulationAO/AO_(P_O)ES_Evolution_Strategies.mqh differ diff --git a/MQL5/Include/Math/AOs/PopulationAO/AO_BSA_BirdSwarmAlgorithm.mqh b/MQL5/Include/Math/AOs/PopulationAO/AO_BSA_BirdSwarmAlgorithm.mqh index aa415ed..ee7a64b 100644 --- a/MQL5/Include/Math/AOs/PopulationAO/AO_BSA_BirdSwarmAlgorithm.mqh +++ b/MQL5/Include/Math/AOs/PopulationAO/AO_BSA_BirdSwarmAlgorithm.mqh @@ -184,14 +184,14 @@ void C_AO_BSA::Revision () for (int i = 0; i < popSize; i++) { - if (a [i].f > fB) ind = i; + if (a [i].f > fB) + { + fB = a [i].f; + ind = i; + } } - if (ind != -1) - { - fB = a [ind].f; - ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY); - } + if (ind != -1) ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY); //---------------------------------------------------------------------------- for (int i = 0; i < popSize; i++) diff --git a/MQL5/Include/Math/AOs/PopulationAO/AO_Boids_BoidsAlgorithm.mqh b/MQL5/Include/Math/AOs/PopulationAO/AO_Boids_BoidsAlgorithm.mqh index 587edac..2e21147 100644 Binary files a/MQL5/Include/Math/AOs/PopulationAO/AO_Boids_BoidsAlgorithm.mqh and b/MQL5/Include/Math/AOs/PopulationAO/AO_Boids_BoidsAlgorithm.mqh differ diff --git a/MQL5/Include/Math/AOs/PopulationAO/AO_SIA_SimulatedIsotropicAnnealing.mqh b/MQL5/Include/Math/AOs/PopulationAO/AO_SIA_SimulatedIsotropicAnnealing.mqh index 72ddaee..dbf4f7b 100644 Binary files a/MQL5/Include/Math/AOs/PopulationAO/AO_SIA_SimulatedIsotropicAnnealing.mqh and b/MQL5/Include/Math/AOs/PopulationAO/AO_SIA_SimulatedIsotropicAnnealing.mqh differ diff --git a/MQL5/Include/Math/AOs/PopulationAO/AO_TSEA_TurtleShellEvolutionAlgorithm.mqh b/MQL5/Include/Math/AOs/PopulationAO/AO_TSEA_TurtleShellEvolutionAlgorithm.mqh new file mode 100644 index 0000000..4935a02 --- /dev/null +++ b/MQL5/Include/Math/AOs/PopulationAO/AO_TSEA_TurtleShellEvolutionAlgorithm.mqh @@ -0,0 +1,671 @@ +//+————————————————————————————————————————————————————————————————————————————+ +//| C_AO_TSEA | +//| Copyright 2007-2024, Andrey Dik | +//| https://www.mql5.com/ru/users/joo | +//—————————————————————————————————————————————————————————————————————————————+ + +//Article: https://www.mql5.com/ru/articles/14789 + +#include "#C_AO.mqh" + +//—————————————————————————————————————————————————————————————————————————————— +struct S_TSEA_Agent +{ + double c []; //coordinates + double f; //fitness + int label; //cluster membership label + int labelClustV; //clusters vertically + //int labelClustH; //clusters horizontally + double minDist; //minimum distance to the nearest centroid + + void Init (int coords) + { + ArrayResize (c, coords); + f = -DBL_MAX; + label = -1; + labelClustV = -1; + minDist = DBL_MAX; + } +}; +//—————————————————————————————————————————————————————————————————————————————— + +//—————————————————————————————————————————————————————————————————————————————— +struct S_TSEA_horizontal +{ + //double cB []; + int indBest; + S_TSEA_Agent agent []; +}; + +struct S_TSEA_vertical +{ + S_TSEA_horizontal cell []; +}; + +//—————————————————————————————————————————————————————————————————————————————— + +//—————————————————————————————————————————————————————————————————————————————— +struct S_T_Cluster +{ + double centroid []; //cluster centroid + double f; //centroid fitness + int count; //number of points in the cluster + int ideasList []; //list of ideas + + void Init (int coords) + { + ArrayResize (centroid, coords); + f = -DBL_MAX; + count = 0; + ArrayResize (ideasList, 0, 100); + } +}; +//—————————————————————————————————————————————————————————————————————————————— + +//—————————————————————————————————————————————————————————————————————————————— +class C_TSEA_clusters +{ + public: //-------------------------------------------------------------------- + + void KMeansInit (S_TSEA_Agent &data [], int dataSizeClust, S_T_Cluster &clust []) + { + for (int i = 0; i < ArraySize (clust); i++) + { + int ind = MathRand () % dataSizeClust; + ArrayCopy (clust [i].centroid, data [ind].c, 0, 0, WHOLE_ARRAY); + } + } + + void KMeansPlusPlusInit (S_TSEA_Agent &data [], int dataSizeClust, S_T_Cluster &clust []) + { + // Choose the first centroid randomly + int ind = MathRand () % dataSizeClust; + ArrayCopy (clust [0].centroid, data [ind].c, 0, 0, WHOLE_ARRAY); + + for (int i = 1; i < ArraySize (clust); i++) + { + double sum = 0; + + // Compute the distance from each data point to the nearest centroid + for (int j = 0; j < dataSizeClust; j++) + { + double minDist = DBL_MAX; + + for (int k = 0; k < i; k++) + { + double dist = VectorDistance (data [j].c, clust [k].centroid); + + if (dist < minDist) + { + minDist = dist; + } + } + + data [j].minDist = minDist; + sum += minDist; + } + + // Choose the next centroid with a probability proportional to the distance + double randomValue = ((double)rand () / 32767) * sum; // Generate a random value in the range [0, sum) + double partialSum = 0; + bool centroidChosen = false; + + for (int j = 0; j < dataSizeClust; j++) + { + partialSum += data [j].minDist; + if (randomValue <= partialSum) + { + ArrayCopy (clust [i].centroid, data [j].c, 0, 0, WHOLE_ARRAY); + centroidChosen = true; + break; + } + } + + + // If no point was assigned to the centroid, reassign it to the farthest point + if (!centroidChosen) + { + double maxDist = -DBL_MAX; + int farthestPointIndex = 0; + + for (int j = 0; j < dataSizeClust; j++) + { + if (data [j].minDist > maxDist) + { + maxDist = data [j].minDist; + farthestPointIndex = j; + } + } + Print ("Центроид ", i, " пустой"); + ArrayCopy (clust [i].centroid, data [farthestPointIndex].c, 0, 0, WHOLE_ARRAY); + } + } + } + + double VectorDistance (double &v1 [], double &v2 []) + { + double distance = 0.0; + for (int i = 0; i < ArraySize (v1); i++) + { + distance += (v1 [i] - v2 [i]) * (v1 [i] - v2 [i]); + } + return MathSqrt (distance); + } + + void KMeans (S_TSEA_Agent &data [], int dataSizeClust, S_T_Cluster &clust []) + { + bool changed = true; + int nClusters = ArraySize (clust); + int cnt = 0; + + while (changed && cnt < 100) + { + cnt++; + changed = false; + + // Назначение точек данных к ближайшему центроиду + for (int d = 0; d < dataSizeClust; d++) + { + int closest_centroid = -1; + double closest_distance = DBL_MAX; + + if (data [d].f != -DBL_MAX) + { + for (int cl = 0; cl < nClusters; cl++) + { + double distance = VectorDistance (data [d].c, clust [cl].centroid); + + if (distance < closest_distance) + { + closest_distance = distance; + closest_centroid = cl; + } + } + + if (data [d].label != closest_centroid) + { + data [d].label = closest_centroid; + changed = true; + } + } + else + { + data [d].label = -1; + } + } + + // Обновление центроидов + double sum_c []; + ArrayResize (sum_c, ArraySize (data [0].c)); + + for (int cl = 0; cl < nClusters; cl++) + { + ArrayInitialize (sum_c, 0.0); + + clust [cl].count = 0; + ArrayResize (clust [cl].ideasList, 0); + + for (int d = 0; d < dataSizeClust; d++) + { + if (data [d].label == cl) + { + for (int k = 0; k < ArraySize (data [d].c); k++) + { + sum_c [k] += data [d].c [k]; + } + + clust [cl].count++; + ArrayResize (clust [cl].ideasList, clust [cl].count); + clust [cl].ideasList [clust [cl].count - 1] = d; + } + } + + if (clust [cl].count > 0) + { + for (int k = 0; k < ArraySize (sum_c); k++) + { + clust [cl].centroid [k] = sum_c [k] / clust [cl].count; + } + } + } + } + } + + + //---------------------------------------------------------------------------- + struct DistanceIndex + { + double distance; + int index; + }; + + void BubbleSort (DistanceIndex &arr [], int start, int end) + { + for (int i = start; i < end; i++) + { + for (int j = start; j < end - i; j++) + { + if (arr [j].distance > arr [j + 1].distance) + { + DistanceIndex temp = arr [j]; + arr [j] = arr [j + 1]; + arr [j + 1] = temp; + } + } + } + } + + int KNN (S_TSEA_Agent &data [], S_TSEA_Agent &point, int k_neighbors, int n_clusters) + { + int n = ArraySize (data); + DistanceIndex distances_indices []; + + // Вычисление расстояний от точки до всех других точек + for (int i = 0; i < n; i++) + { + DistanceIndex dist; + dist.distance = VectorDistance (point.c, data [i].c); + dist.index = i; + ArrayResize (distances_indices, n); + distances_indices [i] = dist; + } + + // Сортировка расстояний + BubbleSort (distances_indices, 0, n - 1); + + // Определение кластера для точки + int votes []; + ArrayResize (votes, n_clusters); + ArrayInitialize (votes, 0); + + for (int j = 0; j < k_neighbors; j++) + { + int label = data [distances_indices [j].index].label; + + if (label != -1 && label < n_clusters) + { + votes [label]++; + } + } + + int max_votes = 0; + int max_votes_cluster = -1; + + for (int j = 0; j < n_clusters; j++) + { + if (votes [j] > max_votes) + { + max_votes = votes [j]; + max_votes_cluster = j; + } + } + + return max_votes_cluster; + } +}; +//—————————————————————————————————————————————————————————————————————————————— + +//—————————————————————————————————————————————————————————————————————————————— +class C_AO_TSEA : public C_AO +{ + public: //-------------------------------------------------------------------- + ~C_AO_TSEA () { } + C_AO_TSEA () + { + ao_name = "TSEA"; + ao_desc = "Turtle Shell Evolution Algorithm"; + ao_link = "https://www.mql5.com/ru/articles/14789"; + + popSize = 100; //population size + + vClusters = 3; //number of vertical clusters + hClusters = 10; //number of horizontal clusters + neighbNumb = 5; //number of nearest neighbors + maxAgentsInCell = 3; //max agents in cell + + ArrayResize (params, 5); + + params [0].name = "popSize"; params [0].val = popSize; + + params [1].name = "vClusters"; params [1].val = vClusters; + params [2].name = "hClusters"; params [2].val = hClusters; + params [3].name = "neighbNumb"; params [3].val = neighbNumb; + params [4].name = "maxAgentsInCell"; params [4].val = maxAgentsInCell; + } + + void SetParams () + { + popSize = (int)params [0].val; + + vClusters = (int)params [1].val; + hClusters = (int)params [2].val; + neighbNumb = (int)params [3].val; + maxAgentsInCell = (int)params [4].val; + } + + bool Init (const double &rangeMinP [], //minimum search range + const double &rangeMaxP [], //maximum search range + const double &rangeStepP [], //step search + const int epochsP = 0); //number of epochs + + void Moving (); + void Revision (); + void Injection (const int popPos, const int coordPos, const double value); + + //---------------------------------------------------------------------------- + int vClusters; //number of vertical clusters + int hClusters; //number of horizontal clusters + int neighbNumb; //number of nearest neighbors + int maxAgentsInCell; + + S_TSEA_Agent agent []; + S_TSEA_vertical cell []; + + S_T_Cluster clusters []; + C_TSEA_clusters km; + + private: //------------------------------------------------------------------- + double minFval; + double stepF; + + int epochs; + int epochsNow; +}; +//—————————————————————————————————————————————————————————————————————————————— + +//—————————————————————————————————————————————————————————————————————————————— +bool C_AO_TSEA::Init (const double &rangeMinP [], //minimum search range + const double &rangeMaxP [], //maximum search range + const double &rangeStepP [], //step search + const int epochsP = 0) //number of epochs +{ + if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; + + //---------------------------------------------------------------------------- + ArrayResize (agent, popSize); + for (int i = 0; i < popSize; i++) agent [i].Init (coords); + + ArrayResize (clusters, hClusters); + for (int i = 0; i < hClusters; i++) clusters [i].Init (coords); + + ArrayResize (cell, vClusters); + + for (int i = 0; i < vClusters; i++) + { + ArrayResize (cell [i].cell, hClusters); + + for (int c = 0; c < hClusters; c++) ArrayResize (cell [i].cell [c].agent, 0, maxAgentsInCell); + } + + minFval = DBL_MAX; + stepF = 0.0; + epochs = epochsP; + epochsNow = 0; + + return true; +} +//—————————————————————————————————————————————————————————————————————————————— + +//—————————————————————————————————————————————————————————————————————————————— +void C_AO_TSEA::Moving () +{ + epochsNow++; + + //---------------------------------------------------------------------------- + //1. Сгенерировать случайные особи в популяцию + 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]); + + agent [i].c [c] = a [i].c [c]; + } + } + + return; + } + + //---------------------------------------------------------------------------- + //---------------------------------------------------------------------------- + int vPos = 0; + int hPos = 0; + int pos = 0; + int size = 0; + double val = 0.0; + double rnd = 0.0; + double min = 0.0; + double max = 0.0; + + for (int v = 0; v < vClusters; v++) + { + for (int h = 0; h < hClusters; h++) + { + size = ArraySize (cell [v].cell [h].agent); + + if (size > 0) + { + max = -DBL_MAX; + pos = -1; + + for (int c = 0; c < size; c++) + { + if (cell [v].cell [h].agent [c].f > max) + { + max = cell [v].cell [h].agent [c].f; + pos = c; + cell [v].cell [h].indBest = c; + } + } + } + } + } + + for (int i = 0; i < popSize; i++) + { + while (true) + { + rnd = u.RNDprobab (); + rnd = (-rnd * rnd + 1.0) * vClusters; + + vPos = (int)rnd; + if (vPos > vClusters - 1) vPos = vClusters - 1; + + hPos = u.RNDminusOne (hClusters); + + size = ArraySize (cell [vPos].cell [hPos].agent); + + if (size > 0) break; + } + + pos = u.RNDminusOne (size); + + if (u.RNDprobab () < 0.5) pos = cell [vPos].cell [hPos].indBest; + + for (int c = 0; c < coords; c++) + { + if (u.RNDprobab () < 0.6) val = cell [vPos].cell [hPos].agent [pos].c [c]; + else val = cB [c]; + + double dist = (rangeMax [c] - rangeMin [c]) * 0.1; + min = val - dist; if (min < rangeMin [c]) min = rangeMin [c]; + max = val + dist; if (max > rangeMax [c]) max = rangeMax [c]; + + val = u.PowerDistribution (val, min, max, 30); + + a [i].c [c] = u.SeInDiSp (val, rangeMin [c], rangeMax [c], rangeStep [c]); + + agent [i].c [c] = a [i].c [c]; + } + } +} +//—————————————————————————————————————————————————————————————————————————————— + +//—————————————————————————————————————————————————————————————————————————————— +void C_AO_TSEA::Revision () +{ + //получить приспособленность-------------------------------------------------- + int pos = -1; + + for (int i = 0; i < popSize; i++) + { + agent [i].f = a [i].f; + + if (a [i].f > fB) + { + fB = a [i].f; + pos = i; + } + + if (a [i].f < minFval) minFval = a [i].f; + } + + if (pos != -1) ArrayCopy (cB, a [pos].c, 0, 0, WHOLE_ARRAY); + + stepF = (fB - minFval) / vClusters; + + //3. Разметка по вертикали дочерней популяции--------------------------------- + for (int i = 0; i < popSize; i++) + { + if (agent [i].f == fB) agent [i].labelClustV = vClusters - 1; + else + { + agent [i].labelClustV = int((agent [i].f - minFval) / stepF); + if (agent [i].labelClustV > vClusters - 1) agent [i].labelClustV = vClusters - 1; + } + } + + //---------------------------------------------------------------------------- + if (!revision) + { + km.KMeansPlusPlusInit (agent, popSize, clusters); + km.KMeans (agent, popSize, clusters); + + revision = true; + } + //---------------------------------------------------------------------------- + else + { + static S_TSEA_Agent data []; + ArrayResize (data, 0, 1000); + int size = 0; + + for (int v = 0; v < vClusters; v++) + { + for (int h = 0; h < hClusters; h++) + { + for (int c = 0; c < ArraySize (cell [v].cell [h].agent); c++) + { + size++; + ArrayResize (data, size); + + data [size - 1] = cell [v].cell [h].agent [c]; + } + } + } + + for (int i = 0; i < popSize; i++) + { + agent [i].label = km.KNN (data, agent [i], neighbNumb, hClusters); + } + /* + if (epochsNow % 5 == 0) + { + km.KMeansPlusPlusInit (data, ArraySize (data), clusters); + km.KMeans (data, ArraySize (data), clusters); + + for (int v = 0; v < vClusters; v++) + { + for (int h = 0; h < hClusters; h++) + { + ArrayResize (cell [v].cell [h].agent, 0); + } + } + + for (int i = 0; i < ArraySize (data); i++) + { + if (data [i].f == fB) data [i].labelClustV = vClusters - 1; + else + { + data [i].labelClustV = int((data [i].f - minFval) / stepF); + if (data [i].labelClustV > vClusters - 1) data [i].labelClustV = vClusters - 1; + } + + int v = data [i].labelClustV; + int h = data [i].label; + + int size = ArraySize (cell [v].cell [h].agent) + 1; + ArrayResize (cell [v].cell [h].agent, size); + + cell [v].cell [h].agent [size - 1] = data [i]; + } + } + */ + } + + //5, 10. Поместить популяцию в панцирь---------------------------------------- + for (int i = 0; i < popSize; i++) + { + int v = agent [i].labelClustV; + int h = agent [i].label; + + int size = ArraySize (cell [v].cell [h].agent); + int pos = 0; + int posMin = 0; + int posMax = 0; + + if (size >= maxAgentsInCell) + { + double minF = DBL_MAX; + double maxF = -DBL_MAX; + + for (int c = 0; c < maxAgentsInCell; c++) + { + if (agent [i].f < minF) + { + minF = agent [i].f; + posMin = c; + } + if (agent [i].f > maxF) + { + maxF = agent [i].f; + posMax = c; + } + } + + if (v == 0) pos = posMax; + else pos = posMin; + } + else + { + ArrayResize (cell [v].cell [h].agent, size + 1); + pos = size; + } + + cell [v].cell [h].agent [pos] = agent [i]; + } +} +//—————————————————————————————————————————————————————————————————————————————— + +//—————————————————————————————————————————————————————————————————————————————— +void C_AO_TSEA::Injection (const int popPos, const int coordPos, const double value) +{ + if (popPos < 0 || popPos >= popSize) return; + if (coordPos < 0 || coordPos >= coords) return; + + if (value < rangeMin [coordPos]) + { + a [popPos].c [coordPos] = rangeMin [coordPos]; + } + + if (value > rangeMax [coordPos]) + { + a [popPos].c [coordPos] = rangeMax [coordPos]; + } + + a [popPos].c [coordPos] = u.SeInDiSp (value, rangeMin [coordPos], rangeMax [coordPos], rangeStep [coordPos]); +} +//—————————————————————————————————————————————————————————————————————————————— diff --git a/MQL5/Include/Math/AOs/PopulationAO/AO_WOA_WhaleOptimizationAlgorithm.mqh b/MQL5/Include/Math/AOs/PopulationAO/AO_WOA_WhaleOptimizationAlgorithm.mqh index 9dee741..0762b3d 100644 Binary files a/MQL5/Include/Math/AOs/PopulationAO/AO_WOA_WhaleOptimizationAlgorithm.mqh and b/MQL5/Include/Math/AOs/PopulationAO/AO_WOA_WhaleOptimizationAlgorithm.mqh differ diff --git a/README.md b/README.md index bb614c9..f67b32e 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,8 @@ A list of implemented (+) optimization algorithms, (-) not included in the repos -(PO)ES ((PO) evolution strategies) +BSO (brain storm optimization) +WOAm (wale optimization algorithm M) --ACOm (ant colony optimization M) +-ACOm (ant colony optimization M) ++TSEA (turtle shell evolution algorithm) -BFO-GA (bacterial foraging optimization - ga) -MEC (mind evolutionary computation) -IWO (invasive weed optimization)