preview
Популяционные алгоритмы оптимизации: Тасующий алгоритм прыгающих лягушек (Shuffled Frog-Leaping, SFL)

Популяционные алгоритмы оптимизации: Тасующий алгоритм прыгающих лягушек (Shuffled Frog-Leaping, SFL)

MetaTrader 5Примеры | 19 сентября 2023, 09:40
214 11
Andrey Dik
Andrey Dik

Содержание:

1. Введение
2. Описание алгоритма
3. Результаты тестов


1. Введение

Тасующий алгоритм прыгающих лягушек (Shuffled Frog Leaping Algorithm, SFL) был предложен Юсуфом (М. Eusuff) и другими авторами в 2003 году. Этот алгоритм объединяет принципы меметического алгоритма и алгоритма роя частиц, и его разработка была вдохновлена поведением группы лягушек в процессе поиска пищи.

Изначально алгоритм SFL был разработан как метаэвристический метод для решения задач комбинаторной оптимизации. Он основан на использовании математических функций и информированного эвристического поиска.

Алгоритм SFL состоит из нескольких взаимодействующих виртуальных популяций лягушек, называемых мемплексами. Виртуальные лягушки выполняют роль хозяев или носителей мемов, где мем представляет собой единицу культурной эволюции. В каждом мемплексе происходит независимый локальный поиск с использованием метода, аналогичного оптимизации роя частиц, но с упором на локальный поиск.

Для обеспечения глобальных исследований виртуальные лягушки периодически перетасовываются и реорганизуются в новые мемплексы с помощью метода, аналогичного алгоритму перетасованной сложной эволюции. Кроме того, в популяции генерируются и заменяются случайные виртуальные лягушки, чтобы обеспечить возможность случайной генерации улучшенной информации.

Тасующий алгоритм прыгающих лягушек является эффективным методом для решения сложных задач оптимизации и позволяет достичь оптимальных решений в различных областях применения. В данной статье мы рассмотрим основные принципы и применение этого алгоритма, а также его преимущества и ограничения.


2. Описание алгоритма

Меметика (memetics) представляет собой подход к моделям эволюционной передачи информации, основанный на концепции мемов. Мемы, являющиеся аналогами генов, служат единицами культурной информации, распространяемой между людьми через имитацию, обучение и другие средства. Понятие мема было введено и основы меметики были разработаны Докинзом (C.R. Dawkins) в 1976 году. Мемы могут передаваться вертикально - от предшественников, родителей, воспитателей, книг, культурных артефактов и т.д. Также возможна горизонтальная передача мемов от человека к человеку и от культуры к культуре. Несмотря на то, что мемы представляют собой чистую информацию, их функционирование приводит к существенным изменениям поведения человека.

Меметические алгоритмы (Memetic algorithms, М-алгоритмы) определяются как гибридные популяционные метаэвристические алгоритмы поисковой оптимизации, основанные на концепции мема и неодарвиновском принципе эволюции. В контексте М-алгоритмов мем является реализацией алгоритма локальной оптимизации, уточняющего текущие решения на каждой итерации или через некоторое число итераций. М-алгоритмы можно рассматривать как гибридизацию одного из популяционных алгоритмов глобального поиска и одного или нескольких классических или популяционных алгоритмов локальной оптимизации. Первоначально М-алгоритмы были предложены как один из вариантов повышения эффективности генетических алгоритмов.

Эффективность М-алгоритмов в значительной степени зависит от значений их свободных параметров. В ряде исследований показано, что очень большое влияние на эффективность этих алгоритмов оказывает выбор используемых мемов. Поэтому данная проблема занимает одно из центральных мест в работах, посвященных М-алгоритмам.

Мемы представляют собой одну из революционных идей, подразумевающую схожесть эволюции генов и эволюции человеческой культуры. Докинз назвал единицу культурного обмена, аналог гена в культуре, мемом. Мемом можно назвать жест, слово или идею; любая единица культурной информации, которая может быть передана от человека к человеку посредством имитации обучения - это мем.

Меметические алгоритмы, в отличие от генетических, имитируют процесс культурной эволюции. Разработанный Москато подход использует аналогию с эволюцией мемов. Меметический алгоритм состоит из следующих этапов: локальный поиск, кооперация, соревнование, и критерий окончания поиска. Меметическим называют широкий класс алгоритмов, объединенных общей идеей: включением в генетический алгоритм индивидуального обучения особей и использование информации о структуре пространства возможных решений. Проблема баланса между популяционным и локальным поиском можно рассматривать как общую проблему соблюдения баланса между экстенсивными и интенсивными исследованиями пространства поиска.

Меметический алгоритм в общих чертах включает в себя следующие компоненты:

1. Локальный поиск. Для его осуществления можно использовать алгоритм имитации отжига и алгоритмы подъема.
2. Кооперация. Обмен информацией между особями может осуществляться через процедуру, аналогичную применению оператора двухточечного скрещивания в генетических алгоритмах.
3. Соревнование. Процедура селекции аналогична таковой в генетических алгоритмах и обычно заключается в отборе наиболее приспособленных особей в популяции и исключении из нее менее приспособленных.
4. Критерий окончания поиска. В меметических алгоритмах он может включать оценку разнообразия особей, помимо подсчета числа итераций и оценки улучшения результата.

Меметические алгоритмы - это широкий класс алгоритмов, объединенных общей идеей индивидуального обучения особей и использования информации о структуре пространства возможных решений.

