preview
Оптимизация хаотичной игрой — Chaos Game Optimization (CGO)

Оптимизация хаотичной игрой — Chaos Game Optimization (CGO)

MetaTrader 5Тестер |
346 0
Andrey Dik
Andrey Dik

Содержание

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


Введение

Алгоритмы оптимизации играют стратегическую роль в решении сложных задач не только в различных областях современной науки, но и в трейдинге. С быстрым развитием технологий эти задачи становятся еще более сложными, а поиск лучшего решения — все более энергозатратным, поэтому возрастают и требования к алгоритмам оптимизации, их результативности при высокой экономичности работы. Одним из новейших и перспективных методов стал алгоритм Chaos Game Optimization (CGO), разработанный Сиамаком Талатахари и Мехди Азизи в 2020 году. Этот алгоритм основан на принципах теории хаоса и использует хаотические последовательности для генерации и улучшения решений.

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


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

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

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

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

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

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

cgo-illustration

Рисунок 1. Типичные действия поискового агента в алгоритме CGO

На рисунке 1 красная точка — текущий агент, для которого вычисляется новая позиция. Синие точки — группа случайно выбранных агентов в популяции в случайно выбранном количестве. Фиолетовая пунктирная окружность — средняя позиция группы. Золотая точка — лучшее найденное решение. Зеленые точки — возможные новые позиции агента по разным формулам:
  • Formula 1: текущая + α(β×лучшая - γ×средняя)
  • Formula 2: лучшая + α(β×средняя - γ×текущая)
  • Formula 3: средняя + α(β×лучшая - γ×текущая)
  • Random: случайная позиция

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

    Приступим к написанию псевдокода алгоритма.

  1. ИНИЦИАЛИЗАЦИЯ АЛГОРИТМА:
    • Задать размер популяции (по умолчанию 50 агентов)
    • Определить границы поиска для каждой координаты:
      • минимальные значения (rangeMin)
      • максимальные значения (rangeMax)
      • шаг изменения (rangeStep)
    • Для каждого агента в популяции:
      • сгенерировать случайные начальные координаты в заданных границах
      • округлить координаты с учетом шага
      • вычислить значение целевой функции
    • Определить лучшее начальное решение среди всех агентов
  2. ОСНОВНОЙ ЦИКЛ ОПТИМИЗАЦИИ:
    • Для каждого агента в популяции: 
    a) Выбрать случайную подгруппу агентов:
    • размер подгруппы = случайное число от 1 до размера популяции
    • случайно выбрать агентов в подгруппу
    b) Вычислить среднюю позицию выбранной подгруппы:
    • для каждой координаты: средняя_координата = сумма_координат_группы / размер_группы
    c) Сгенерировать коэффициенты для формул движения:
    • α (альфа) = выбрать случайно один из способов:
      • способ 1: случайное число от 0 до 1
      • способ 2: 2 × случайное(0,1) - 1 [получаем число от -1 до 1]
      • способ 3: Ir × случайное(0,1) + 1
      • способ 4: Ir × случайное(0,1) + (1-Ir) где Ir = случайно 0 или 1
    • β (бета) = случайно 1 или 2
    • γ (гамма) = случайно 1 или 2
    d) Выбрать случайно одну из четырех формул движения:
    • Формула 1: новая_позиция = текущая + α(β×лучшая - γ×средняя)
    • Формула 2: новая_позиция = лучшая + α(β×средняя - γ×текущая)
    • Формула 3: новая_позиция = средняя + α(β×лучшая - γ×текущая)
    • Формула 4: новая_позиция = случайная позиция в границах поиска
    e) Применить выбранную формулу:
    • для каждой координаты:
      • вычислить новое значение по формуле
      • проверить выход за границы поиска
      • если вышли за границы, скорректировать до ближайшей границы
      • округлить значение с учетом шага изменения
    f) Вычислить значение целевой функции в новой позиции
  3. ОБНОВЛЕНИЕ ЛУЧШЕГО РЕШЕНИЯ:
    • Для каждого агента:
      • если значение целевой функции агента лучше текущего лучшего:
        • обновить лучшее значение
        • сохранить координаты нового лучшего решения
  4. ПОВТОРЕНИЕ:
    • Повторять шаги 2-3 пока не выполнится условие остановки:
      • достигнуто максимальное число итераций
      • или найдено решение требуемого качества
      • или другой критерий остановки
  5. Переходим к самой реализации алгоритма. Класс "C_AO_CGO" реализует алгоритм CGO и является производным от класса "C_AO", в нем наследуются свойства и методы базового класса. 

    Методы:

    • SetParams ()  — устанавливает значение "popSize" на основе данных в массиве параметров "params". Это важно для настройки алгоритма перед его использованием.
    • Init ()  — метод инициализации, принимает минимальные и максимальные значения диапазона, шаг изменения и количество эпох. Его цель — подготовить алгоритм к запуску, задавая границы поиска и другие параметры.
    • Moving ()  — описывает шаги, связанные с движением особей в процессе оптимизации. Его реализация обеспечивает логику альтернативных решений и их улучшение.
    • Revision ()  — отвечает за пересмотр текущих по популяции и глобально лучшего решения.

    Приватные методы:

    • GetAlpha () — для получения параметра alpha, который используется для контроля стратегии, "интенсивности" и "разнообразия" поиска.
    • GenerateNewSolution () — для генерации нового решения на основе индекса (seedIndex) и среднего значения группы (meanGroup). 
    class C_AO_CGO : public C_AO
    {
      public: //--------------------------------------------------------------------
      ~C_AO_CGO () { }
      C_AO_CGO ()
      {
        ao_name = "CGO";
        ao_desc = "Chaos Game Optimization";
        ao_link = "https://www.mql5.com/ru/articles/17047";
    
        popSize = 25;
    
        ArrayResize (params, 1);
        params [0].name = "popSize"; params [0].val = popSize;
      }
    
      void SetParams ()
      {
        popSize = (int)params [0].val;
      }
    
      bool Init (const double &rangeMinP  [],  // минимальные значения
                 const double &rangeMaxP  [],  // максимальные значения
                 const double &rangeStepP [],  // шаг изменения
                 const int     epochsP = 0);   // количество эпох
    
      void Moving ();
      void Revision ();
    
      private: //-------------------------------------------------------------------
      double GetAlpha ();
      void   GenerateNewSolution (int seedIndex, double &meanGroup []);
    };
    //——————————————————————————————————————————————————————————————————————————————

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

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

    Метод "Moving" выполняет основную логику перемещения особей популяции решений в алгоритме CGO. Основная цель этого метода — обновить решения в популяции на основе правил, включая генерацию новых решений и усреднение результатов. Давайте подробно рассмотрим его основные части.

    Первая часть, инициализация при первом вызове (если переменная "revision" равна "false"):

    • Запускается внешний цикл по всем элементам популяции (popSize) и для каждого элемента (индивидуума i):
      • Запускается внутренний цикл по координатам (coords):
      • Генерируется случайное значение для каждой координаты с помощью метода u.RNDfromCI (), который возвращает случайное значение в заданном диапазоне.
      • Это значение затем корректируется с помощью u.SeInDiSp (), что гарантирует, что значение остаётся в пределах диапазона, и округляет его к ближайшему шагу.
      • Устанавливается флаг "revision" в "true" для следующего вызова метода и выходим из метода.
      Вторая часть, обновление популяции (если переменная "revision" установлена в "true"):
      • Для каждого индивидуума популяции:
        • Генерируется случайный размер группы "randGroupSize" от "1" до "popSize".
        • Создается массив "meanGroup" для хранения среднего значения координат, размер которого соответствует количеству координат, устанавливается в "coords".
        • Заполняется массив "randIndices", содержащий случайные индексы (индивидов), которые будут использованы для формирования группы.
        • На каждой итерации добавляются случайные индексы в "randIndices", при этом индексы выбираются случайным образом.
        • Затем для каждой группы суммируются значения координат для каждого индивидуума из случайно выбранных индексов, и результат сохраняется в "meanGroup".
        • После суммирования, значение в "meanGroup" делится на количество индивидов в группе для получения среднего значения.
        • Генерируется новое решение для индивидуума "i" на основе среднего значения группы с помощью метода "GenerateNewSolution ()".
      //——————————————————————————————————————————————————————————————————————————————
      void C_AO_CGO::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;
        }
      
        //----------------------------------------------------------------------------
        for (int i = 0; i < popSize; i++)
        {
          int randGroupSize = u.RNDminusOne (popSize) + 1;
          double meanGroup [];
          ArrayResize (meanGroup, coords);
          ArrayInitialize (meanGroup, 0);
      
          int randIndices [];
          ArrayResize (randIndices, randGroupSize);
      
          for (int j = 0; j < randGroupSize; j++) randIndices [j] = u.RNDminusOne (popSize);
      
          for (int j = 0; j < randGroupSize; j++)
          {
            for (int c = 0; c < coords; c++)
            {
              meanGroup [c] += a [randIndices [j]].c [c];
            }
          }
          for (int c = 0; c < coords; c++) meanGroup [c] /= randGroupSize;
      
          GenerateNewSolution (i, meanGroup);
        }
      }
      //——————————————————————————————————————————————————————————————————————————————

      Метод "Revision" выполняет обновление лучших решений в популяции. Проходит по всем индивидам в популяции циклом и для каждого индивидуума проверяет, если его значение функции приспособленности "f" больше текущего лучшего значения "fB", и если это так, обновляет "fB" новым значением "f" и копирует координаты c текущего индивидуума в массив "cB". Таким образом, метод "Revision" находит и обновляет лучшее известное решение в популяции на основе значений функции приспособленности.

      //——————————————————————————————————————————————————————————————————————————————
      void C_AO_CGO::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);
          }
        }
      }
      //——————————————————————————————————————————————————————————————————————————————

      Метод "GetAlpha" генерирует и возвращает случайное значение "alpha" в зависимости от случайных условий выбора.

      • Ir   — случайное значение, равное "0" или "1".
      • Существует четыре возможных случая (имеющихся в "case"), в каждом из которых генерируется значение "alpha" по соответствующей формуле:
        • Случай 0: Генерирует значение от 0 до 1.
        • Случай 1: Генерирует значение от -1 до 1.
        • Случай 2: Генерирует значение от 1 до 2, умножая его на "Ir" (0 или 1).
        • Случай 3: Генерирует значение, которое зависит от "Ir" и имеет диапазон от 0 до 1, либо 1, в зависимости от значения "Ir".
      Отметим, что выражения, используемые для генерации числа "alpha" можно было бы использовать в более простом виде, но используется оригинальный вид, соответсвующий формулам авторов алгоритма.
        //——————————————————————————————————————————————————————————————————————————————
        double C_AO_CGO::GetAlpha ()
        {
          int Ir = u.RNDminusOne (2);
        
          switch (u.RNDminusOne (4))
          {
            case 0: return u.RNDfromCI (0, 1);
            case 1: return 2 * u.RNDfromCI (0, 1) - 1;
            case 2: return Ir * u.RNDfromCI (0, 1) + 1;
            case 3: return Ir * u.RNDfromCI (0, 1) + (1 - Ir);
          }
          return 0;
        }
        //——————————————————————————————————————————————————————————————————————————————

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

        Инициализация параметров:
        • alpha — значение, полученное вызовом метода "GetAlpha ()", используемое для влияния на новое положение.
        • beta и gamma — случайные значения (1 или 2).
        Выбор формулы:
        • formula — случайно выбирается одно из четырех возможных уравнений, по которым будет рассчитано новое положение.
        Цикл по координатам: для каждой координаты (от 0 до coords):
        • newPos — переменная для хранения нового положения с использованием выбранной формулы.
        • В зависимости от значения "formula":
          • Случай 0: новое положение рассчитывается как текущее положение агента с добавлением комбинации координат из "cB" (лучшее решение по популяции) и "meanGroup".
          • Случай 1: новое положение рассчитывается с использованием координаты из "cB" и среднего значения "meanGroup".
          • Случай 2: новое положение определяется по среднему значению и координате текущего агента.
          • Случай 3: новое положение задается случайным образом в пределах заданного диапазона (rangeMin [c] и rangeMax [c]).
        Коррекция нового положения:
        • a [seedIndex].c [c]   — соответсвующая координата агента обновляется с использованием метода "u.SeInDiSp ()", который учитывает минимальные значения, максимальные значения и шаги, чтобы гарантировать, что новое значение находится в допустимых пределах.
        //——————————————————————————————————————————————————————————————————————————————
        void C_AO_CGO::GenerateNewSolution (int seedIndex, double &meanGroup [])
        {
          double alpha = GetAlpha ();
          int    beta  = u.RNDminusOne (2) + 1;
          int    gamma = u.RNDminusOne (2) + 1;
        
          int formula = u.RNDminusOne (4);
        
          for (int c = 0; c < coords; c++)
          {
            double newPos = 0;
        
            switch (formula)
            {
              case 0:
                newPos = a [seedIndex].c [c] + alpha * (beta * cB [c] - gamma * meanGroup [c]);
                break;
        
              case 1:
                newPos = cB [c] + alpha * (beta * meanGroup [c] - gamma * a [seedIndex].c [c]);
                break;
        
              case 2:
                newPos = meanGroup [c] + alpha * (beta * cB [c] - gamma * a [seedIndex].c [c]);
                break;
        
              case 3:
                newPos = u.RNDfromCI (rangeMin [c], rangeMax [c]);
                break;
            }
        
            a [seedIndex].c [c] = u.SeInDiSp (newPos, rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
        //——————————————————————————————————————————————————————————————————————————————

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

        double rnd = u.RNDprobab();                             // Получаем случайное число от 0.0 до 1.0
        rnd *= rnd;                                             // Возводим его в квадрат
        int ind = (int)u.Scale(rnd, 0.0, 1.0, 0, popSize - 1);  // Масштабируем в индекс
        a[seedIndex].c [c] = a[ind].c [c];                      // Копируем координату из другого агента с полученным индексом

        Происходит копирование координаты из случайно выбранного агента, и выбор агента происходит не равномерно, а с квадратичным распределением вероятности (rnd *= rnd). Это создает "bias" в сторону выбора агентов с меньшими индексами (лучшие решения имеют большую вероятность быть выбранными). Случайное число возводим в квадрат, тем самым создаем неравномерное распределение, масштабируем в диапазон индексов популяции и затем копируем, до применения основных формул движения. Моя нацеленность на попытку ускорить конвергенцию в перспективных областях, к сожалению не принесла ожидаемого эффекта.

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

        //——————————————————————————————————————————————————————————————————————————————
        void C_AO_CGO::GenerateNewSolution (int seedIndex, double &meanGroup [])
        {
          double alpha = GetAlpha ();
          int    beta  = u.RNDminusOne (2) + 1;
          int    gamma = u.RNDminusOne (2) + 1;
        
          int formula = u.RNDminusOne (4);
        
          for (int c = 0; c < coords; c++)
          {
            double rnd = u.RNDprobab ();
            rnd *= rnd;
            int ind = (int)u.Scale (rnd, 0.0, 1.0, 0, popSize - 1);
            a [seedIndex].c [c] = a [ind].c [c];
        
            double newPos = 0;
        
            switch (formula)
            {
              case 0:
                newPos = a [seedIndex].c [c] + alpha * (beta * cB [c] - gamma * meanGroup [c]);
                break;
        
              case 1:
                newPos = cB [c] + alpha * (beta * meanGroup [c] - gamma * a [seedIndex].c [c]);
                break;
        
              case 2:
                newPos = meanGroup [c] + alpha * (beta * cB [c] - gamma * a [seedIndex].c [c]);
                break;
        
              case 3:
                newPos = u.RNDfromCI (rangeMin [c], rangeMax [c]);
                break;
            }
        
            a [seedIndex].c [c] = u.SeInDiSp (newPos, rangeMin [c], rangeMax [c], rangeStep [c]);
          }
        }
        //——————————————————————————————————————————————————————————————————————————————


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

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

        CGO|Chaos Game Optimization|50.0|
        =============================
        5 Hilly's; Func runs: 10000; result: 0.5725597668122144
        25 Hilly's; Func runs: 10000; result: 0.3715760642098293
        500 Hilly's; Func runs: 10000; result: 0.32017971142744234
        =============================
        5 Forest's; Func runs: 10000; result: 0.6117551660766816
        25 Forest's; Func runs: 10000; result: 0.619308424855028
        500 Forest's; Func runs: 10000; result: 0.6216109945434442
        =============================
        5 Megacity's; Func runs: 10000; result: 0.3753846153846153
        25 Megacity's; Func runs: 10000; result: 0.2192307692307692
        500 Megacity's; Func runs: 10000; result: 0.19028461538461647
        =============================
        All score: 3.90189 (43.35%)

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

        Hilly

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

        Forest

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

        Megacity

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

        По итогам тестирования алгоритм CGO занимает 38-ю позицию в рейтинговой таблице популяционных алгоритмов оптимизации.

        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
        8AAmarchery algorithm M0,917440,708760,421602,047800,925270,758020,353282,036570,673850,552000,237381,463235,54861,64
        9ESGevolution of social groups (joo)0,999060,796540,350562,146161,000000,828630,131021,959650,823330,553000,047251,423585,52961,44
        10SIAsimulated isotropic annealing (joo)0,957840,842640,414652,215130,982390,795860,205071,983320,686670,493000,090531,270205,46960,76
        11ACSartificial cooperative search0,755470,747440,304071,806981,000000,888610,224132,112740,690770,481850,133221,305835,22658,06
        12DAdialectical algorithm0,861830,700330,337241,899400,981630,727720,287181,996530,703080,452920,163671,319675,21657,95
        13BHAmblack hole algorithm M0,752360,766750,345831,864930,935930,801520,271772,009230,650770,516460,154721,321955,19657,73
        14ASOanarchy society optimization0,848720,746460,314651,909830,961480,791500,238031,991010,570770,540620,166141,277525,17857,54
        15RFOroyal flush optimization (joo)0,833610,737420,346291,917330,894240,738240,240981,873460,631540,502920,164211,298675,08956,55
        16AOSmatomic orbital search M0,802320,704490,310211,817020,856600,694510,219961,771070,746150,528620,143581,418355,00655,63
        17TSEAturtle shell evolution algorithm (joo)0,967980,644800,296721,909490,994490,619810,227081,841390,690770,426460,135981,253225,00455,60
        18DEdifferential evolution0,950440,616740,303081,870260,953170,788960,166521,908650,786670,360330,029531,176534,95555,06
        19CROchemical reaction optimisation0,946290,661120,298531,905930,879060,584220,211461,674730,758460,426460,126861,311784,89254,36
        20BIOblood inheritance optimization (joo)0,815680,653360,308771,777810,899370,653190,217601,770160,678460,476310,139021,293784,84253,80
        21BSAbird swarm algorithm0,893060,649000,262501,804550,924200,711210,249391,884790,693850,326150,100121,120124,80953,44
        22HSharmony search0,865090,687820,325271,878180,999990,680020,095901,775920,620000,422670,054581,097254,75152,79
        23SSGsaplings sowing and growing0,778390,649250,395431,823080,859730,624670,174291,658690,646670,441330,105981,193984,67651,95
        24BCOmbacterial chemotaxis optimization M0,759530,622680,314831,697040,893780,613390,225421,732590,653850,420920,144351,219124,64951,65
        25ABOafrican buffalo optimization0,833370,622470,299641,755480,921700,586180,197231,705110,610000,431540,132251,173784,63451,49
        26(PO)ES(PO) evolution strategies0,790250,626470,429351,846060,876160,609430,195911,681510,590000,379330,113221,082554,61051,22
        27TSmtabu search M0,877950,614310,291041,783300,928850,518440,190541,637830,610770,382150,121571,114494,53650,40
        28BSObrain storm optimization0,937360,576160,296881,810410,931310,558660,235371,725340,552310,290770,119140,962224,49849,98
        29WOAmwale optimization algorithm M0,845210,562980,262631,670810,931000,522780,163651,617430,663080,411380,113571,188034,47649,74
        30AEFAartificial electric field algorithm0,877000,617530,252351,746880,927290,726980,180641,834900,666150,116310,095080,877544,45949,55
        31AEOartificial ecosystem-based optimization algorithm0,913800,467130,264701,645630,902230,437050,214001,553270,661540,308000,285631,255174,45449,49
        32ACOmant colony optimization M0,881900,661270,303771,846930,858730,586800,150511,596040,596670,373330,024720,994724,43849,31
        33BFO-GAbacterial foraging optimization - ga0,891500,551110,315291,757900,969820,396120,063051,428990,726670,275000,035251,036924,22446,93
        34SOAsimple optimization algorithm0,915200,469760,270891,655850,896750,374010,169841,440600,695380,280310,108521,084224,18146,45
        35ABHAartificial bee hive algorithm0,841310,542270,263041,646630,878580,477790,171811,528180,509230,338770,103970,951974,12745,85
        36ACMOatmospheric cloud model optimization0,903210,485460,304031,692700,802680,378570,191781,373030,623080,244000,107950,975034,04144,90
        37ADAMmadaptive moment estimation M0,886350,447660,266131,600140,844970,384930,168891,398800,661540,270460,105941,037944,03744,85
        38CGOchaos game optimization0,572560,371580,320181,264320,611760,619310,621611,852670,375380,219230,190280,784903,90243,35
        39ATAmartificial tribe algorithm M0,717710,553040,252351,523100,824910,559040,204731,588670,440000,186150,094110,720263,83242,58
        40ASHAartificial showering algorithm0,896860,404330,256171,557370,803600,355260,191601,350460,476920,181230,097740,755893,66440,71
        41ASBOadaptive social behavior optimization0,763310,492530,326191,582020,795460,400350,260971,456770,264620,171690,182000,618313,65740,63
        42MECmind evolutionary computation0,695330,533760,326611,555690,724640,330360,071981,126980,525000,220000,041980,786983,47038,55
        43CSAcircle search algorithm0,665600,453170,291261,410030,687970,413970,205251,307190,375380,236310,106460,718153,43538,17
        44IWOinvasive weed optimization0,726790,522560,331231,580580,707560,339550,074841,121960,423330,230670,046170,700173,40337,81
        45Micro-AISmicro artificial immune system0,795470,519220,308611,623300,729560,368790,093981,192330,376670,158670,028020,563353,37937,54

        RWrandom walk0,487540,321590,257811,066940,375540,219440,158770,753750,279690,149170,098470,527342,34826,09


        Выводы

        Проанализировав результаты работы алгоритма CGO, я пришёл к важным выводам. Алгоритм Chaos Game Optimization демонстрирует крайне интересное поведение. В целом, его эффективность можно оценить как ниже среднего, что подтверждается общим результатом в 43.35%. Однако наиболее примечательным является его поведение при масштабировании задачи, CGO показывает высокую эффективность именно на многомерных задачах — тестах с размерностью в 1000 переменных. Это нетипичное свойство для большинства метаэвристических алгоритмов, которые обычно страдают от "проклятия размерности" и теряют эффективность при увеличении числа переменных. CGO же, напротив, иногда даже превосходит свои результаты на 10-и и 50-и мерных задачах при работе с 1000-мерными задачами. Особенно ярко это проявляется на тестовой функции Forest, имеющей глобальный экстремум в одной "острой" точке.

        Я полагаю, что этот феномен обусловлен самой природой алгоритма. Хаотическая основа CGO и разнообразие формул движения создают эффективный механизм исследования высокоразмерных пространств. Четыре различные стратегии обновления позиций, случайный выбор между ними и непредсказуемый коэффициент α, позволяют алгоритму решать задачи на сложных многомерных ландшафтах. Особенно хорошо алгоритм проявил себя на функциях типа Forest с результатами 0.61-0.62, что значительно выше его средних показателей.

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

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

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

        Tab

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

        Chart

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

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

        Плюсы:

        1. Отсутствуют внешние параметры
        2. Хорошая сходимость на функциях высокой и средней размерности

        Минусы:

        1. Застревает в локальных экстремумах на задачах малой размерности.

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


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

        #ИмяТипОписание
        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_AO_CGO.mq5
        СкриптИспытательный стенд для CGO
        Прикрепленные файлы |
        CGO.ZIP (167.34 KB)
        Анализ всех вариантов движения цены на квантовом компьютере IBM Анализ всех вариантов движения цены на квантовом компьютере IBM
        Используем квантовый компьютер от IBM для открытия всех вариантов движения цены. Звучит как научная фантастика? Добро пожаловать в мир квантовых вычислений для трейдинга!
        От начального до среднего уровня: Массивы и строки (III) От начального до среднего уровня: Массивы и строки (III)
        Эта статья посвящена рассмотрению двух аспектов. Во-первых, того, как стандартная библиотека может преобразовывать бинарные значения в другие формы представления, такие как восьмеричная, десятичная и шестнадцатеричная. А во-вторых, мы поговорим о том, как можно определить ширину нашего пароля на основе секретной фразы, используя уже полученные знания.
        Разрабатываем мультивалютный советник (Часть 24): Подключаем новую стратегию (I) Разрабатываем мультивалютный советник (Часть 24): Подключаем новую стратегию (I)
        В данной статье рассмотрим как нам подключить новую стратегию к созданной системе автоматической оптимизации. Посмотрим, какие советники нам понадобится создать и можно ли будет обойтись без изменений файлов библиотеки Advisor или свести необходимые изменения к минимуму.
        Объединение стратегий фундаментального и технического анализа на языке MQL5 для начинающих Объединение стратегий фундаментального и технического анализа на языке MQL5 для начинающих
        В этой статье обсудим, как эффективно интегрировать следование тренду и фундаментальные принципы в один советник для создания более надежной стратегии. Статья продемонстрирует, насколько просто любой желающий может приступить к созданию собственных торговых алгоритмов с помощью языка MQL5.