preview
Алгоритм конкурентного обучения — Competitive Learning Algorithm (CLA)

Алгоритм конкурентного обучения — Competitive Learning Algorithm (CLA)

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

Содержание

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


Введение

За последние десятилетия было предложено множество биоинспирированных алгоритмов: от муравьиных колоний и роев частиц до серых волков и китов. Однако человеческое общество, с его сложными социальными взаимодействиями, также может служить богатым источником идей для создания эффективных оптимизационных методов. Именно эта идея легла в основу алгоритма конкурентного обучения (Competitive Learning Algorithm, CLA).

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

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


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

Алгоритм Конкурентного Обучения (Competitive Learning Algorithm) основан на метафоре образовательного процесса в школе. В этой метафоре студенты представляют собой возможные решения задачи оптимизации, классы — это группы студентов, учителя — лучшие студенты в каждом классе, знания соответствуют координатам в пространстве поиска, а оценка определяется значением фитнес-функции.

Работа алгоритма начинается с инициализации, которую можно сравнить с началом учебного года. Создается популяция, к примеру, из 198 студентов, которые делятся на 9 классов по 22 студента в каждом. Каждому студенту случайным образом присваиваются начальные "знания" —координаты в пространстве поиска.

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

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

Алгоритм использует пять ключевых параметров:

  • popSize — определяет общее количество студентов,
  • numClasses — задает количество классов,
  • beta — регулирует влияние всех студентов класса на его общий рейтинг,
  • gamma — устанавливает вероятность обучения у других классов,
  • deltaIter — указывает, после какой итерации начинается расширенное обучение с использованием персонального опыта и межклассового взаимодействия.

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

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

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

CLA_L

Рисунок 1. Схема работы алгоритма

Иллюстрация работы алгоритма описывает три основных шага: инициализацию, фазу обучения и оценку с обновлением. Теперь приступим к написанию псевдокода алгоритма CLA_L.

ЧТО НАМ НУЖНО ЗАРАНЕЕ:
- 198 студентов (наши решения)
- 9 классов (группы для студентов)
- Параметр β = 0.9 (насколько важны все студенты в классе)
- Параметр γ = 0.5 (шанс учиться у других классов)
- После 4-й итерации включаем дополнительные виды обучения
- Функция для оценки качества решения

НАЧИНАЕМ:

1. СОЗДАЁМ ШКОЛУ:
   - Распределяем 198 студентов по 9 классам (по 22 в каждом)
   - Даём каждому студенту случайную начальную позицию в пространстве
   - В каждом классе выбираем лучшего студента - он становится учителем
   - Создаём журнал для записи истории каждого студента

2. УЧЕБНЫЙ ПРОЦЕСС (итерационный процесс) :
   
   Шаг 1: Определяем интенсивность обучения
   - Для каждого класса считаем, насколько активно нужно учиться
   - Слабые классы (с плохим рангом) учатся интенсивнее
   - Со временем все учатся менее интенсивно (как в реальной жизни)
   
   Шаг 2: Находим "среднего учителя"
   - Берём позиции всех учителей
   - Считаем их среднее значение
   - Это будет общешкольный эталон знаний
   
   Шаг 3: Каждый студент учится:
   
   Для каждого студента делаем:
   
   а) ВСЕГДА учимся у своего учителя:
      - Смотрим, где находится учитель нашего класса
      - Двигаемся в его сторону
      - Скорость движения зависит от фактора обучения класса
   
   б) После 4-й итерации - вспоминаем свой опыт:
      - Смотрим в журнал: где я был лучше всего за последние 4 урока?
      - С некоторой случайной силой возвращаемся к той позиции
      - Это помогает не забыть хорошие решения
   
   в) После 4-й итерации — учимся у других классов:
      - Бросаем монетку (с вероятностью 50%)
      - Если выпала решка — смотрим на "среднего учителя"
      - Немного двигаемся в его сторону
      - Это помогает обмениваться опытом между классами
   
   Шаг 4: Оцениваем всех студентов
   - Каждый студент получает оценку (fitness)
   - Чем лучше позиция, тем выше оценка
   
   Шаг 5: Обновляем школьную иерархию
   
   Для каждого класса:
   - Находим нового лучшего студента — он становится учителем
   - Считаем общий уровень класса:
     * Берём оценку учителя
     * Добавляем средний балл всех студентов (умноженный на β)
   - Определяем ранг класса:
     * Лучшие классы получают низкий ранг (1, 2, 3...)
     * Худшие классы получают высокий ранг
   
   Шаг 6: Запоминаем лучшее решение
   - Если какой-то учитель лучше нашего рекорда
   - Сохраняем его как новый рекорд
   
   Шаг 7: Записываем в историю
   - Сохраняем текущие позиции всех студентов
   - Это понадобится для персонального обучения