Мемы, подобно генам, являются репликаторами, то есть объектами, способными к самовоспроизведению. Для мемов выживание и воспроизводство зависят от наличия носителя, который распространяет мем. Мемы могут видоизменяться, формируя новые мемы, и участвуют в борьбе за ресурсы - умы людей.

Мемы часто объединяются в комплексы, или мемплексы, для усиления в борьбе за носителей. Мемплексы аналогичны симбиотическим совокупностям генов, составляющих генетические коды биологических организмов. Примером мемплекса может быть религия. Мемплексы оказывают глубокое влияние на формирование индивидуального и общественного поведения.

Переходим непосредственно к тасующему алгоритму прыгающих лягушек. Основная идея алгоритма заключается в разбиении всей популяции лягушек с глобальным лидером G на мемы - группы, в каждой из которых есть лидер L (рисунок 1).

frogs1

Рисунок 1. Популяция лягушек с глобальным лидером (G), разбитая на мемы со своим локальным лидером (L).

В рамках каждого мема лягушки стремятся двигаться в направлении локального лидера. Лидерство обновляется, если позиция одной из лягушек улучшается. Мемы не разделяются в пространстве поиска и могут иметь пересечения. Таким образом, лягушка, находящаяся на территории одного мема, вполне может принадлежать к другому. Это создает динамичную среду, где лягушки могут переходить пространственно из одного мема в другой, в зависимости от изменения их позиций в пространстве поиска.

Рассматриваем задачу глобальной условной оптимизации, в которой фитнес-функция подлежит максимизации. Алгоритм SFL включает в себя следующие основные шаги.

1.0 Инициализация алгоритма
1.1 Создание начальной популяции лягушек с случайными координатами.
1.2 Определение приспособленности каждой лягушки.
1.3 Создание мемплексов (подпопуляций), распределение лягушек по мемплексам.

2.0 Цикл поиска оптимального решения
2.1 Для каждого мемплекса:
    2.1.1 Определить лучшую лягушку.
    2.1.2 Переместить остальные лягушки в сторону лучшей лягушки в мемплексе.
2.2 Если перемещение лягушки не улучшило ее приспособленность:
    2.2.1 Переместить ее в сторону глобально лучшей лягушки.
    2.2.2 Если и это не улучшило приспособленность, переместить лягушку в случайное место на поле.

3.0 Измерение приспособленности лягушек
3.1 Для каждой лягушки в популяции измерить ее приспособленность.
3.2 Обновить информацию о лучшей лягушке в каждом мемплексе и в популяции в целом.

4.0 Определение глобально лучшей лягушки и обновление мемплексов
4.1 Определить глобально лучшую лягушку по всей популяции.
4.2 Если это последний цикл:
    4.2.1 Сбросить счетчик циклов мемплекса.
    4.2.2 Сгенерировать случайные индексы для лягушек.
    4.2.3 Скопировать лягушек в мемплексы по новым индексам.
    4.2.4 Определить в каждом мемплексе лучшую лягушку.
    4.2.5 Сбросить приспособленность лягушек и шаг.
4.3 Если это не последний цикл:
    4.3.1 Скопировать приспособленность и координаты лягушек из популяции в соответствующие лягушки мемплексов.
    4.3.2 Определить в каждом мемплексе лучшую лягушку.
    4.3.3 Определить направление следующего прыжка для каждой лягушки в зависимости от ее приспособленности.


Таким образом, основой алгоритма SFL является комбинирование локального поиска в пределах каждого из мемеплексов и глобального поиска путем обмена информацией о положениях лучших агентов мемеплексов.
Данный алгоритм представляет собой модификацию алгоритма SFL, в котором на этапе локального поиска агент движется не точно в направлении лучшего агента соответствующего мемеплекса, а в некотором случайным образом возмущенном направлении. Известны как последовательные, так и параллельные гибридизации алгоритма со многими популяционными алгоритмами, например с алгоритмом искусственной иммунной системы.

frog2

Рисунок 2. Прыжки лягушек. Если предыдущее перемещение оказалось неудачным, то следующий прыжок будет произведен из того же самого места.

Логической единицей алгоритма оптимизации SFL является лягушка, она может быть описана структурой S_Frog, которая представляет собой агента в пространстве поиска.

Структура S_Frog содержит следующие поля:

  • c - массив координат текущей позиции лягушки.
  • cPrev - массив координат предыдущей позиции лягушки.
  • f - значение функции приспособленности для текущей позиции лягушки.
  • fPrev - значение функции приспособленности для предыдущей позиции лягушки.
  • frogStep - номер шага, на котором находится лягушка.
//——————————————————————————————————————————————————————————————————————————————
struct S_Frog
{
  void Init (int coords)
  {
    ArrayResize (c,     coords);
    ArrayResize (cPrev, coords);
    f        = -DBL_MAX;
    fPrev    = -DBL_MAX;
    frogStep = 0;
  }
  double c     []; //coordinates
  double cPrev []; //previous coordinates
  double f;        //fitness
  double fPrev;    //previous fitness
  int    frogStep; //frog step
};
//——————————————————————————————————————————————————————————————————————————————

Структура S_Memeplex описывает мемплекс в алгоритме. Она содержит следующие поля:

  • frogs - массив лягушек, составляющих мемплекс.
  • fBest - лучшее значение функции приспособленности среди всех лягушек в мемплексе.
  • cBest - массив координат, соответствующих лучшему значению функции приспособленности в мемплексе.
//——————————————————————————————————————————————————————————————————————————————
struct S_Memeplex
{
  S_Frog frogs [];
  double fBest;     //best fitness
  double cBest [];  //best coordinates
};
//——————————————————————————————————————————————————————————————————————————————

