preview
Алгоритм на основе фракталов — Fractal-Based Algorithm (FBA)

Алгоритм на основе фракталов — Fractal-Based Algorithm (FBA)

MetaTrader 5Трейдинг |
582 0
Andrey Dik
Andrey Dik

Содержание
  1. Введение
  2. Реализация алгоритма
  3. Результаты тестов


Введение

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

В данной статье рассмотрим новый метаэвристический алгоритм для решения задач непрерывной оптимизации — Fractal-based Algorithm (FBA) Марджана Каеди от 2017 года. Этот подход основан на геометрических свойствах фракталов и использует концепцию самоподобия для адаптивного исследования пространства. В основе алгоритма лежит инновационная эвристика, которая оценивает перспективность различных областей поиска на основе плотности расположения в них качественных решений.

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


Реализация алгоритма

Представьте, что вы ищете клад в огромном поле. Как бы вы действовали? Вероятно, не стали бы перекапывать каждый сантиметр земли — это заняло бы слишком много времени. Именно такую проблему решает фрактальный алгоритм (FBA) — он помогает найти "сокровище" (оптимальное решение) в огромном пространстве возможностей, не проверяя каждую точку.

Как можно представить работу этого алгоритма? Сначала разбиваем всю территорию поиска на равные квадраты, как шахматную доску. Затем забрасываем "искателей" (случайные точки) по всему полю, чтобы получить первое представление о местности. Каждый искатель докладывает о качестве найденного им участка. Алгоритм отбирает самых успешных (60% лучших точек) и смотрит, в каких квадратах они сосредоточены. Эти квадраты помечаются как "перспективные зоны" (30% лучших квадратов).

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

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

fba-space-partitioning

Рисунок 1. Визуализация схемы работы алгоритма FBA

Основная идея FBA, показанная на рисунке выше, заключается в постепенном разбиении пространства поиска на все меньшие и меньшие области фрактальным образом, фокусируя вычислительные ресурсы на наиболее перспективных областях. Это создает самоподобную структуру, которая исследует пространство решений. Переходим к написанию псевдокода алгоритма FBA.

Инициализация:
  • Разделить все пространство поиска на равные подпространства
  • Сгенерировать начальную популяцию равномерно по всему пространству
  • Оценить пригодность каждой точки
Процесс итерации:
  • Определение перспективных точек: выберите P1% точек с наилучшей пригодностью.
  • Рассчет рангов подпространств: подсчитайте, сколько перспективных точек попадает в каждое подпространство.
  • Выбор перспективных подпространств: выберите P2% подпространств с наивысшими перспективными рангами.
  • Разделение перспективных подпространств: дальнейшее разделение перспективных областей на более мелкие подпространства.
  • Генерация нового населения: создание новых точек с более высокой плотностью в перспективных регионах.
  • Применение мутации: добавить гауссовский шум к P3% точек (механизм исследования).
  • Интеграция популяций: объединение текущей и новой популяции с сохранением лучших точек.
Критерий останова:
  • Продолжайте, пока не будет достигнут критерий остановки (максимальное количество итераций, порог пригодности и т.д.)
  • Верните лучшее найденное решение

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

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

В классе объявлено также внутреннее структурное представление "S_Subspace", которое используется для описания подпространства поиска. Каждое подпространство характеризуется минимальными и максимальными границами по каждому измерению, степенью его перспективности, уровнем в иерархии, а также связью с родительским подпространством.

Основные методы внутри класса включают функционал для:

  • Проверки, находится ли точка внутри заданного подпространства.
  • Создания начальной разметки пространства и его дальнейшего деления.
  • Выявления перспективных точек, расчёта их рангов и выбора лучших подпространств для дальнейшего деления.
  • Генерации новой популяции путём объединения, мутаций и сортировки по критерию эффективности или пригодности.

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

//——————————————————————————————————————————————————————————————————————————————
class C_AO_FBA : public C_AO
{
  public: //--------------------------------------------------------------------
  ~C_AO_FBA () { }
  C_AO_FBA ()
  {
    ao_name = "FBA";
    ao_desc = "Fractal-Based Algorithm";
    ao_link = "https://www.mql5.com/ru/articles/17458";

    popSize = 50;  // размер популяции
    P1      = 60;  // процент перспективных точек
    P2      = 30;  // процент перспективных подпространств
    P3      = 0.8; // процент точек для случайной модификации
    m_value = 10;  // число интервалов для разделения каждого измерения

    ArrayResize (params, 5);

    params [0].name = "popSize"; params [0].val = popSize;
    params [1].name = "P1";      params [1].val = P1;
    params [2].name = "P2";      params [2].val = P2;
    params [3].name = "P3";      params [3].val = P3;
    params [4].name = "m_value"; params [4].val = m_value;
  }

  void SetParams ()
  {
    popSize = (int)params [0].val;
    P1      = (int)params [1].val;
    P2      = (int)params [2].val;
    P3      =      params [3].val;
    m_value = (int)params [4].val;
  }

  bool Init (const double &rangeMinP  [],  // минимальные значения
             const double &rangeMaxP  [],  // максимальные значения
             const double &rangeStepP [],  // шаг изменения
             const int     epochsP = 0);   // количество эпох

  void Moving   ();
  void Revision ();