3. ЗАКАНЧИВАЕМ:
   - Возвращаем лучшее найденное решение
   - И его оценку

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

  • popSize — размер популяции (количество "агентов" или "учеников");
  • numClasses — количество классов, на которые разделены агенты;
  • beta — параметр, вероятно влияющий на скорость обучения или адаптации;
  • gamma — еще один параметр, скорее всего, связанный с обучением;
  • deltaIter — частота выполнения определенных шагов алгоритма (в итерациях).

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

Методы:

  • SetParams () — обновляет значения внутренних переменных класса (параметров алгоритма), используя значения из массива "params". Это позволяет изменять параметры, заданные вовне.
  • Init () — инициализирует алгоритм. Принимает параметры, для определения границ и шага изменения параметров.
  • Moving () — основной метод для "движения" агентов (выполнения итераций и шагов алгоритма).
  • Revision () — метод связан с коррекцией или улучшением решений.
  • Injection () — метод для "внедрения" (inserting) нового знания или данных в агента.
Поля класса:
  • numClasses, beta, gamma, deltaIter — параметры алгоритма.
  • currentIter, studentsPerClass, totalIters — внутренние переменные для управления ходом алгоритма.
  • teachers [] — массив структур "S_AO_Agent", представляющих "учителей" в каждом классе (опорные точки).
  • classRanks [] — ранги классов, отражающие их производительность.
  • classTotalCosts [] — общие затраты или "стоимость" каждого класса.
  • CL [] — временный массив для вычисления среднего знания учителей.
  • UpdateTeachersAndCosts () — внутренний метод для обновления информации об учителях и их стоимости.
  • UpdateClassRanks () — внутренний метод для обновления рангов классов.
  • UpdateStudentsKnowledge () — внутренний метод для обновления знаний или параметров "учеников".
//————————————————————————————————————————————————————————————————————
class C_AO_CLA_l : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_CLA_l () { }
  C_AO_CLA_l ()
  {
    ao_name = "CLA_L";
    ao_desc = "Competitive Learning Algorithm";
    ao_link = "https://www.mql5.com/ru/articles/18857";

    popSize        = 198;
    numClasses     = 3;
    beta           = 0.3;
    gamma          = 0.8;
    deltaIter      = 2;

    ArrayResize (params, 5);

    params [0].name = "popSize";     params [0].val = popSize;
    params [1].name = "numClasses";  params [1].val = numClasses;
    params [2].name = "beta";        params [2].val = beta;
    params [3].name = "gamma";       params [3].val = gamma;
    params [4].name = "deltaIter";   params [4].val = deltaIter;
  }

  void SetParams ()
  {
    popSize    = (int)params [0].val;
    numClasses = (int)params [1].val;
    beta       = params      [2].val;
    gamma      = params      [3].val;
    deltaIter  = (int)params [4].val;
  }

  bool Init (const double &rangeMinP  [],
             const double &rangeMaxP  [],
             const double &rangeStepP [],
             const int     epochsP = 0);

  void Moving   ();
  void Revision ();
  void Injection (const int popPos, const int coordPos, const double value) { }

  //------------------------------------------------------------------
  int    numClasses;
  double beta;
  double gamma;
  int    deltaIter;

  private: //---------------------------------------------------------
  int    currentIter;
  int    studentsPerClass;
  int    totalIters;

  // Структуры для классов
  S_AO_Agent teachers        [];
  double     classRanks      [];
  double     classTotalCosts [];

  // Временные массивы
  double     CL [];             // Среднее знание учителей

  // Вспомогательные методы
  void   UpdateTeachersAndCosts  ();
  void   UpdateClassRanks        ();
  void   UpdateStudentsKnowledge ();
};
//————————————————————————————————————————————————————————————————————