Класс C_AO_SFL предоставляет методы для инициализации алгоритма, перемещения лягушек и ревизии популяции. Он также содержит вспомогательные методы для преобразования значений и генерации случайных чисел.

Напишем класс алгоритма SFL, включающий в себя поля:

  • cB - массив, хранящий лучшие координаты (best coordinates);
  • fB - переменная, хранящая значение целевой функции (fitness function) для лучших координат;
  • frogs -  массив, хранящий всех лягушек в популяции;
  • mems -  массив, хранящий мемплексы (группы лягушек);
  • rangeMax - массив, хранящий максимальные значения для каждой координаты поиска;
  • rangeMin - массив, хранящий минимальные значения для каждой координаты поиска;
  • rangeStep - массив, хранящий шаги поиска для каждой координаты.


Открытые методы класса:

  • Init - метод инициализации параметров алгоритма, принимает следующие параметры:
  • coordinatesNumberP -  количество координат поиска;
  • populationSizeP -  размер популяции (количество лягушек);
  • numbMemsP -  количество мемплексов (групп лягушек);
  • numbCyclesP - количество циклов в мемплексе;
  • frogStepsToLocalMaxP - количество шагов лягушки до локального максимума;
  • movConstantP - константа перемещения (шаг поиска) лягушек.

Moving - метод, реализующий перемещение лягушек в пространстве поиска.
Revision - метод, реализующий ревизию популяции лягушек и обновление лучших координат.
SeInDiSp - вспомогательный метод, реализующий преобразование значения из одного диапазона в другой с заданным шагом.
RNDfromCI - вспомогательный метод, генерирующий случайное число в заданном интервале.

Описание приватных полей класса:

  • coordinatesNumber - количество координат поиска;
  • frogsNumber - количество лягушек в популяции;
  • numbMems - количество мемплексов (групп лягушек);
  • numbCycles - количество циклов в мемплексе;
  • frogStepsToLocalMax - количество шагов лягушки до локального максимума;
  • movConstant - константа перемещения (шаг поиска) лягушек;
  • memsFrogs - количество лягушек в каждом мемплексе;
  • numbCyclesCNT - счетчик циклов;
  • vect - массив, хранящий вектор;
  • indexes - массив, хранящий индексы;
  • revision - флаг, указывающий на необходимость ревизии популяции.
//——————————————————————————————————————————————————————————————————————————————
class C_AO_SFL
{
  //----------------------------------------------------------------------------
  public: double cB        []; //best coordinates
  public: double fB;           //FF of the best coordinates
  public: S_Frog frogs     []; //all frogs
  public: S_Memeplex mems  []; //memeplexes

  public: double rangeMax  []; //maximum search range
  public: double rangeMin  []; //manimum search range
  public: double rangeStep []; //step search


  public: void Init (const int    coordinatesNumberP,   //coordinates number
                     const int    populationSizeP,      //population size
                     const int    numbMemsP,            //number of memeplexes
                     const int    numbCyclesP,          //number of cycles in the memeplex
                     const int    frogStepsToLocalMaxP, //frog steps to the local maximum
                     const double movConstantP);        //movement step (0.0 .. 1.0)

  public: void Moving   ();
  public: void Revision ();

  //----------------------------------------------------------------------------
  private: int    coordinatesNumber;   //coordinates number
  private: int    frogsNumber;         //frogs number
  private: int    numbMems;            //number of memeplexes
  private: int    numbCycles;          //number of cycles in the memeplex
  private: int    frogStepsToLocalMax; //frog steps to the local maximum
  private: double movConstant;         //movement step (0.0 .. 1.0)
  private: int    memsFrogs;           //number of frogs in the memplex
  private: int    numbCyclesCNT;       //cycle counter

  private: double vect    [];          //vector
  private: int    indexes [];          //indexes
  private: bool   revision;

  private: double SeInDiSp  (double In, double InMin, double InMax, double Step);
  private: double RNDfromCI (double min, double max);
};
//——————————————————————————————————————————————————————————————————————————————

Для инициализации алгоритма будем использовать метод инициализации Init, имеющий несколько параметров:

  • coordinatesNumberP - количество координат
  • populationSizeP - размер популяции
  • numbMemsP - количество мемплексов
  • numbCyclesP - количество циклов в мемплексе
  • frogStepsToLocalMaxP - количество шагов лягушки до локального максимума
  • movConstantP - шаг перемещения (от 0.0 до 1.0)