  //----------------------------------------------------------------------------
  int    P1;           // процент перспективных точек
  int    P2;           // процент перспективных подпространств
  double P3;           // доля точек для случайной модификации
  int    m_value;      // число интервалов для разделения каждого измерения

  private: //-------------------------------------------------------------------

  // Структура для представления подпространства
  struct S_Subspace
  {
      double min [];        // минимальные границы подпространства
      double max [];        // максимальные границы подпространства
      double promisingRank; // ранг перспективности (нормализованное значение)
      bool   isPromising;   // флаг перспективности
      int    parentIndex;   // индекс родительского подпространства (-1 для корневых)
      int    level;         // уровень в иерархии (0 для исходного пространства)

      void Init (int coords)
      {
        ArrayResize (min, coords);
        ArrayResize (max, coords);
        promisingRank = 0.0;
        isPromising   = false;
        parentIndex   = -1;
        level         = 0;
      }
  };

  S_Subspace subspaces     []; // массив подпространств

  // Вспомогательные методы
  bool   IsPointInSubspace              (const double &point [], const S_Subspace &subspace);
  void   CreateInitialSpacePartitioning ();
  void   DivideSubspace                 (int subspaceIndex);
  void   IdentifyPromisingPoints        (int &promisingIndices []);
  void   CalculateSubspaceRanks         (const int &promisingIndices []);
  void   SelectPromisingSubspaces       ();
  void   DividePromisingSubspaces       ();
  void   GenerateNewPopulation          ();
  void   MutatePoints                   ();
  void   SortByFitness                  (double &values [], int &indices [], int size, bool ascending = false);
  void   QuickSort                      (double &values [], int &indices [], int low, int high, bool ascending);
  int    Partition                      (double &values [], int &indices [], int low, int high, bool ascending);
};
//——————————————————————————————————————————————————————————————————————————————

Метод инициализации "Init" предназначен для установки начальных условий работы алгоритма. Он принимает массивы с минимальными, максимальными границами переменных и шагами изменения для каждого параметра, а также количество эпох. Внутри метода сначала вызывается базовая инициализация, после чего, создается начальное деление пространства поиска, то есть, формируется начальная структура подпространств на основе заданных диапазонов и шагов. Если все операции успешны, метод возвращает "true", иначе — "false".

Основной метод движения "Moving" выполняет цикл поиска решения путем последовательных итераций. Первая итерация — это инициализация начальной популяции точек равномерно по всему исследуемому пространству: каждое значение переменной выбирается случайным образом внутри диапазона и корректируется под заданный шаг.

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

//——————————————————————————————————————————————————————————————————————————————
bool C_AO_FBA::Init (const double &rangeMinP  [],  // минимальные значения
                     const double &rangeMaxP  [],  // максимальные значения
                     const double &rangeStepP [],  // шаг изменения
                     const int     epochsP = 0)    // количество эпох
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //----------------------------------------------------------------------------
  // Создаем начальное разделение пространства поиска
  CreateInitialSpacePartitioning ();

  return true;
}
//——————————————————————————————————————————————————————————————————————————————

//+----------------------------------------------------------------------------+
//| Основной метод оптимизации                                                 |
//+----------------------------------------------------------------------------+
void C_AO_FBA::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;
  }

  // Основной процесс оптимизации

  // 1. Выявление перспективных точек (P1% точек с лучшими значениями функции)
  int promisingIndices [];
  IdentifyPromisingPoints (promisingIndices);

  // 2. Расчет рангов перспективности для каждого подпространства
  CalculateSubspaceRanks (promisingIndices);

  // 3. Выбор P2% самых перспективных подпространств
  SelectPromisingSubspaces ();

  // 4. Разделение перспективных подпространств на более мелкие
  DividePromisingSubspaces ();

  // 5. Генерация новых точек с учетом рангов перспективности
  GenerateNewPopulation ();

  // 6. Случайная модификация (мутация)
  MutatePoints ();
}
//——————————————————————————————————————————————————————————————————————————————

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