Метод "Init ()" класса "C_AO_CLA_l" выполняет инициализацию алгоритма перед началом работы. Логика работы разбита на несколько этапов. Standard Initialization: вызывается метод "StandardInit ()" и выполняет стандартную инициализацию для алгоритма оптимизации. Ему передаются границы параметров "rangeMinP", "rangeMaxP", и шаг "rangeStepP". Если стандартная инициализация не удалась, метод "Init ()" возвращает "false".

Initialization of Algorithm Parameters: сбрасывает счетчик текущей итерации в "0", устанавливает общее количество итераций, которое алгоритм должен выполнить, используя значение, переданное в аргументе "epochsP", вычисляет количество "студентов" (агентов) в каждом классе, деля размер популяции на количество классов. Далее метод проверяет, что в каждом классе есть хотя бы один студент. Далее цикл "for" инициализирует каждого агента в популяции. Для каждого агента в массиве "a" вызывается метод "Init ()". Метод предполагает инициализацию количества координат для каждого человека.

Цикл "for" инициализирует "учителя" для каждого класса, устанавливает ранг каждого класса равным "1.0", а также устанавливает начальную "стоимость" каждого класса в отрицательное максимальное значение, чтобы гарантировать, что впоследствии будет найдено лучшее решение. Если все этапы инициализации прошли успешно, метод возвращает "true".