Вначале метод сбрасывает генератор случайных чисел и устанавливает начальное значение переменной fB (-DBL_MAX) и revision (false).
Затем метод создает массивы rangeMax, rangeMin, rangeStep, vect, indexes, cB, frogs и mems с нужными размерами.
Метод инициализирует каждую лягушку в массиве frogs и каждую лягушку в каждом мемплексе в массиве mems с помощью метода Init, передавая количество координат.
Затем метод устанавливает начальное значение переменной fBest каждого мемплекса в -DBL_MAX и создает массив cBest для каждого мемплекса с нужным размером.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_SFL::Init (const int    coordinatesNumberP,   //coordinates number
                     const int    populationSizeP,      //population size
                     const int    numbMemsP,            //number of memeplexes
                     const int    numbCyclesP,          //number of cycles in the memeplex
                     const int    frogStepsToLocalMaxP, //frog steps to the local maximum
                     const double movConstantP)         //movement step (0.0 .. 1.0)
{
  MathSrand ((int)GetMicrosecondCount ()); // reset of the generator
  fB       = -DBL_MAX;
  revision = false;

  coordinatesNumber   = coordinatesNumberP;
  frogsNumber         = populationSizeP;
  numbMems            = numbMemsP;
  numbCycles          = numbCyclesP;
  frogStepsToLocalMax = frogStepsToLocalMaxP;
  movConstant         = movConstantP;
  memsFrogs           = frogsNumber / numbMems;

  numbCyclesCNT       = 0;

  ArrayResize (rangeMax,  coordinatesNumber);
  ArrayResize (rangeMin,  coordinatesNumber);
  ArrayResize (rangeStep, coordinatesNumber);
  ArrayResize (vect,      coordinatesNumber);
  ArrayResize (indexes,   frogsNumber);

  ArrayResize (cB, coordinatesNumber);

  ArrayResize (frogs, frogsNumber);
  for (int i = 0; i < frogsNumber; i++)
  {
    frogs [i].Init (coordinatesNumber);
  }

  ArrayResize (mems, numbMems);
  for (int i = 0; i < numbMems; i++)
  {
    ArrayResize (mems [i].frogs, memsFrogs);
    for (int frgs = 0; frgs < memsFrogs; frgs++)
    {
      mems [i].frogs [frgs].Init (coordinatesNumber);
    }

    mems [i].fBest = -DBL_MAX;
    ArrayResize (mems [i].cBest, coordinatesNumber);
  }
}
//——————————————————————————————————————————————————————————————————————————————

Метод Moving предназначен для перемещения лягушек по пространству поиска. Метод достаточно большой и разберём его частями.

В начале кода проверяется значение переменной revision. Если оно равно false, то выполняется следующий блок кода:

- Задается начальное значение переменной fB, которая представляет собой наилучшую оценку функции. В данном случае ей присваивается значение -DBL_MAX (отрицательная бесконечность).
- Запускается цикл, в котором происходит инициализация каждой лягушки. Для каждой лягушки:
- Запускается цикл по каждой координате c.
- Генерируется случайное значение для координаты c с помощью функции RNDfromCI, которая возвращает случайное число в заданном диапазоне.
- Значение координаты c приводится к диапазону с помощью функции SeInDiSp, которая позволяет сдвинуть значение внутри диапазона с определенным шагом.
- Значение функции f для лягушки устанавливается равным -DBL_MAX.
- Значение предыдущего значения функции fPrev для лягушки устанавливается равным -DBL_MAX.
- Шаг лягушки frogStep устанавливается равным 0.

- Запускается цикл по каждой координате c.
- Вычисляется значение vect[c], которое представляет собой произведение разности между максимальным и минимальным значением диапазона на movConstant.
- Переменной revision присваивается значение true, чтобы указать, что инициализация была выполнена.
- Переменной numbCyclesCNT присваивается значение 0.

Таким образом, данный код выполняет инициализацию лягушек, устанавливает начальные значения функции и других параметров, а также вычисляет значение vect[c] для каждой координаты c.