//+----------------------------------------------------------------------------+
//| Обновление лучшего решения                                                 |
//+----------------------------------------------------------------------------+
void C_AO_FBA::Revision ()
{
  // Поиск лучшего решения
  for (int i = 0; i < popSize; i++)
  {
    // Обновление лучшего решения
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

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

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

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

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

//+----------------------------------------------------------------------------+
//| Создание начального разделения пространства                                |
//+----------------------------------------------------------------------------+
void C_AO_FBA::CreateInitialSpacePartitioning ()
{
  // Создаем начальное разделение пространства
  int totalSubspaces = (int)MathPow (m_value, coords);

  // При очень большой размерности ограничиваем количество подпространств
  if (totalSubspaces > 10000) totalSubspaces = 10000;

  ArrayResize (subspaces, totalSubspaces);

  // Инициализируем все подпространства
  for (int i = 0; i < totalSubspaces; i++)
  {
    subspaces [i].Init (coords);
    subspaces [i].level = 0; // Начальный уровень
  }

  // Разделяем начальное пространство на равные подпространства
  int index = 0;

  // В зависимости от размерности пространства выбираем метод разделения
  if (coords == 1)
  {
    // Одномерный случай
    double intervalSize = (rangeMax [0] - rangeMin [0]) / m_value;

    for (int i = 0; i < m_value && index < totalSubspaces; i++)
    {
      subspaces [index].min [0] = rangeMin [0] + i * intervalSize;
      subspaces [index].max [0] = rangeMin [0] + (i + 1) * intervalSize;
      index++;
    }
  }
  else
    if (coords == 2)
    {
      // Двумерный случай
      double intervalSize0 = (rangeMax [0] - rangeMin [0]) / m_value;
      double intervalSize1 = (rangeMax [1] - rangeMin [1]) / m_value;

      for (int i = 0; i < m_value && index < totalSubspaces; i++)
      {
        for (int j = 0; j < m_value && index < totalSubspaces; j++)
        {
          subspaces [index].min [0] = rangeMin [0] + i * intervalSize0;
          subspaces [index].max [0] = rangeMin [0] + (i + 1) * intervalSize0;
          subspaces [index].min [1] = rangeMin [1] + j * intervalSize1;
          subspaces [index].max [1] = rangeMin [1] + (j + 1) * intervalSize1;
          index++;
        }
      }
    }
    else
    {
      // Многомерный случай - используем итеративный подход
      int indices [];
      ArrayResize (indices, coords);
      for (int i = 0; i < coords; i++) indices [i] = 0;

      while (index < totalSubspaces)
      {
        // Вычисляем границы текущего подпространства
        for (int c = 0; c < coords; c++)
        {
          double intervalSize = (rangeMax [c] - rangeMin [c]) / m_value;
          subspaces [index].min [c] = rangeMin [c] + indices [c] * intervalSize;
          subspaces [index].max [c] = rangeMin [c] + (indices [c] + 1) * intervalSize;
        }

        // Переходим к следующему подпространству
        int c = coords - 1;
        while (c >= 0)
        {
          indices [c]++;
          if (indices [c] < m_value) break;
          indices [c] = 0;
          c--;
        }

        // Если завершили полный цикл, выходим
        if (c < 0) break;

        index++;
      }
    }
}
//——————————————————————————————————————————————————————————————————————————————

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

//+----------------------------------------------------------------------------+
//| Определение принадлежности точки подпространству                           |
//+----------------------------------------------------------------------------+
bool C_AO_FBA::IsPointInSubspace (const double &point [], const S_Subspace &subspace)
{
  // Проверяем, находится ли точка в указанном подпространстве
  for (int c = 0; c < coords; c++)
  {
    if (point [c] < subspace.min [c] || point [c] >= subspace.max [c])
    {
      return false;
    }
  }

  return true;
}
//——————————————————————————————————————————————————————————————————————————————

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

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

//+----------------------------------------------------------------------------+
//| Выявление перспективных точек                                              |
//+----------------------------------------------------------------------------+
void C_AO_FBA::IdentifyPromisingPoints (int &promisingIndices [])
{
  // Выбираем P1% точек с лучшими значениями функции

  // Создаем массивы для сортировки
  double values  [];
  int    indices [];

  ArrayResize (values,  popSize);
  ArrayResize (indices, popSize);

  // Заполняем массивы
  for (int i = 0; i < popSize; i++)
  {
    values  [i] = a [i].f;
    indices [i] = i;
  }

  // Сортируем по убыванию (для задачи максимизации)
  SortByFitness (values, indices, popSize);

  // Выбираем P1% лучших точек
  int numPromisingPoints = (int)MathRound (popSize * P1 / 100.0);
  numPromisingPoints = MathMax (1, MathMin (numPromisingPoints, popSize));

  ArrayResize (promisingIndices, numPromisingPoints);

  for (int i = 0; i < numPromisingPoints; i++)
  {
    promisingIndices [i] = indices [i];
  }
}
//——————————————————————————————————————————————————————————————————————————————

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

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

//+----------------------------------------------------------------------------+
//| Расчет рангов перспективности подпространств                               |
//+----------------------------------------------------------------------------+
void C_AO_FBA::CalculateSubspaceRanks (const int &promisingIndices [])
{
  // Сбрасываем ранги подпространств
  for (int i = 0; i < ArraySize (subspaces); i++)
  {
    subspaces [i].promisingRank = 0.0;
  }

  // Подсчитываем перспективные точки в каждом подпространстве
  for (int i = 0; i < ArraySize (promisingIndices); i++)
  {
    int pointIndex = promisingIndices [i];

    for (int j = 0; j < ArraySize (subspaces); j++)
    {
      if (IsPointInSubspace (a [pointIndex].c, subspaces [j]))
      {
        subspaces [j].promisingRank++;
        break; // Точка может находиться только в одном подпространстве
      }
    }
  }

  // Нормализуем ранги перспективности согласно статье
  // PromisingRank = Number of promising points in s / Total promising points
  int totalPromisingPoints = ArraySize (promisingIndices);
  if (totalPromisingPoints > 0)
  {
    for (int i = 0; i < ArraySize (subspaces); i++)
    {
      subspaces [i].promisingRank = subspaces [i].promisingRank / totalPromisingPoints;
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

Метод "SelectPromisingSubspaces" определяет, какие подпространства следует считать перспективными, основываясь на их рангах. Создаются временные массивы "ranks" и "indices" для хранения рейтингов подпространств и их соответствующих индексов. Значения рейтингов и индексы подпространств копируются во временные массивы. Для каждого подпространства устанавливается флаг "isPromising" в "false". Это необходимо, чтобы начать с чистого листа и не учитывать результаты предыдущих итераций. Массив "ranks" сортируется в порядке убывания, а параллельно с этим, массив "indices" перестраивается так, чтобы сохранять соответствие индексов рейтингам.

Таким образом, в "indices" после сортировки находятся индексы подпространств, отсортированные по убыванию их рейтингов. Вычисляется количество подпространств, которые будут считаться перспективными, исходя из параметра "P2" (процент лучших подпространств). Полученное значение ограничивается диапазоном от 1 до общего числа подпространств. Проходится по первым "numPromisingSubspaces" элементам массива "indices". Для каждого индекса в этом массиве устанавливается флаг "isPromising" в "true" для соответствующего подпространства. Это означает, что данное подпространство считается перспективным.

Также присутствует проверка индекса на нахождение в пределах допустимого диапазона, чтобы избежать ошибок при обращении к массиву "subspaces". В результате выполнения метода, флаг "isPromising" установлен в "true" для "P2%" подпространств с наивысшими рангами.

//+----------------------------------------------------------------------------+
//| Выбор перспективных подпространств                                         |
//+----------------------------------------------------------------------------+
void C_AO_FBA::SelectPromisingSubspaces ()
{
  // Выбираем P2% подпространств с наивысшими рангами как перспективные

  // Создаем массивы для сортировки
  double ranks [];
  int indices [];

  int numSubspaces = ArraySize (subspaces);
  ArrayResize (ranks, numSubspaces);
  ArrayResize (indices, numSubspaces);

  // Заполняем массивы
  for (int i = 0; i < numSubspaces; i++)
  {
    ranks [i] = subspaces [i].promisingRank;
    indices [i] = i;
    // Сбрасываем флаг перспективности
    subspaces [i].isPromising = false;
  }

  // Сортируем по убыванию рангов
  SortByFitness (ranks, indices, numSubspaces);

  // Выбираем P2% самых перспективных подпространств
  int numPromisingSubspaces = (int)MathRound (numSubspaces * P2 / 100.0);
  numPromisingSubspaces = MathMax (1, MathMin (numPromisingSubspaces, numSubspaces));

  // Отмечаем перспективные подпространства
  for (int i = 0; i < numPromisingSubspaces && i < ArraySize (indices); i++)
  {
    // Защита от выхода за пределы массива
    if (indices [i] >= 0 && indices [i] < ArraySize (subspaces))
    {
      subspaces [indices [i]].isPromising = true;
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

Метод "DividePromisingSubspaces" предназначен для разделения перспективных подпространств на более мелкие части. Сначала он идентифицирует все подпространства, помеченные как перспективные, путем проверки флага "isPromising". Индексы этих подпространств собираются во временный массив "promisingIndices". Затем, для каждого подпространства, индекс которого находится в массиве "promisingIndices", вызывается функция "DivideSubspace" для передачи индекса этого подпространства.

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

//+----------------------------------------------------------------------------+
//| Разделение перспективных подпространств                                    |
//+----------------------------------------------------------------------------+
void C_AO_FBA::DividePromisingSubspaces ()
{
  // Собираем индексы перспективных подпространств
  int promisingIndices [];
  int numPromising = 0;

  for (int i = 0; i < ArraySize (subspaces); i++)
  {
    if (subspaces [i].isPromising)
    {
      numPromising++;
      ArrayResize (promisingIndices, numPromising);
      promisingIndices [numPromising - 1] = i;
    }
  }

  // Делим каждое перспективное подпространство
  for (int i = 0; i < numPromising; i++)
  {
    DivideSubspace (promisingIndices [i]);
  }
}
//——————————————————————————————————————————————————————————————————————————————

Метод "DivideSubspace" предназначен для разбиения конкретного подпространства на более мелкие части. Вначале выбирается родительское подпространство по переданному индексу. Проверяется, не превышает ли общее число подпространств установленный лимит (10 000). Определяется общее число новых подпространств, которое получится в результате деления по каждому измерению на "m_value  частей" — это возведение "m_value" в степень, равную количеству измерений.

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

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

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

//+----------------------------------------------------------------------------+
//| Разделение конкретного подпространства                                     |
//+----------------------------------------------------------------------------+
void C_AO_FBA::DivideSubspace (int subspaceIndex)
{
  // Делим указанное подпространство на m_value^coords подпространств

  S_Subspace parent = subspaces [subspaceIndex];

  // Ограничение на максимальное количество подпространств
  if (ArraySize (subspaces) > 10000) return;

  // Для каждого измерения делим на m_value частей
  int totalNewSubspaces = (int)MathPow (m_value, coords);
  int currentSize = ArraySize (subspaces);
  ArrayResize (subspaces, currentSize + totalNewSubspaces);

  // Создаем новые подпространства
  int newIndex = currentSize;
  int indices [];
  ArrayResize (indices, coords);
  for (int i = 0; i < coords; i++) indices [i] = 0;

  for (int idx = 0; idx < totalNewSubspaces && newIndex < ArraySize (subspaces); idx++)
  {
    subspaces [newIndex].Init (coords);
    subspaces [newIndex].level = parent.level + 1;
    subspaces [newIndex].parentIndex = subspaceIndex;

    // Вычисляем границы текущего подпространства
    for (int c = 0; c < coords; c++)
    {
      double intervalSize = (parent.max [c] - parent.min [c]) / m_value;
      subspaces [newIndex].min [c] = parent.min [c] + indices [c] * intervalSize;
      subspaces [newIndex].max [c] = parent.min [c] + (indices [c] + 1) * intervalSize;
    }

    // Переходим к следующему подпространству
    int c = coords - 1;
    while (c >= 0)
    {
      indices [c]++;
      if (indices [c] < m_value) break;
      indices [c] = 0;
      c--;
    }

    // Если завершили полный цикл, выходим
    if (c < 0) break;

    newIndex++;
  }
}
//——————————————————————————————————————————————————————————————————————————————

Метод "GenerateNewPopulation" создает новую популяцию точек, распределяя их по подпространствам в соответствии с "перспективностью", определяемой параметром "promisingRank".

Сначала вычисляется общая сумма "promisingRank" для всех подпространств. Это значение будет использоваться для определения пропорционального количества точек, которое должно быть сгенерировано в каждом подпространстве. Если сумма рангов оказывается близкой к нулю (меньше 0.0001), что может произойти, когда все подпространства имеют нулевой ранг, то всем подпространствам присваивается минимальный положительный ранг (1.0), чтобы обеспечить равномерное распределение точек. Затем, для каждого подпространства вычисляется количество точек, которые нужно сгенерировать в нем, пропорционально его "promisingRank" относительно общей суммы рангов.

Используется формула (subspaces[i].promisingRank / totalRank) * popSize, где popSize - требуемый размер популяции. Результат округляется до ближайшего целого числа. Количество точек ограничивается сверху, чтобы не превысить "popSize". Внутри каждого подпространства генерируется вычисленное количество точек, и для каждой точки генерируются "coords" координат, используя равномерное распределение в пределах границ подпространства. Для каждого измерения значение координаты ограничивается заданными минимальным и максимальным значениями и приводится к сетке с шагом "rangeStep[c]".

Если после распределения точек по подпространствам, общее количество сгенерированных точек меньше "popSize", оставшиеся точки генерируются равномерно по всему пространству поиска, используя границы всего пространства "rangeMin[c]", "rangeMax[c]", ограничения и приводятся к сетке с шагом "rangeStep[c]". Это гарантирует, что популяция всегда будет иметь размер "popSize".

//+----------------------------------------------------------------------------+
//| Генерация новой популяции                                                  |
//+----------------------------------------------------------------------------+
void C_AO_FBA::GenerateNewPopulation ()
{
  // Вычисляем сумму рангов всех подпространств
  double totalRank = 0.0;
  for (int i = 0; i < ArraySize (subspaces); i++)
  {
    totalRank += subspaces [i].promisingRank;
  }

  // Если все ранги равны 0, установим равномерное распределение
  if (totalRank <= 0.0001) // Проверка на приближенное равенство к нулю
  {
    for (int i = 0; i < ArraySize (subspaces); i++)
    {
      subspaces [i].promisingRank = 1.0;
    }
    totalRank = ArraySize (subspaces);
  }

  int points = 0;

  for (int i = 0; i < ArraySize (subspaces) && points < popSize; i++)
  {
    // Вычисляем количество точек для этого подпространства согласно формуле
    int pointsToGenerate = (int)MathRound ((subspaces [i].promisingRank / totalRank) * popSize);

    // Ограничение, чтобы не выйти за пределы
    pointsToGenerate = MathMin (pointsToGenerate, popSize - points);

    // Генерируем точки в этом подпространстве
    for (int j = 0; j < pointsToGenerate; j++)
    {
      // Создаем новую точку равномерно в пределах подпространства
      for (int c = 0; c < coords; c++)
      {
        a [points].c [c] = u.RNDfromCI (subspaces [i].min [c], subspaces [i].max [c]);
        a [points].c [c] = u.SeInDiSp (a [points].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }

      points++;
      if (points >= popSize) break;
    }
  }

  // Если не все точки были сгенерированы, заполняем оставшиеся равномерно по всему пространству
  while (points < popSize)
  {
    for (int c = 0; c < coords; c++)
    {
      a [points].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
      a [points].c [c] = u.SeInDiSp (a [points].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
    points++;
  }
}
//——————————————————————————————————————————————————————————————————————————————

Метод "MutatePoints" класса "C_AO_FBA " предназначен для осуществления мутации точек в популяции и является частью процедуры алгоритма для повышения варьируемости решений и предотвращения застревания в локальных ловушках.

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

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

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

//+----------------------------------------------------------------------------+
//| Мутация точек в популяции                                                  |
//+----------------------------------------------------------------------------+
void C_AO_FBA::MutatePoints ()
{
  // Добавляем гауссовский шум к P3% случайно выбранных точек из новой популяции
  /*
  int numToMutate = (int)MathRound (popSize * P3 / 100.0);
  numToMutate = MathMax (1, MathMin (numToMutate, popSize));

  for (int i = 0; i < numToMutate; i++)
  {
    int index = u.RNDminusOne (popSize);

    // Добавляем шум к каждой координате
    for (int c = 0; c < coords; c++)
    {
      // Стандартное отклонение 10% от диапазона
      //double stdDev = (rangeMax [c] - rangeMin [c]) * 0.1;

      // Гауссовский шум с использованием метода Бокса-Мюллера
      //double noise = NormalRandom (0.0, stdDev);

      // Добавляем шум и ограничиваем значение
      a [index].c [c] += noise;
      a [index].c [c] = u.SeInDiSp (a [index].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }
  */

  for (int p = 0; p < popSize; p++)
  {
    for (int c = 0; c < coords; c++)
    {
      if (u.RNDprobab () < P3)
      {
        a [p].c [c] = u.PowerDistribution (cB [c], rangeMin [c], rangeMax [c], 20);
        a [p].c [c] = u.SeInDiSp (a [p].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

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

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

//+----------------------------------------------------------------------------+
//| Сортировка по значению фитнес-функции                                      |
//+----------------------------------------------------------------------------+
void C_AO_FBA::SortByFitness (double &values [], int &indices [], int size, bool ascending = false)
{
  if (size > 1) QuickSort (values, indices, 0, size - 1, ascending);
}
//——————————————————————————————————————————————————————————————————————————————

Метод "QuickSort" реализует алгоритм быстрой сортировки для двух связанных массивов: "values" (массив значений) и "indices" (массив индексов). Он рекурсивно разделяет и сортирует массивы таким образом, чтобы сохранялось соответствие между значением и его исходным индексом. Параметры: массивы для сортировки, границы сортируемой области (low и high) и флаг "ascending", определяющий порядок сортировки.

//+----------------------------------------------------------------------------+
//| Алгоритм быстрой сортировки                                                |
//+----------------------------------------------------------------------------+
void C_AO_FBA::QuickSort (double &values [], int &indices [], int low, int high, bool ascending)
{
  if (low < high)
  {
    int pi = Partition (values, indices, low, high, ascending);

    QuickSort (values, indices, low, pi - 1, ascending);
    QuickSort (values, indices, pi + 1, high, ascending);
  }
}
//——————————————————————————————————————————————————————————————————————————————

Метод "Partition" является ключевой частью алгоритма быстрой сортировки. Его задача — выбрать опорный элемент и перераспределить элементы массива "indices" таким образом, чтобы все элементы, "меньшие" опорного (или "большие", в зависимости от порядка сортировки), оказались слева от него, а "большие" ("меньшие") — справа. "Меньше" и "больше" определяются относительно значений в массиве "values", на которые указывают индексы из "indices".

    //+----------------------------------------------------------------------------+
    //| Функция разделения для QuickSort                                           |
    //+----------------------------------------------------------------------------+
    int C_AO_FBA::Partition (double &values [], int &indices [], int low, int high, bool ascending)
    {
      double pivot = values [indices [high]];
      int i = low - 1;
    
      for (int j = low; j < high; j++)
      {
        bool condition = ascending ? (values [indices [j]] < pivot) : (values [indices [j]] > pivot);
        if (condition)
        {
          i++;
          // Обмен значениями
          int temp = indices [i];
          indices [i] = indices [j];
          indices [j] = temp;
        }
      }
    
      // Обмен значениями
      int temp = indices [i + 1];
      indices [i + 1] = indices [high];
      indices [high] = temp;
    
      return i + 1;
    }
    //——————————————————————————————————————————————————————————————————————————————


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

    Результат алгоритм показывает хороший.

    FBA|Fractal-Based Algorithm|50.0|60.0|30.0|0.8|10.0|
    =============================
    5 Hilly's; Func runs: 10000; result: 0.7900044352090774
    25 Hilly's; Func runs: 10000; result: 0.6513385092404853
    500 Hilly's; Func runs: 10000; result: 0.2896548031738138
    =============================
    5 Forest's; Func runs: 10000; result: 0.8715768614282637
    25 Forest's; Func runs: 10000; result: 0.5682316842631675
    500 Forest's; Func runs: 10000; result: 0.18876552479611478
    =============================
    5 Megacity's; Func runs: 10000; result: 0.6107692307692306
    25 Megacity's; Func runs: 10000; result: 0.46061538461538465
    500 Megacity's; Func runs: 10000; result: 0.12398461538461655
    =============================
    All score: 4.55494 (50.61%)

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

    Hilly

    FBA на тестовой функции Hilly

    Forest

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

    Megacity

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

    По результатам тестирования алгоритм FBA занимает 29 место в нашей рейтинговой таблице.

    AO Description Hilly Hilly
    Final
    Forest Forest
    Final
    Megacity (discrete) Megacity
    Final
    Final
    Result
    % of
    MAX
    10 p (5 F)50 p (25 F)1000 p (500 F)10 p (5 F)50 p (25 F)1000 p (500 F)10 p (5 F)50 p (25 F)1000 p (500 F)
    1ANSacross neighbourhood search0,949480,847760,438572,235811,000000,923340,399882,323230,709230,634770,230911,574916,13468,15
    2CLAcode lock algorithm (joo)0,953450,871070,375902,200420,989420,917090,316422,222940,796920,693850,193031,683806,10767,86
    3AMOmanimal migration ptimization M0,903580,843170,462842,209590,990010,924360,465982,380340,567690,591320,237731,396755,98766,52
    4(P+O)ES(P+O) evolution strategies0,922560,881010,400212,203790,977500,874900,319452,171850,673850,629850,186341,490035,86665,17
    5CTAcomet tail algorithm (joo)0,953460,863190,277702,094350,997940,857400,339492,194840,887690,564310,105121,557125,84664,96
    6TETAtime evolution travel algorithm (joo)0,913620,823490,319902,057010,970960,895320,293242,159520,734620,685690,160211,580525,79764,41
    7SDSmstochastic diffusion search M0,930660,854450,394762,179880,999830,892440,196192,088460,723330,611000,106701,441035,70963,44
    8BOAmbilliards optimization algorithm M0,957570,825990,252352,035901,000000,900360,305022,205380,735380,525230,095631,356255,59862,19
    9AAmarchery algorithm M0,917440,708760,421602,047800,925270,758020,353282,036570,673850,552000,237381,463235,54861,64
    10ESGevolution of social groups (joo)0,999060,796540,350562,146161,000000,828630,131021,959650,823330,553000,047251,423585,52961,44
    11SIAsimulated isotropic annealing (joo)0,957840,842640,414652,215130,982390,795860,205071,983320,686670,493000,090531,270205,46960,76
    12ACSartificial cooperative search0,755470,747440,304071,806981,000000,888610,224132,112740,690770,481850,133221,305835,22658,06
    13DAdialectical algorithm0,861830,700330,337241,899400,981630,727720,287181,996530,703080,452920,163671,319675,21657,95
    14BHAmblack hole algorithm M0,752360,766750,345831,864930,935930,801520,271772,009230,650770,516460,154721,321955,19657,73
    15ASOanarchy society optimization0,848720,746460,314651,909830,961480,791500,238031,991010,570770,540620,166141,277525,17857,54
    16RFOroyal flush optimization (joo)0,833610,737420,346291,917330,894240,738240,240981,873460,631540,502920,164211,298675,08956,55
    17AOSmatomic orbital search M0,802320,704490,310211,817020,856600,694510,219961,771070,746150,528620,143581,418355,00655,63
    18TSEAturtle shell evolution algorithm (joo)0,967980,644800,296721,909490,994490,619810,227081,841390,690770,426460,135981,253225,00455,60
    19DEdifferential evolution0,950440,616740,303081,870260,953170,788960,166521,908650,786670,360330,029531,176534,95555,06
    20SRAsuccessful restaurateur algorithm (joo)0,968830,634550,292171,895550,946370,555060,191241,692670,749230,440310,125261,314804,90354,48
    21CROchemical reaction optimisation0,946290,661120,298531,905930,879060,584220,211461,674730,758460,426460,126861,311784,89254,36
    22BIOblood inheritance optimization (joo)0,815680,653360,308771,777810,899370,653190,217601,770160,678460,476310,139021,293784,84253,80
    23BSAbird swarm algorithm0,893060,649000,262501,804550,924200,711210,249391,884790,693850,326150,100121,120124,80953,44
    24HSharmony search0,865090,687820,325271,878180,999990,680020,095901,775920,620000,422670,054581,097254,75152,79
    25SSGsaplings sowing and growing0,778390,649250,395431,823080,859730,624670,174291,658690,646670,441330,105981,193984,67651,95
    26BCOmbacterial chemotaxis optimization M0,759530,622680,314831,697040,893780,613390,225421,732590,653850,420920,144351,219124,64951,65
    27ABOafrican buffalo optimization0,833370,622470,299641,755480,921700,586180,197231,705110,610000,431540,132251,173784,63451,49
    28(PO)ES(PO) evolution strategies0,790250,626470,429351,846060,876160,609430,195911,681510,590000,379330,113221,082554,61051,22
    29FBAfractal-based Algorithm0,790000,651340,289651,730990,871580,568230,188771,628580,610770,460620,123981,195374,55550,61
    30TSmtabu search M0,877950,614310,291041,783300,928850,518440,190541,637830,610770,382150,121571,114494,53650,40
    31BSObrain storm optimization0,937360,576160,296881,810410,931310,558660,235371,725340,552310,290770,119140,962224,49849,98
    32WOAmwale optimization algorithm M0,845210,562980,262631,670810,931000,522780,163651,617430,663080,411380,113571,188034,47649,74
    33AEFAartificial electric field algorithm0,877000,617530,252351,746880,927290,726980,180641,834900,666150,116310,095080,877544,45949,55
    34AEOartificial ecosystem-based optimization algorithm0,913800,467130,264701,645630,902230,437050,214001,553270,661540,308000,285631,255174,45449,49
    35ACOmant colony optimization M0,881900,661270,303771,846930,858730,586800,150511,596040,596670,373330,024720,994724,43849,31
    36BFO-GAbacterial foraging optimization - ga0,891500,551110,315291,757900,969820,396120,063051,428990,726670,275000,035251,036924,22446,93
    37SOAsimple optimization algorithm0,915200,469760,270891,655850,896750,374010,169841,440600,695380,280310,108521,084224,18146,45
    38ABHAartificial bee hive algorithm0,841310,542270,263041,646630,878580,477790,171811,528180,509230,338770,103970,951974,12745,85
    39ACMOatmospheric cloud model optimization0,903210,485460,304031,692700,802680,378570,191781,373030,623080,244000,107950,975034,04144,90
    40ADAMmadaptive moment estimation M0,886350,447660,266131,600140,844970,384930,168891,398800,661540,270460,105941,037944,03744,85
    41CGOchaos game optimization0,572560,371580,320181,264320,611760,619310,621611,852670,375380,219230,190280,784903,90243,35
    42ATAmartificial tribe algorithm M0,717710,553040,252351,523100,824910,559040,204731,588670,440000,186150,094110,720263,83242,58
    43CROmcoral reefs optimization M0,785120,460320,259581,505020,866880,352970,162671,382520,632310,267380,107341,007033,89543,27
    44CFOcentral force optimization0,609610,549580,278311,437500,634180,468330,225411,327920,572310,234770,095860,902943,66840,76
    45ASHAartificial showering algorithm0,896860,404330,256171,557370,803600,355260,191601,350460,476920,181230,097740,755893,66440,71
    RWneuroboids optimization algorithm 2(joo)0,487540,321590,257811,066940,375540,219440,158770,753750,279690,149170,098470,527342,34826,09


    Выводы

    Версия алгоритма FBA с модифицированной мутацией, с результатами 50.61%, демонстрирует двукратное улучшение эффективности по сравнению с результатами оригинальной версии.

    FBA|Fractal-Based Algorithm|50.0|60.0|30.0|5.0|
    =============================
    5 Hilly's; Func runs: 10000; result: 0.4780639253626462
    25 Hilly's; Func runs: 10000; result: 0.3222029113692554
    500 Hilly's; Func runs: 10000; result: 0.25720991988937014
    =============================
    5 Forest's; Func runs: 10000; result: 0.36567166223346415
    25 Forest's; Func runs: 10000; result: 0.22043205527307377
    500 Forest's; Func runs: 10000; result: 0.15869146061036057
    =============================
    5 Megacity's; Func runs: 10000; result: 0.2861538461538461
    25 Megacity's; Func runs: 10000; result: 0.15015384615384622
    500 Megacity's; Func runs: 10000; result: 0.09838461538461622
    =============================
    All score: 2.33696 (25.97%)

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

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

    Tab

    Рисунок 2. Цветовая градация алгоритмов по соответствующим тестам

    chart

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

    Плюсы и минусы алгоритма FBA:

    Плюсы:

    1. Стабильные результаты на функциях средней и высокой размерности.

    Минусы:

    1. Большой разброс результатов на функциях малой размерности.
    2. Интересная, но достаточно "слабая" базовая идея алгоритма.

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


    Программы, используемые в статье

    #ИмяТипОписание
    1#C_AO.mqh
    Включаемый файл
    Родительский класс популяционных алгоритмов оптимизации
    2#C_AO_enum.mqh
    Включаемый файл
    Перечисление популяционных алгоритмов оптимизации
    3TestFunctions.mqh
    Включаемый файл
    Библиотека тестовых функций
    4
    TestStandFunctions.mqh
    Включаемый файл
    Библиотека функций тестового стенда
    5
    Utilities.mqh
    Включаемый файл
    Библиотека вспомогательных функций
    6
    CalculationTestResults.mqh
    Включаемый файл
    Скрипт для расчета результатов в сравнительную таблицу
    7
    Testing AOs.mq5
    СкриптЕдиный испытательный стенд для всех популяционных алгоритмов оптимизации
    8
    Simple use of population optimization algorithms.mq5
    Скрипт
    Простой пример использования популяционных алгоритмов оптимизации без визуализации
    9
    Test_FBA.mq5
    СкриптИспытательный стенд для FBA
    Прикрепленные файлы |
    FBA.ZIP (274.96 KB)
    Скрытые марковские модели в торговых системах на машинном обучении Скрытые марковские модели в торговых системах на машинном обучении
    Скрытые марковские модели (СММ) представляют собой мощный класс вероятностных моделей, предназначенных для анализа последовательных данных, где наблюдаемые события зависят от некоторой последовательности ненаблюдаемых (скрытых) состояний, которые формируют марковский процесс. Основные предположения СММ включают марковское свойство для скрытых состояний, означающее, что вероятность перехода в следующее состояние зависит только от текущего состояния, и независимость наблюдений при условии знания текущего скрытого состояния.
    Возможности Мастера MQL5, которые вам нужно знать (Часть 41): Сети Deep-Q Возможности Мастера MQL5, которые вам нужно знать (Часть 41): Сети Deep-Q
    Сеть Deep-Q (Deep-Q-Network) — это алгоритм обучения с подкреплением, который вовлекает нейронные сети в прогнозирование следующего значения Q и идеального действия в процессе обучения модуля машинного обучения. Мы уже рассматривали альтернативный алгоритм обучения с подкреплением — Q-обучение. Таким образом, в данной статье представлен еще один пример того, как многослойный перцептрон (multi-layer perceptron, MLP), обученный с помощью обучения с подкреплением, может использоваться в пользовательском классе сигналов.
    Нейросети в трейдинге: Прогнозирование временных рядов при помощи адаптивного модального разложения (Окончание) Нейросети в трейдинге: Прогнозирование временных рядов при помощи адаптивного модального разложения (Окончание)
    В статье рассматривается адаптация и практическая реализация фреймворка ACEFormer средствами MQL5 в контексте алгоритмической торговли. Показаны ключевые архитектурные решения, особенности обучения и результаты тестирования модели на реальных данных.
    Арбитражный трейдинг Forex: Матричная торговая система на возврат к справедливой стоимости с ограничением риска Арбитражный трейдинг Forex: Матричная торговая система на возврат к справедливой стоимости с ограничением риска
    Статья содержит детальное описание алгоритма расчета кросс-курсов, визуализацию матрицы дисбалансов и рекомендации по оптимальной настройке параметров MinDiscrepancy и MaxRisk для эффективной торговли. Система автоматически рассчитывает "справедливую стоимость" каждой валютной пары через кросс-курсы, генерируя сигналы на покупку при отрицательных отклонениях, и на продажу — при положительных.