//————————————————————————————————————————————————————————————————————
bool C_AO_CLA_l::Init (const double &rangeMinP  [],
                       const double &rangeMaxP  [],
                       const double &rangeStepP [],
                       const int epochsP = 0)
{
  if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;

  //------------------------------------------------------------------
  currentIter = 0;
  totalIters = epochsP;
  studentsPerClass = popSize / numClasses;

  if (studentsPerClass < 1)
  {
    Print ("Ошибка: слишком мало студентов на класс");
    return false;
  }

  // Корректировка размера популяции
  //spopSize = studentsPerClass * numClasses;
  ArrayResize (a, popSize);
  for (int i = 0; i < popSize; i++) a [i].Init (coords);

  // Инициализация структур классов
  ArrayResize (teachers, numClasses);
  ArrayResize (classRanks, numClasses);
  ArrayResize (classTotalCosts, numClasses);

  for (int i = 0; i < numClasses; i++)
  {
    teachers        [i].Init (coords);
    classRanks      [i] = 1.0;
    classTotalCosts [i] = -DBL_MAX;
  }

  // Временные массивы
  ArrayResize (CL, coords);

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

Метод "Moving ()" класса "C_AO_CLA_l" реализует одну итерацию основного алгоритма оптимизации, проверяет, является ли она первой итерацией и, если это первая итерация (то есть, "revision" имеет значение "false"), метод инициализирует положения всех агентов в популяции случайными значениями в заданном диапазоне. Перебирает всех агентов в популяции и для каждого агента перебирает все его координаты.

Для каждой координаты метод генерирует случайное значение "val" в диапазоне от "rangeMin [c]" до "rangeMax [c]" с использованием метода "u.RNDfromCI ()", предоставляющего функцию для генерации случайных чисел. Присваивает координате "a [i].c [c]" агента "i" значение "val", предварительно "обрезав" его, чтобы он находился в допустимом диапазоне и соответствовал заданному шагу "rangeStep [c]". Используется метод "u.SeInDiSp ()", который выполняет проверку и корректировку значения.

После инициализации популяции, устанавливает флаг, чтобы указать, что инициализация была выполнена. Если это не первая итерация, увеличивает счетчик текущей итерации. Вызывает метод "UpdateStudentsKnowledge ()", который реализует основной механизм обучения или адаптации агентов в соответствии с принципами конкурентного обучения, используемого в алгоритме "CLA_L". Логика этого метода определяет, как агенты взаимодействуют, обмениваются информацией и улучшают свои позиции в пространстве поиска решений.

//————————————————————————————————————————————————————————————————————
void C_AO_CLA_l::Moving ()
{
  // Начальная инициализация популяции
  if (!revision)
  {
    for (int i = 0; i < popSize; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        double val = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        a [i].c [c] = u.SeInDiSp (val, rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }
    revision = true;
    return;
  }

  currentIter++;

  // Обновление знаний студентов
  UpdateStudentsKnowledge ();
}
//————————————————————————————————————————————————————————————————————

Метод "UpdateStudentsKnowledge ()" отвечает за обновление позиции каждого ученика (агента) в процессе оптимизации. Вычисляется текущий прогресс оптимизации. Рассчитываются два ключевых фактора обучения:

  • TF  (Teaching Factor) — показывает, насколько ученик учится у учителя.
  • CF  (Confirmatory Factor) — показывает, насколько ученик учитывает средние знания учителей. Эти факторы зависят от прогресса и ранга класса ученика.

Далее вычисляется среднее значение координат всех "активных" учителей (учителей, чьи результаты валидны). Это среднее знание хранится во временном массиве. Для каждого ученика (агента) определяется класс, к которому принадлежит ученик, а для каждой координаты ученика вычисляется новая позиция ученика, суммируя текущую координату с "источниками знаний". Ученик учится у своего учителя, корректируя свою позицию на величину, пропорциональную разнице между координатой учителя и своей собственной, умноженную на TF. Если прошло достаточно итераций (currentIter > deltaIter) и у ученика есть "лучшее решение", то ученик учится на своем предыдущем успешном опыте, корректируя свою позицию в направлении лучшей координаты, умножая на случайный фактор.

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

//————————————————————————————————————————————————————————————————————
void C_AO_CLA_l::UpdateStudentsKnowledge ()
{
  // Расчет факторов обучения для текущей итерации
  double progress = (double)currentIter / (double)MathMax (totalIters, 100);

  // Вычисление среднего знания всех учителей (CL)
  ArrayInitialize (CL, 0.0);
  int validTeachers = 0;

  for (int k = 0; k < numClasses; k++)
  {
    // Проверяем, что учитель инициализирован
    if (teachers [k].f > -DBL_MAX)
    {
      for (int c = 0; c < coords; c++)
      {
        CL [c] += teachers [k].c [c];
      }
      validTeachers++;
    }
  }

  if (validTeachers > 0)
  {
    for (int c = 0; c < coords; c++)
    {
      CL [c] /= validTeachers;
    }
  }

  // Обновление каждого студента
  for (int i = 0; i < popSize; i++)
  {
    int classIdx = i / studentsPerClass;

    // Teaching Factor - уменьшается с прогрессом
    double TF = MathExp (-0.6 * progress * classRanks [classIdx]);
    TF = MathMax (0.1, MathMin (1.0, TF));

    // Confirmatory Factor
    double CF = MathExp (-0.5 * progress * classRanks [classIdx]);
    CF = MathMax (0.1, MathMin (1.0, CF));

    // Обновление позиции студента
    for (int c = 0; c < coords; c++)
    {
      double newPos = a [i].c [c];

      // a) Teacher Learning - учимся у учителя класса
      if (teachers [classIdx].f > -DBL_MAX)
      {
        newPos += TF * (teachers [classIdx].c [c] - a [i].c [c]);
      }

      // b) Personal Learning - учимся у своего лучшего решения
      if (currentIter > deltaIter && a [i].fB > -DBL_MAX)
      {
        double PF = u.RNDprobab ();
        newPos += PF * (a [i].cB [c] - a [i].c [c]);
      }

      // c) Confirmatory Learning - учимся у среднего всех учителей
      if (currentIter > deltaIter && validTeachers > 0)
      {
        double rnd = u.RNDprobab ();
        if (rnd >= gamma) // Участвует с вероятностью (1-gamma)
        {
          newPos += CF * (CL [c] - a [i].c [c]);
        }
      }

      // Применение границ
      a [i].c [c] = u.SeInDiSp (newPos, rangeMin [c], rangeMax [c], rangeStep [c]);
    }
  }
}
//————————————————————————————————————————————————————————————————————

Метод "Revision ()" в классе "C_AO_CLA_l" служит для обновления информации о лучших решениях каждого агента, а также для обновления глобального лучшего решения и соответствующих параметров.

Для каждого агента проверяется, улучшилась ли его текущая функция цели (a [i].f) по сравнению с его запомненным лучшим значением (a [i].fB). Если да, то лучшее значение сохраняется, и копируются соответствующие координаты. После проверки всех агентов, если у какого-либо из них получилось лучшее значение функции, чем текущий глобальный максимум (fB), обновляются глобальные параметры: fB — глобальное лучшее значение функции и cB — координаты этого лучшего решения.

Далее вызывается метод "UpdateTeachersAndCosts ()", который обновляет информацию о лучших учителях. В конце вызывается "UpdateClassRanks ()", чтобы пересчитать ранги классов, что влияет на приоритетность выбора определенных решений в последующих итерациях.

//————————————————————————————————————————————————————————————————————
void C_AO_CLA_l::Revision ()
{
  for (int i = 0; i < popSize; i++)
  {
    // Обновление персональных лучших позиций
    if (a [i].f > a [i].fB)
    {
      a [i].fB = a [i].f;
      ArrayCopy (a [i].cB, a [i].c);
    }
    // Обновление глобального лучшего
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ArrayCopy (cB, a [i].c);
    }
  }

  // Обновление учителей 
  UpdateTeachersAndCosts ();

  // Обновление рангов классов
  UpdateClassRanks ();
}
//————————————————————————————————————————————————————————————————————

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

Для поиска лучшего студента в классе инициализируются переменные для хранения лучшего значения функции, индекса лучшего студента, суммы значений функции всех студентов в классе и количества валидных студентов. Цикл проходит по всем студентам в текущем классе. Для каждого студента проверяется, является ли его решение валидным, и если решение валидно, то: значение функции этого студента добавляется к "sumFitness", увеличивается счётчик валидных студентов, и если значение функции текущего студента лучше, чем текущее лучшее значение "bestFitness", тогда оно обновляется и "bestIdx".

После нахождения лучшего студента в классе, проверяется, что в классе был хотя бы один валидный студент (validStudents > 0). Если условие выполнено, то координаты лучшего студента копируются в координаты учителя соответствующего класса, и значение функции лучшего студента присваивается значению функции учителя.

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

//————————————————————————————————————————————————————————————————————
void C_AO_CLA::UpdateTeachersAndCosts ()
{
  for (int k = 0; k < numClasses; k++)
  {
    int startIdx = k * studentsPerClass;
    int endIdx = startIdx + studentsPerClass;

    double bestFitness = -DBL_MAX;
    int bestIdx = startIdx;
    double sumFitness = 0.0;
    int validStudents = 0;

    // Находим лучшего студента (учителя) в классе
    for (int i = startIdx; i < endIdx; i++)
    {
      if (a[i].f > -DBL_MAX) // Проверка валидности
      {
        sumFitness += a[i].f;
        validStudents++;
        
        if (a[i].f > bestFitness)
        {
          bestFitness = a[i].f;
          bestIdx = i;
        }
      }
    }

    // Обновляем учителя
    if (validStudents > 0)
    {
      ArrayCopy (teachers[k].c, a[bestIdx].c);
      teachers[k].f = bestFitness;
      
      // Расчет total cost класса
      double avgFitness = sumFitness / validStudents;
      classTotalCosts[k] = bestFitness + beta * avgFitness;
    }
  }
}
//————————————————————————————————————————————————————————————————————

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

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

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

//————————————————————————————————————————————————————————————————————
void C_AO_CLA_l::UpdateClassRanks ()
{
  // Находим min и max costs среди валидных классов
  double minCost   = DBL_MAX;
  double maxCost   = -DBL_MAX;
  int validClasses = 0;

  for (int k = 0; k < numClasses; k++)
  {
    if (classTotalCosts [k] > -DBL_MAX)
    {
      if (classTotalCosts [k] < minCost) minCost = classTotalCosts [k];
      if (classTotalCosts [k] > maxCost) maxCost = classTotalCosts [k];
      validClasses++;
    }
  }

  if (validClasses == 0 || maxCost - minCost < 1e-10)
  {
    // Все классы одинаковые или нет валидных
    for (int k = 0; k < numClasses; k++) classRanks [k] = 1.0;
  }
  else
  {
    // Ранжирование: лучшие классы (высокий cost) получают низкий ранг
    for (int k = 0; k < numClasses; k++)
    {
      if (classTotalCosts [k] > -DBL_MAX)
      {
        // Нормализация от 0 до 1
        double normalized = (classTotalCosts [k] - minCost) / (maxCost - minCost);

        // Инверсия: лучшие получают ранг близкий к 1
        classRanks [k] = 1.0 + (1.0 - normalized) * (numClasses - 1.0);

        // Ограничение
        classRanks [k] = MathMax (1.0, MathMin ((double)numClasses, classRanks [k]));
      }
      else
      {
        classRanks [k] = numClasses; // Худший ранг для неинициализированных
      }
    }
  }
}
//————————————————————————————————————————————————————————————————————


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

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

CLA_L|Competitive Learning Algorithm|198.0|3.0|0.3|0.8|2.0|
=============================
5 Hilly's; Func runs: 10000; result: 0.6482993681242128
25 Hilly's; Func runs: 10000; result: 0.5535249826770444
500 Hilly's; Func runs: 10000; result: 0.2584959913710746
=============================
5 Forest's; Func runs: 10000; result: 0.8027208362980616
25 Forest's; Func runs: 10000; result: 0.49540442971179494
500 Forest's; Func runs: 10000; result: 0.20048686632188017
=============================
5 Megacity's; Func runs: 10000; result: 0.6353846153846153
25 Megacity's; Func runs: 10000; result: 0.2716923076923076
500 Megacity's; Func runs: 10000; result: 0.10086153846153936
=============================
All score: 3.96687 (44.08%)

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

Hilly

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

Forest

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

Megacity

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

По итогам работы алгоритма в турнирной таблице CLA_L представлен ознакомительно.

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
12EOmextremal_optimization_M0,761660,772420,317471,851550,999990,767510,235272,002770,747690,539690,142491,429875,28458,71
13BBObiogeography based optimization0,949120,694560,350311,993990,938200,673650,256821,868670,746150,482770,173691,402615,26558,50
14ACSartificial cooperative search0,755470,747440,304071,806981,000000,888610,224132,112740,690770,481850,133221,305835,22658,06
15DAdialectical algorithm0,861830,700330,337241,899400,981630,727720,287181,996530,703080,452920,163671,319675,21657,95
16BHAmblack hole algorithm M0,752360,766750,345831,864930,935930,801520,271772,009230,650770,516460,154721,321955,19657,73
17ASOanarchy society optimization0,848720,746460,314651,909830,961480,791500,238031,991010,570770,540620,166141,277525,17857,54
18RFOroyal flush optimization (joo)0,833610,737420,346291,917330,894240,738240,240981,873460,631540,502920,164211,298675,08956,55
19AOSmatomic orbital search M0,802320,704490,310211,817020,856600,694510,219961,771070,746150,528620,143581,418355,00655,63
20TSEAturtle shell evolution algorithm (joo)0,967980,644800,296721,909490,994490,619810,227081,841390,690770,426460,135981,253225,00455,60
21BSAbacktracking_search_algorithm0,973090,545340,290981,809410,999990,585430,217471,802890,847690,369530,129781,347004,95955,10
22DEdifferential evolution0,950440,616740,303081,870260,953170,788960,166521,908650,786670,360330,029531,176534,95555,06
23SRAsuccessful restaurateur algorithm (joo)0,968830,634550,292171,895550,946370,555060,191241,692670,749230,440310,125261,314804,90354,48
24CROchemical reaction optimisation0,946290,661120,298531,905930,879060,584220,211461,674730,758460,426460,126861,311784,89254,36
25BIOblood inheritance optimization (joo)0,815680,653360,308771,777810,899370,653190,217601,770160,678460,476310,139021,293784,84253,80
26BSAbird swarm algorithm0,893060,649000,262501,804550,924200,711210,249391,884790,693850,326150,100121,120124,80953,44
27DEAdolphin_echolocation_algorithm0,759950,675720,341711,777380,895820,642230,239411,777460,615380,440310,151151,206844,76252,91
28HSharmony search0,865090,687820,325271,878180,999990,680020,095901,775920,620000,422670,054581,097254,75152,79
29SSGsaplings sowing and growing0,778390,649250,395431,823080,859730,624670,174291,658690,646670,441330,105981,193984,67651,95
30BCOmbacterial chemotaxis optimization M0,759530,622680,314831,697040,893780,613390,225421,732590,653850,420920,144351,219124,64951,65
31ABOafrican buffalo optimization0,833370,622470,299641,755480,921700,586180,197231,705110,610000,431540,132251,173784,63451,49
32(PO)ES(PO) evolution strategies0,790250,626470,429351,846060,876160,609430,195911,681510,590000,379330,113221,082554,61051,22
33FBAfractal-based Algorithm0,790000,651340,289651,730990,871580,568230,188771,628580,610770,460620,123981,195374,55550,61
34TSmtabu search M0,877950,614310,291041,783300,928850,518440,190541,637830,610770,382150,121571,114494,53650,40
35BSObrain storm optimization0,937360,576160,296881,810410,931310,558660,235371,725340,552310,290770,119140,962224,49849,98
36WOAmwale optimization algorithm M0,845210,562980,262631,670810,931000,522780,163651,617430,663080,411380,113571,188034,47649,74
37AEFAartificial electric field algorithm0,877000,617530,252351,746880,927290,726980,180641,834900,666150,116310,095080,877544,45949,55
38AEOartificial ecosystem-based optimization algorithm0,913800,467130,264701,645630,902230,437050,214001,553270,661540,308000,285631,255174,45449,49
39CAmcamel algorithm M0,786840,560420,351331,698590,827720,560410,243361,631490,648460,330920,134181,113564,44449,37
40ACOmant colony optimization M0,881900,661270,303771,846930,858730,586800,150511,596040,596670,373330,024720,994724,43849,31
41CMAEScovariance_matrix_adaptation_evolution_strategy0,762580,720890,000001,483470,820560,796160,000001,616720,758460,490770,000001,249234,34948,33
42BFO-GAbacterial foraging optimization - ga0,891500,551110,315291,757900,969820,396120,063051,428990,726670,275000,035251,036924,22446,93
43SOAsimple optimization algorithm0,915200,469760,270891,655850,896750,374010,169841,440600,695380,280310,108521,084224,18146,45
44ABHAartificial bee hive algorithm0,841310,542270,263041,646630,878580,477790,171811,528180,509230,338770,103970,951974,12745,85
45ACMOatmospheric cloud model optimization0,903210,485460,304031,692700,802680,378570,191781,373030,623080,244000,107950,975034,04144,90
CLA_Lcompetitivelearningalgorithm0,648290,553520,258491,460300,802720,495400,200481,498600,635380,271690,100861,007933,96744,08
RWrandom walk0,487540,321590,257811,066940,375540,219440,158770,753750,279690,149170,098470,527342,34826,09


Выводы

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

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

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

tab

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

chart

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

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

Плюсы:

  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_AO_CLA_L.mq5
СкриптИспытательный стенд для CLA_L
Прикрепленные файлы |
CLA_l.ZIP (317.9 KB)
Трейдинг с экономическим календарем MQL5 (Часть 5): Добавление в панель адаптивных элементов управления и кнопок сортировки Трейдинг с экономическим календарем MQL5 (Часть 5): Добавление в панель адаптивных элементов управления и кнопок сортировки
В этой статье мы создадим кнопки для фильтров валютных пар, уровней важности, временных фильтров и функцию отмены для улучшения управления панелью. Кнопки будут запрограммированы на динамическую реакцию на действия пользователя, обеспечивая бесперебойное взаимодействие. Мы также автоматизируем их поведение, чтобы отражать изменения в реальном времени на панели. Это повысит общую функциональность, мобильность и оперативность панели.
Создание динамических графических интерфейсов на MQL5 через бикубическую интерполяцию Создание динамических графических интерфейсов на MQL5 через бикубическую интерполяцию
В настоящей статье мы исследуем динамические графические интерфейсы MQL5, использующие бикубическую интерполяцию для высококачественного масштабирования изображений на торговых графиках. Мы подробно описываем гибкие варианты позиционирования, позволяющие выполнять динамическое центрирование или угловую привязку с настраиваемыми смещениями.
Преодоление ограничений машинного обучения (Часть 1): Нехватка совместимых метрик Преодоление ограничений машинного обучения (Часть 1): Нехватка совместимых метрик
В настоящей статье показано, что часть проблем, с которыми мы сталкиваемся, коренится в слепом следовании «лучшим практикам». Предоставляя читателю простые, основанные на реальном рынке доказательства, мы объясним ему, почему мы должны воздержаться от такого поведения и вместо этого принять передовой опыт, основанный на конкретных областях, если наше сообщество хочет получить хоть какой-то шанс на восстановление скрытого потенциала ИИ.
Новый подход к пользовательским критериям при оптимизациях (Часть 1): Примеры функций активации Новый подход к пользовательским критериям при оптимизациях (Часть 1): Примеры функций активации
Это первая из серии статей, посвященных математическим аспектам создания пользовательских критериев с особым акцентом на нелинейных функциях, применяемых в нейросетях, MQL5-коде для реализации, а также на использования целевых и корректирующих смещений.