if (!revision)
{
  fB = -DBL_MAX;

  for (int frgs = 0; frgs < frogsNumber; frgs++)
  {
    for (int c = 0; c < coordinatesNumber; c++)
    {
      frogs [frgs].c [c] = RNDfromCI (rangeMin [c], rangeMax [c]);
      frogs [frgs].c [c] = SeInDiSp (frogs [frgs].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      frogs [frgs].f        = -DBL_MAX;
      frogs [frgs].fPrev    = -DBL_MAX;
      frogs [frgs].frogStep = 0;
    }
  }

  for (int c = 0; c < coordinatesNumber; c++)
  {
    vect [c] = (rangeMax [c] - rangeMin [c]) * movConstant;
  }

  revision      = true;
  numbCyclesCNT = 0;
}

Если флаг revision равен true, это означает, что алгоритм перешел в стадию перемещения лягушек. С переменными метода мы уже познакомились, поэтому подробно останавливаться на них не будем. В этой части кода реализуются прыжки лягушек в соответствии с индивидуальным для каждой лягушки шагом. На первом шаге лягушка прыгает в сторону локального лидера, попытка засчитывается, если положение лягушки стало лучше, в противном случае счетчик шага будет увеличен. То есть, на прыжки в сторону локального лидера выделяется фиксированное количество попыток согласно внешним параметрам алгоритма.

Для лягушки - лидера используется иной принцип перемещения в отличии от всех остальных в мемплексе. Лидер просто совершает прыжки в случайном направлении в небольшой окрестности своего положения.

В отличии от лидера все остальные лягушки совершают прыжки в сторону лидера по следующей формуле:

coord = mems [m].frogs [frgs].cPrev [c] + rnd * vect [c] * ((mems [m].cBest [c] - mems [m].frogs [frgs].cPrev [c]) / eDistance);

из формулы видим, что новая координата лягушки будет получена путём перемещения в сторону лидера на расстояние между ними, нормированное на евклидово расстояние по всем координатам, при этом вводится случайная компонента rnd.
else
{
  int cnt = 0;
  double eDistance = 0.0; //euclidean distance
  double coordDiff = 0.0; //the difference in coordinates

  for  (int m = 0; m < numbMems; m++)
  {
    for (int frgs = 0; frgs < memsFrogs; frgs++)
    {
      //2.1 передвинуть лягушки в сторону лучшего в мемплексе-----------------
      if (mems [m].frogs [frgs].frogStep < frogStepsToLocalMax)
      {
        if (mems [m].frogs [frgs].fPrev != -DBL_MAX && mems [m].fBest != -DBL_MAX)
        {
          if (mems [m].frogs [frgs].fPrev == mems [m].fBest)
          {
            for (int c = 0; c < coordinatesNumber; c++)
            {
              rnd = RNDfromCI (-1.0, 1.0);
              rnd = rnd < 0.0 ? -rnd * rnd : rnd * rnd;
              coord = mems [m].frogs [frgs].cPrev [c] + rnd * vect [c] * 0.2;
              mems [m].frogs [frgs].c [c] = SeInDiSp (coord, rangeMin [c], rangeMax [c], rangeStep [c]);
            }
          }
          else
          {
            eDistance = 0.0;
            coordDiff = 0.0;

            //посчитаем евклидово расстояние----------------------------------
            for (int c = 0; c < coordinatesNumber; c++)
            {
              coordDiff  = mems [m].cBest [c] - mems [m].frogs [frgs].cPrev [c];
              coordDiff *= coordDiff;
              eDistance += coordDiff;
            }

            eDistance = sqrt (eDistance);

            for (int c = 0; c < coordinatesNumber; c++)
            {
              rnd = RNDfromCI (-1.0, 1.0);
              coord = mems [m].frogs [frgs].cPrev [c] + rnd * vect [c] * ((mems [m].cBest [c] - mems [m].frogs [frgs].cPrev [c]) / eDistance);
              mems [m].frogs [frgs].c [c] = SeInDiSp (coord, rangeMin [c], rangeMax [c], rangeStep [c]);
            }
          }
        }
        else
        {
          for (int c = 0; c < coordinatesNumber; c++)
          {
            rnd = RNDfromCI (-1.0, 1.0);
            rnd = rnd < 0.0 ? -rnd * rnd : rnd * rnd;
            coord = mems [m].frogs [frgs].cPrev [c] + rnd * vect [c];
            mems [m].frogs [frgs].c [c] = SeInDiSp (coord, rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
      }
      else
      {
        //2.2 передвинуть в сторону глобального если ход хуже предыдущего-----
        if (mems [m].frogs [frgs].frogStep /*==*/ >= frogStepsToLocalMax)
        {
          eDistance = 0.0;
          coordDiff = 0.0;

          //посчитаем евклидово расстояние------------------------------------
          for (int c = 0; c < coordinatesNumber; c++)
          {
            coordDiff  = cB [c] - mems [m].frogs [frgs].cPrev [c];
            coordDiff *= coordDiff;
            eDistance += coordDiff;
          }

          eDistance = sqrt (eDistance);

          for (int c = 0; c < coordinatesNumber; c++)
          {
            rnd = RNDfromCI (-1.0, 1.0);
            coord = mems [m].frogs [frgs].cPrev [c] + rnd * vect [c] * ((cB [c] - mems [m].frogs [frgs].cPrev [c]) / eDistance);
            mems [m].frogs [frgs].c [c] = SeInDiSp (coord, rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
        //2.3 передвинуть случайно по полю --------------------------------
        else
        {
          for (int c = 0; c < coordinatesNumber; c++)
          {
            rnd = RNDfromCI (-1.0, 1.0);

            coord    = RNDfromCI (rangeMin [c], rangeMax [c]);
            mems [m].frogs [frgs].c [c] = SeInDiSp (coord, rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
      }

      frogs [cnt] = mems [m].frogs [frgs];
      cnt++;
    }
  }


  //--------------------------------------------------------------------------
  numbCyclesCNT++;
}

Метод Revision предназначен для определения глобального лидера на каждой итерации алгоритма и определения локальных лидеров. Если количество циклов, отведённых на перемещения лягушек в рамках каждого мемплекса, исчерпано, то производим тасование - переназначаем лягушки по мемплексам случайным образом, тем самым осуществляется обмен информацией между мемплексами. В этом методе так же производится учет соответствующих шагов прыжков - куда будет перемещаться лягушка на следующей итерации (в сторону локального или глобального лидера или в случайном направлении в пространстве поиска).

//——————————————————————————————————————————————————————————————————————————————
void C_AO_SFL::Revision ()
{
  //4.1 определить глобально лучшего по популяции-------------------------------
  for (int i = 0; i < frogsNumber; i++)
  {
    if (frogs [i].f > fB)
    {
      fB = frogs [i].f;
      ArrayCopy (cB, frogs [i].c, 0, 0, WHOLE_ARRAY);
    }
  }

  int cnt = 0;

  //если последний цикл=========================================================
  if (numbCyclesCNT >= numbCycles)
  {
    //4.2.0 сбросить счетчик циклов мемплекса-----------------------------------
    numbCyclesCNT = 0;

    //4.2.1 сгенерировать случайно индексы--------------------------------------
    for (int i = 0; i < frogsNumber; i++) indexes [i] = i;

    Shuffle (indexes, frogsNumber);

    //4.2.2 скопировать случайно в мемплексы------------------------------------
    for  (int m = 0; m < numbMems; m++)
    {
      mems [m].fBest = -DBL_MAX;

      for (int frgs = 0; frgs < memsFrogs; frgs++)
      {
        mems [m].frogs [frgs] = frogs [indexes [cnt]];
        cnt++;

        //4.2.3 определить в каждом мемплексе лучшего---------------------------
        if (mems [m].frogs [frgs].f > mems [m].fBest)
        {
          mems [m].fBest = mems [m].frogs [frgs].f;
          ArrayCopy (mems [m].cBest, mems [m].frogs [frgs].c, 0, 0, WHOLE_ARRAY);
        }

        //4.2.4 сбросить приспособленность лягушек и шаг------------------------
        mems [m].frogs [frgs].f        = -DBL_MAX;
        mems [m].frogs [frgs].fPrev    = -DBL_MAX;
        mems [m].frogs [frgs].frogStep = 0;
      }
    }
  }
  //если НЕ последний цикл======================================================
  else
  {
    for  (int m = 0; m < numbMems; m++)
    {
      for (int frgs = 0; frgs < memsFrogs; frgs++)
      {
        mems [m].frogs [frgs].frogStep++;

        //4.3.1 скопировать приспособленность и координаты лягушек из популяции в
        //соответствующие лягушки мемплексов
        mems [m].frogs [frgs].f = frogs [cnt].f;
        ArrayCopy (mems [m].frogs [frgs].c, frogs [cnt].c, 0, 0, WHOLE_ARRAY);

        //4.3.2 определить в каждом мемплексе лучшего---------------------------
        if (frogs [cnt].f > mems [m].fBest)
        {
          mems [m].fBest = frogs [cnt].f;
          ArrayCopy (mems [m].cBest, frogs [cnt].c, 0, 0, WHOLE_ARRAY);
        }

        //4.3.3 определить направление следующего прыжка------------------------
        if (mems [m].frogs [frgs].frogStep <= frogStepsToLocalMax)
        {
          if (mems [m].frogs [frgs].f > mems [m].frogs [frgs].fPrev)
          {
            mems [m].frogs [frgs].fPrev = mems [m].frogs [frgs].f;
            ArrayCopy (mems [m].frogs [frgs].cPrev, mems [m].frogs [frgs].c, 0, 0, WHOLE_ARRAY);
            mems [m].frogs [frgs].frogStep = 0;
          }
        }
        else
        {
          if (mems [m].frogs [frgs].frogStep >= frogStepsToLocalMax + 1)
          {
            if (mems [m].frogs [frgs].f > mems [m].frogs [frgs].fPrev)
            {
              mems [m].frogs [frgs].fPrev = mems [m].frogs [frgs].f;
              ArrayCopy (mems [m].frogs [frgs].cPrev, mems [m].frogs [frgs].c, 0, 0, WHOLE_ARRAY);
              mems [m].frogs [frgs].frogStep = 0;
            }
          }
          else
          {
            mems [m].frogs [frgs].f        = -DBL_MAX;
            mems [m].frogs [frgs].fPrev    = -DBL_MAX;
            mems [m].frogs [frgs].frogStep = 0;
          }
        }

        cnt++;
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

В алгоритме оптимизации SFL нам понадобится перемешивать лягушки между мемплексами случайным образом. Задача случайного тасования интересна тем, что ее алгоритмическое решение нетривиально, однако благодаря Рональду Фишеру и Фрэнку Йетсу, мир получил эффективный и быстрый алгоритм. Ранее в статьях мы не использовали подобную концепцию, так как не было необходимости в этом. Он широко используется в компьютерных науках, особенно в области генетических алгоритмов, криптографии и статистики.

Основные преимущества алгоритма Фишера — Йетса:
1. Простота реализации: Алгоритм Фишера — Йетса легко реализовать на любом языке программирования. Он не требует сложных математических вычислений или специальных библиотек.
2. Эффективность: Алгоритм Фишера — Йетса работает за линейное время, то есть время выполнения зависит от количества элементов, которые нужно переставить. Это делает его очень эффективным для больших наборов данных.
3. Случайность: Алгоритм Фишера — Йетса обеспечивает высокую случайность перестановок. Это важно для многих приложений, таких как случайные тесты, шифрование и симуляции.
4. Независимость от входных данных: Алгоритм Фишера — Йетса может быть применен к любому набору данных без необходимости знания их структуры или свойств. Это делает его универсальным инструментом для многих задач.
5. Псевдослучайность: Алгоритм Фишера — Йетса генерирует псевдослучайные перестановки, которые могут быть использованы во многих приложениях, где требуется случайность, но не обязательно истинная случайность.

В целом, алгоритм Фишера — Йетса интересен своей простотой, эффективностью и универсальностью. Он является полезным инструментом для многих задач, связанных с случайностью и перестановками данных.

//——————————————————————————————————————————————————————————————————————————————
void Shuffle (int & arr [], int size)
{
  int index, temp;
  for (int i = size - 1; i > 0; i--)
  {
    index = MathRand () % (i + 1);
    temp = arr [i];
    arr [i] = arr [index];
    arr [index] = temp;
  }
}
//——————————————————————————————————————————————————————————————————————————————

3. Результаты тестов

Распечатка работы тасующего алгоритма прыгающих лягушек на тестовом стенде:

C_AO_SFL:50;25;15;5;0.7
=============================
5 Rastrigin's; Func runs 10000 result: 66.52578871476199
Score: 0.82429
25 Rastrigin's; Func runs 10000 result: 52.38937199890908
Score: 0.64913
500 Rastrigin's; Func runs 10000 result: 44.5591163558836
Score: 0.55211
=============================
5 Forest's; Func runs 10000 result: 0.5762718670482314
Score: 0.32597
25 Forest's; Func runs 10000 result: 0.16329693292687839
Score: 0.09237
500 Forest's; Func runs 10000 result: 0.04968320483668511
Score: 0.02810
=============================
5 Megacity's; Func runs 10000 result: 3.1599999999999997
Score: 0.26333
25 Megacity's; Func runs 10000 result: 1.016
Score: 0.08467
500 Megacity's; Func runs 10000 result: 0.3004
Score: 0.02503
=============================
All score: 2.84501

При наблюдении за анимацией работы алгоритма SFL не обнаруживается никакой кластеризации или группирования по отдельным мемам, хотя ожидалось обратное. Агенты (точки) оптимизации распределены по полю хаотично, и нет никаких закономерностей в движении частиц. Сразу заметно низкое качество сходимости алгоритма, что, к сожалению, проявляется в застревании в локальных экстремумах, что можно определить по долгим плавным участкам на графике сходимости. Однако с увеличением количества оптимизируемых параметров "ступенчатость" снижается.

r

  SFL на тестовой функции Rastrigin.

f

  SFL на тестовой функции Forest.

m

  SFL на тестовой функции Megacity.

Перейдем к подведению итогов. При анализе таблицы с результатами тестирования алгоритмов можно заметить, что алгоритм SFL показывает ниже средних результатов в качестве оптимизации. Хотя на гладкой функции Растригина SFL имеет некоторое преимущество, оно недостаточно, чтобы рекомендовать его как предпочтительный алгоритм для гладких функций. На функциях с изломами Forest и Megacity тасующий алгоритм прыгающих лягушек демонстрирует худшие результаты по сравнению с гладкими функциями. Это можно объяснить тем, что на пологих участках оптимизируемой функции прыжки лягушек не улучшают их позицию, и они постоянно возвращаются на исходное место, не имея возможности "зацепиться" за рельеф.

AO

Description

Rastrigin

Rastrigin final

Forest

Forest final

Megacity (discrete)

Megacity final

Final result

10 params (5 F)

50 params (25 F)

1000 params (500 F)

10 params (5 F)

50 params (25 F)

1000 params (500 F)

10 params (5 F)

50 params (25 F)

1000 params (500 F)

SSG

saplings sowing and growing

1,00000

1,00000

0,55665

2,55665

0,72740

0,94522

1,00000

2,67262

0,76364

0,85977

1,00000

2,62340

100,000

HS

harmony search

0,99676

0,95282

0,48178

2,43136

1,00000

0,98931

0,44806

2,43736

1,00000

1,00000

0,41537

2,41537

92,329

ACOm

ant colony optimization M

0,34611

0,17985

0,17044

0,69640

0,86888

1,00000

0,77362

2,64249

1,00000

0,88930

0,05606

1,94536

65,347

IWO

invasive weed optimization

0,95828

0,67083

0,29807

1,92719

0,70773

0,46349

0,31773

1,48896

0,80000

0,42067

0,33289

1,55356

61,104

COAm

cuckoo optimization algorithm M

0,92400

0,46794

0,26004

1,65199

0,58378

0,34034

0,16526

1,08939

0,72727

0,33025

0,17083

1,22835

47,612

FAm

firefly algorithm M

0,59825

0,33980

0,17135

1,10941

0,51073

0,42299

0,49790

1,43161

0,34545

0,28044

0,35258

0,97847

41,537

ABC

artificial bee colony

0,78170

0,32715

0,20822

1,31707

0,53837

0,21455

0,13344

0,88636

0,56364

0,26569

0,13926

0,96858

36,849

GSA

gravitational search algorithm

0,70167

0,45217

0,00000

1,15384

0,31660

0,36416

0,33204

1,01280

0,59395

0,35054

0,00000

0,94448

36,028

BA

bat algorithm

0,40526

0,63761

0,84451

1,88738

0,20841

0,17477

0,25989

0,64308

0,29698

0,09963

0,17371

0,57032

35,888

BFO

bacterial foraging optimization

0,67203

0,30963

0,11813

1,09979

0,39702

0,26623

0,20652

0,86976

0,52122

0,33211

0,18932

1,04264

34,693

EM

electroMagnetism-like algorithm

0,12235

0,46278

1,00000

1,58513

0,00000

0,03498

0,34880

0,38377

0,00000

0,00000

0,10924

0,10924

22,091

SFL

shuffled frog-leaping

0,40072

0,23739

0,26548

0,90360

0,20153

0,04147

0,02652

0,26952

0,27273

0,05535

0,06639

0,39446

15,203

MA

monkey algorithm

0,33192

0,33451

0,14644

0,81287

0,10012

0,07891

0,08932

0,26836

0,21818

0,04243

0,10720

0,36781

13,603

FSS

fish school search

0,46812

0,25337

0,11302

0,83451

0,12840

0,05013

0,06516

0,24369

0,16971

0,04796

0,08283

0,30050

12,655

PSO

particle swarm optimisation

0,20449

0,08200

0,07160

0,35809

0,18895

0,10486

0,21738

0,51119

0,23636

0,05903

0,01957

0,31496

10,031

RND

random

0,16826

0,09743

0,08019

0,34589

0,13496

0,04810

0,04715

0,23021

0,16971

0,03875

0,04922

0,25767

5,302

GWO

grey wolf optimizer

0,00000

0,00000

0,02256

0,02256

0,06570

0,00000

0,00000

0,06570

0,32727

0,07378

0,02557

0,42663

1,000


Выводы

SFL представляет собой скорее "надстройку" для других алгоритмов оптимизации, которые можно использовать как логику перемещения агентов в мемплексах, SFL и был изначально изобретён как методика улучшения качества оптимизации уже существующих алгоритмов. Как самостоятельный алгоритм оптимизации SFL демонстрирует результаты ниже средних среди популяционных алгоритмов, рассмотренных ранее. SFL имеет большие возможности экспериментирования с комбинированием логических шагов в алгоритме, повышающих как разведку, так и эксплуатацию. SFL обладает высокой гибкостью и адаптивностью, что делает его подходящим для специального использования в специфических задачах оптимизации.

Гистограмма результатов тестирования алгоритмов на рисунке 3 (по шкале от 0 до 100, чем больше тем лучше, в архиве скрипт для расчета рейтинговой таблицы по методике в этой статье).

chart

Рисунок 3. Гистограмма итоговых результатов тестирования алгоритмов.

Плюсы и минусы тасующего алгоритма прыгающих лягушек (SFL):

Плюсы:
1. Небольшое количество внешних параметров.
2. Оригинальность архитектуры алгоритма, возможность использовать в мемах другие алгоритмы оптимизации.

Минусы:
1. Высокая вычислительная трудоёмкость.
2. Невысокие результаты на гладких и дискретных функциях.
3. Застревание на функциях с ровными горизонтальными "площадками".

К каждой статье я прикрепляю архив, содержащий обновленные актуальные версии кодов алгоритмов, описанных в предыдущих статьях. Статьи созданы на основе накопленного опыта автора и его личного мнения, выводы и суждения основываются на результатах проведенных экспериментов.

Прикрепленные файлы |
Последние комментарии | Перейти к обсуждению на форуме трейдеров (11)
fxsaber
fxsaber | 20 сент. 2023 в 00:33
Stanislav Korotky #:

Набросал его просто потому, что если уж ваять какие-то навороты, то без потенциальных проблем - "зацепило" использование цикла с перебором строк (!) - может быть супер неэффективно в программах побольше.

В ini-файле не только строковые параметры, но и других типов (хотя представлены текстом).

Синглтон у фабрики - это нормально. Синглтон у объекта функции - в данном случае просто для работоспособности примера - можно реализовать множественность.

Использую string-решение на этапе инициализации. Думаю, меньше миллисекунды занимает. Честно говоря, не прочувствовал потенциальных проблем. 

Dmitry Fedoseev
Dmitry Fedoseev | 20 сент. 2023 в 06:16
fxsaber #:

Спасибо автору за проделанную огромную работу и безвозмездную возможность использовать ее!



Немного смотрю код. И вот эту красоту заменил у себя на такой ужас.

А в чем выигрыш? Это же действительно ужас. Извините, если что))

Dmitry Fedoseev
Dmitry Fedoseev | 20 сент. 2023 в 06:32
Stanislav Korotky #:

По-хорошему, создание функции по запросу должно в самом классе сидеть (без плясок со строками и "завязкой" на совпадение фрагментов имен классов и элементов перечисления). Там есть вариант автора. Вот еще один - что-то вроде такого:

Файл Functions.mqh:

Использовать в скрипте так:

Извините, но я конечно же ошибаюсь, вот в той строке кода:

static FunctionInstance *pointers[] = {C_Function::fabric<C_Skin>(), C_Function::fabric<C_Forest>(), C_Function::fabric<C_Megacity>(), C_Function::fabric<C_Rastrigin>(), C_Function::fabric<C_Universe>()};
 

не будут создаваться по объекту каждого типа?

...тогда как, за все время работы программы будет использоваться только один тип.

fxsaber
fxsaber | 20 сент. 2023 в 07:25
Dmitry Fedoseev #:

А в чем выигрыш? Это же действительно ужас. Извините, если что))

Сложный вопрос. Не знаю.

Stanislav Korotky
Stanislav Korotky | 20 сент. 2023 в 19:46
Dmitry Fedoseev #:

Извините, но я конечно же ошибаюсь, вот в той строке кода:

не будут создаваться по объекту каждого типа?

...тогда как, за все время работы программы будет использоваться только один тип.

В этой строчке создаются фабрики. Они действительно для всех классов резервируются (во время старта). Рабочий объект фабрика создает при явном вызове create.

Готовые шаблоны для подключения индикаторов в экспертах (Часть 2): Индикакторы объёма и Билла Вильямса Готовые шаблоны для подключения индикаторов в экспертах (Часть 2): Индикакторы объёма и Билла Вильямса
В статье рассмотрим стандартные индикаторы из категории Объемов и индикаторов Билла Вильямса. Создадим готовые к применению шаблоны использования индикаторов в советниках — объявление и установка параметров, инициализация, деинициализация индикаторов и получение данных и сигналов из индикаторных буферов в советниках.
Разработка пользовательского канала Дончиана с помощью MQL5 Разработка пользовательского канала Дончиана с помощью MQL5
Существует множество технических инструментов, которые можно использовать для визуализации ценового канала. Одним из таких инструментов является канал Дончиана (Donchian Channel). В этой статье мы узнаем, как создать канал Дончиана и как использовать его в качестве пользовательского индикатора в составе советника.
Язык визуального программиования ДРАКОН (Drakon) — средство общения для разработчика MQL и заказчика Язык визуального программиования ДРАКОН (Drakon) — средство общения для разработчика MQL и заказчика
ДРАКОН — язык визуального программирования, специально разработанный для упрощения взаимодействия между специалистами разных отраслей (биологами, физиками, инженерами...) с программистами в российских космических проектах (например, при создании создание комплекса "Буран"). В этой статье я расскажу о том, как ДРАКОН делает создание алгоритмов доступным и интуитивно понятным, даже если вы никогда не сталкивались с кодом, а также - как заказчику легче объяснить свои мысли при заказе торговых роботов, а программисту - совершать меньше ошибок в сложных функциях.
Понимание и эффективное использование тестера стратегий MQL5 Понимание и эффективное использование тестера стратегий MQL5
MQL5-разработчикам крайне необходимо освоить важные и ценные инструменты. Одним из таких инструментов является тестер стратегий. Статья представляет собой практическое руководство по использованию тестера стратегий MQL5.