preview
Алгоритм Поиска Ворона — Crow Search Algorithm (CSA)

Алгоритм Поиска Ворона — Crow Search Algorithm (CSA)

MetaTrader 5Трейдинг |
66 0
Andrey Dik
Andrey Dik
Содержание
  1. Введение
  2. Реализация алгоритма
  3. Результаты тестов


Введение

В данной статье рассмотрим алгоритм Crow Search Algorithm (CSA), идея которого мне показалась очень перспективной, это ройный метаэвристический метод, предложенный для глобальной оптимизации. CSA моделирует поведение ворон при сокрытии и поиске "тайников" с пищей. Алгоритм прост в реализации, имеет небольшое число параметров и породил множество модификаций и гибридных версий, мы рассмотрим оригинальную версию. 

Алгоритм CSA был предложен Али Аскарзаде (Ali Askarzadeh) и опубликован в 2016 году в журнале "Computers & Structures (Elsevier)".


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

Представьте себе раннее утро в парке. На ветке сидит ворона — чёрная, блестящая, с глазами, в которых отражается мысль. Она не просто птица, она стратег. И это не просто пернатые, это настоящие аналитики в мире природы, самые умные из птиц. Интеллектуальное поведение этих пернатых вдохновило на создание Crow Search Algorithm (CSA), потому что они умеют делать то, что должен уметь хороший алгоритм: искать, запоминать, адаптироваться и не дать себя обмануть.

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

Навык 2: Слежка за другими. Другая ворона наблюдает. Она сидит на фонарном столбе и смотрит, куда первая спрятала свою еду. Потом, когда та улетит, она спустится и проверит. В CSA агент выбирает другого и пытается "проследовать" к его тайнику, ведь если тот нашёл хорошее решение, почему бы не проверить это?

Навык 3: Обман. Однако, первая ворона не так глупа. Она знает, что за ней могут наблюдать. Поэтому делает ложный манёвр — притворяется, что прячет еду под кустом, а на самом деле прячет её за камнем. В алгоритме аналогично: если агент "замечает, что за ним наблюдают", он уводит преследователя в случайное место. Это помогает алгоритму не застревать в одном решении — как будто ворона говорит: «Ты думал, я поведу тебя туда, где много вкусного? Ха, обманула!»

Навык 4: Адаптация. Вороны не действуют по шаблону. Сегодня они прячут еду в траве, завтра — в щели забора. Они пробуют, учатся, меняют стратегию. В CSA: параметры движения и поведения могут меняться. Алгоритм не стоит на месте — он адаптируется, как ворона, которая ищет лучший способ выжить.

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

csa

Рисунок 1. Иллюстрация работы алгоритма CSA

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

ИНИЦИАЛИЗАЦИЯ:
  1. Задать параметры:
    • N = количество ворон (размер популяции)
    • max_iter = максимальное число итераций
    • fl = длина полета (flight length)
    • AP = вероятность осознания (awareness probability)
    • d = размерность задачи
  2. Инициализировать популяцию:
    • Для каждой вороны i от 1 до N:
      • Позиция[i] = случайная позиция в пространстве поиска
      • Вычислить фитнес позиции[i]
      • Память[i] = Позиция[i] (изначально память равна текущей позиции)
      • Фитнес_памяти[i] = фитнес позиции[i]
ОСНОВНОЙ ЦИКЛ:
  1. Повторять для каждой итерации t от 1 до max_iter:
    1. Для каждой вороны i от 1 до N: a. Выбрать цель следования:
      • Случайно выбрать ворону j (где j ≠ i)
      b. Сгенерировать случайное число:
      • r = случайное число от 0 до 1
      c. Определить поведение на основе осознания:ЕСЛИ r ≥ AP (ворона j НЕ осознает слежку):
      • Для каждой размерности k:
        • ri = случайное число от 0 до 1
        • Новая_позиция[i][k] = Позиция[i][k] + ri × fl × (Память[j][k] - Позиция[i][k])
      ИНАЧЕ (ворона j осознает слежку):
      • Новая_позиция[i] = случайная позиция в пространстве поиска
      d. Проверить допустимость:
      • Убедиться, что Новая_позиция[i] находится в границах
      • Если вышла за границы - вернуть на границу
    2. Вычислить фитнес новых позиций:
      • Для каждой вороны i:
        • Фитнес_новый[i] = вычислить фитнес(Новая_позиция[i])
    3. Обновить память:
      • Для каждой вороны i:
        • ЕСЛИ Фитнес_новый[i] лучше, чем Фитнес_памяти[i]:
          • Память[i] = Новая_позиция[i]
          • Фитнес_памяти[i] = Фитнес_новый[i]
        • ИНАЧЕ:
          • Память не изменяется
    4. Обновить текущие позиции:
      • Для каждой вороны i:
        • Позиция[i] = Новая_позиция[i]
ЗАВЕРШЕНИЕ:
  1. Найти лучшее решение:
    • Лучшее_решение = Память с наилучшим фитнесом среди всех ворон
    • Вернуть Лучшее_решение

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

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

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

//————————————————————————————————————————————————————————————————————
// Структура для хранения памяти вороны
struct S_CrowMemory
{
  double position[];  // позиция в памяти
  double fitness;     // фитнес позиции в памяти
  
  void Init(int dimensions)
  {
    ArrayResize(position, dimensions);
    fitness = -DBL_MAX;
  }
};
//————————————————————————————————————————————————————————————————————

Класс "C_AO_CrowSearchAlgorithm" представляет собой реализацию алгоритма поиска на основе поведения ворон. Он наследуется от базового класса "C_AO". При создании объекта класса, помимо установки имени и описания, инициализируются основные параметры алгоритма:

  • popSize — количество агентов (ворон) в популяции,
  • flightLength — параметр, определяющий дальность перемещения вороны во время полета,
  • awarenessProbability — вероятность того, что ворона "осознает" свое окружение и действия других ворон.
Создается массив "params" для хранения этих параметров, их названий и значений, что используется для их настройки.
  • SetParams () — метод предназначен для обновления внутренних параметров алгоритма из внешнего источника (из массива "params").
  • Init () — является основной точкой инициализации всего алгоритма. Он принимает диапазоны допустимых значений для параметров поиска (минимум, максимум, шаг) и общее количество эпох (итераций).
  • Moving () — отвечает за основное движение и обновление позиций ворон в пространстве поиска на каждой итерации алгоритма.
  • Revision () — производит проверку и коррекцию состояний, оценку качества найденных решений после каждой итерации.
Вспомогательные приватные методы:
  • InitializeMemory () — инициализирует память для всех ворон в популяции,
  • SelectRandomCrow () — выбирает случайную ворону из популяции, исключая при этом указанную ворону,
  • UpdateMemory () — обновляет запись в памяти для конкретной вороны.
Публичные поля данных:
  • flightLength — длина полета, параметр алгоритма
  • awarenessProbability — вероятность осознания, параметр алгоритма
Приватные члены данных: crowMemory [] — массив структур "S_CrowMemory", где для каждой вороны хранится ее позиция и значение пригодности (фитнес). Таким образом, "C_AO_CrowSearchAlgorithm" является классом, который инкапсулирует логику работы алгоритма поиска ворон, управляя популяцией ворон, их перемещением, с использованием памяти и параметров оптимизации.
//————————————————————————————————————————————————————————————————————
class C_AO_CrowSearchAlgorithm : public C_AO
{
  public: //----------------------------------------------------------
  ~C_AO_CrowSearchAlgorithm () { }
  C_AO_CrowSearchAlgorithm ()
  {
    ao_name = "CSA";
    ao_desc = "Crow Search Algorithm";
    ao_link = "https://www.mql5.com/ru/articles/19669";

    popSize              = 20;    // размер популяции (количество ворон)
    flightLength         = 2.0;   // длина полета (fl)
    awarenessProbability = 0.1;   // вероятность осознания (AP)

    ArrayResize (params, 3);

    params [0].name = "popSize";              params [0].val = popSize;
    params [1].name = "flightLength";         params [1].val = flightLength;
    params [2].name = "awarenessProbability"; params [2].val = awarenessProbability;
  }

  void SetParams ()
  {
    popSize              = (int)params [0].val;
    flightLength         = params      [1].val;
    awarenessProbability = params      [2].val;
  }

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

  void Moving   ();
  void Revision ();
  
  private:
  void InitializeMemory ();
  int  SelectRandomCrow (int excludeCrow);
  void UpdateMemory (int crowIndex);

  //------------------------------------------------------------------
  public:
  double flightLength;         // длина полета (fl)
  double awarenessProbability; // вероятность осознания (AP)

  private: //---------------------------------------------------------
  S_CrowMemory crowMemory[]; // память ворон (массив структур)
};
//————————————————————————————————————————————————————————————————————

Метод "Init" является начальной точкой для запуска алгоритма поиска ворон. Его основная задача — подготовить все необходимые структуры и проверить корректность входных параметров.

Стандартная инициализация. Сначала вызывается метод "StandardInit", который выполняет общую подготовку, необходимую для любого алгоритма оптимизации: устанавливает диапазоны поиска, шаг, количество эпох и определяют количество "измерений". Если эта стандартная инициализация завершается неудачно, метод "Init" сразу же возвращает "false".

Инициализация памяти ворон. Выделяется память для массива структур "crowMemory", размер которого равен "popSize" (количеству ворон в популяции). Затем для каждой вороны в этом массиве вызывается ее собственный метод "Init". Этот метод, в свою очередь, настраивает структуру памяти вороны, указывая, сколько измерений (параметров) она должна отслеживать (это значение берется из "coords", установленного "StandardInit"), и устанавливает начальное значение пригодности.

Проверка и корректировка параметров. Проверяется параметр "flightLength". Если он меньше или равен нуля, ему присваивается значение по умолчанию (2.0). Проверяется параметр "awarenessProbability". Если он меньше нуля, ему присваивается 0.0. Если он больше единицы, ему присваивается 1.0. Эти проверки гарантируют, что вероятностные параметры остаются в допустимом диапазоне.

Возврат результата. Если все шаги инициализации прошли успешно, метод возвращает "true", алгоритм готов к работе.
    //————————————————————————————————————————————————————————————————————
    //--- Инициализация
    bool C_AO_CrowSearchAlgorithm::Init (const double &rangeMinP  [],
                                         const double &rangeMaxP  [],
                                         const double &rangeStepP [],
                                         const int     epochsP)
    {
      if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false;
    
      //------------------------------------------------------------------
      // Инициализация массива структур памяти
      ArrayResize (crowMemory, popSize);
      
      for (int i = 0; i < popSize; i++)
      {
        crowMemory[i].Init(coords);
      }
      
      // Проверка корректности параметров
      if (flightLength <= 0.0) flightLength = 2.0;
      if (awarenessProbability < 0.0) awarenessProbability = 0.0;
      if (awarenessProbability > 1.0) awarenessProbability = 1.0;
    
      return true;
    }
    //————————————————————————————————————————————————————————————————————

    Метод "InitializeMemory" отвечает за первоначальное наполнение "памяти" каждой вороны информацией о ее текущей позиции и ее качестве (пригодности).

    Перебор всех ворон. Метод проходит по каждой вороне в популяции, используя цикл от нуля до "popSize" (общего числа ворон).

    Копирование позиции. Для каждой вороны "i" происходит копирование текущей позиции вороны, "a [i].c" содержит текущие координаты вороны "i" (в виде массива), а "coords" — количество измерений (параметров) в пространстве поиска. Эти координаты затем сохраняются в "crowMemory [i].position", то есть в памяти вороны "i".

    Сохранение пригодности. Одновременно с позицией, значение пригодности (качества) текущей позиции вороны "a [i].f" сохраняется в "crowMemory [i].fitness".

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

      //————————————————————————————————————————————————————————————————————
      //--- Инициализация памяти ворон
      void C_AO_CrowSearchAlgorithm::InitializeMemory ()
      {
        // Инициализируем память текущими позициями
        for (int i = 0; i < popSize; i++)
        {
          ArrayCopy (crowMemory[i].position, a[i].c, 0, 0, coords);
          crowMemory[i].fitness = a[i].f;
        }
      }
      //————————————————————————————————————————————————————————————————————

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

      Проверка размера популяции. Если в популяции всего одна ворона (popSize <= 1), то единственной доступной вороной является ворона с индексом "0", поэтому она и возвращается.

      Генерация случайного индекса. Метод входит в цикл "do-while". Внутри цикла генерируется случайное число (selectedCrow), которое представляет собой индекс потенциально выбранной вороны. Для генерации используется функция  "u.RNDfromCI", которая, выдает случайное число в диапазоне от 0.0 до "popSize" (исключая саму верхнюю границу, поэтому добавляется небольшое смещение). Полученное дробное число преобразуется в целое. Производятся дополнительные проверки, чтобы гарантировать, что полученный индекс находится в допустимых границах массива популяции. Если индекс выходит за эти границы, он корректируется.

      Условие исключения. Цикл "do-while" продолжает генерировать случайные индексы до тех пор, пока сгенерированный "selectedCrow" не будет отличаться от "excludeCrow". То есть, если случайно была выбрана та ворона, которую мы хотим исключить, процесс повторяется.

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

      //————————————————————————————————————————————————————————————————————
      //--- Выбор случайной вороны (исключая указанную)
      int C_AO_CrowSearchAlgorithm::SelectRandomCrow (int excludeCrow)
      {
        if (popSize <= 1) return 0;
        
        int selectedCrow;
        do
        {
          selectedCrow = (int)MathFloor(u.RNDfromCI(0.0, popSize - 0.001));
          if (selectedCrow >= popSize) selectedCrow = popSize - 1;
          if (selectedCrow < 0) selectedCrow = 0;
        } 
        while (selectedCrow == excludeCrow);
        
        return selectedCrow;
      }
      //————————————————————————————————————————————————————————————————————

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

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

      Фаза 2: Основной цикл итераций (после инициализации). Этот цикл выполняется на каждой итерации алгоритма и включает следующие шаги для каждой вороны:

      • Сохранение предыдущей позиции. Перед генерацией новой позиции, текущая позиция (a [i].c) и значение пригодности (a [i].f) текущей вороны сохраняются в "предыдущей" переменной (a [i].cP и a [i].fP соответственно). Это необходимо в случае, если новая позиция окажется хуже, можно будет вернуться к предыдущей,
      • Выбор "соперника". Случайным образом выбирается другая ворона (j) из популяции. Эта выбранная ворона будет служить объектом для следования. Важно, что выбранная ворона не может быть той же самой, которую мы сейчас обрабатываем (SelectRandomCrow (i) гарантирует это),
      • Определение сценария движения. Генерируется случайное число (r_j) от 0 до 1 и сравнивается с параметром "awarenessProbability" (вероятность осознания). Этот параметр определяет, как ворона будет двигаться:

      • Сценарий 1: Ворона следует за другой (Формула 1)

        • Если "r_j" больше или равно "awarenessProbability", это означает, что ворона "i" будет пытаться приблизиться к лучшей позиции, основанной на позиции выбранной вороны "j".
        • Новая позиция вычисляется по формуле: новая позиция = текущая позиция + случайный шаг * коэффициент полета * (позиция вороны (j) - текущая позиция).
        • Случайный шаг (r_i) — это еще одно случайное число, обеспечивающее вариативность.
        • Коэффициент полета "flightLength" — параметр, контролирующий насколько далеко ворона может перемещаться.
        • Позиция вороны (j) - текущая_позиция  — вектор, направленный от текущей позиции к позиции выбранной для следования вороны.
        • После вычисления новой позиции, она также проверяется на допустимость в пределах заданных диапазонов (rangeMin, rangeMax, rangeStep).
      • Сценарий 2: Полное случайное перемещение (Формула 2)

        • Если "r_j" меньше "awarenessProbability", это означает, что ворона "j" "замечает" слежку и принимает решительные меры.
        • В этом случае ворона "i" игнорирует позицию вороны "j" и перемещается в абсолютно новую случайную позицию в пределах всего пространства поиска.
        • Эта новая случайная позиция также генерируется с учетом допустимых диапазонов и шагов.
      После того как новая позиция вороны "i" была сгенерирована и проверена на допустимость, происходит пересчет ее значения пригодности (a [i].f) на основе этой новой позиции. Таким образом, метод гарантирует, что популяция ворон постоянно исследует пространство поиска, используя как направленное движение к другим особям, так и случайное блуждание.
      //————————————————————————————————————————————————————————————————————
      //--- Основной шаг алгоритма (формулы 1 и 2 из документа)
      void C_AO_CrowSearchAlgorithm::Moving ()
      {
        // Начальная инициализация популяции
        if (!revision)
        {
          // Шаг 1: Инициализация популяции ворон случайным образом
          for (int i = 0; i < popSize; i++)
          {
            for (int j = 0; j < coords; j++)
            {
              a [i].c [j] = u.RNDfromCI (rangeMin [j], rangeMax [j]);
              a [i].c [j] = u.SeInDiSp (a [i].c [j], rangeMin [j], rangeMax [j], rangeStep [j]);
            }
          }
          
          revision = true;
          return;
        }
      
        //------------------------------------------------------------------
        // Основной цикл итерации
        
        // Шаг 5: Генерация новой позиции для каждой вороны
        for (int i = 0; i < popSize; i++)
        {
          // Сохраняем текущие позиции в cP (предыдущие координаты)
          ArrayCopy (a[i].cP, a[i].c, 0, 0, coords);
          a[i].fP = a[i].f;
          
          // Случайно выбираем ворону j для следования (кроме самой себя)
          int j = SelectRandomCrow(i);
          
          // Генерируем случайное число для определения осознания
          double r_j = u.RNDfromCI(0.0, 1.0);
          
          // Применяем формулы обновления позиции согласно сценариям
          if (r_j >= awarenessProbability)
          {
            // Сценарий 1: Ворона j не знает о слежке
            // Формула (1): P_i^(t+1) = P_i^t + r_i * fl_j * (M_j^t - P_i^t)
            for (int c = 0; c < coords; c++)
            {
              double r_i = u.RNDfromCI(0.0, 1.0);
              a[i].c[c] = a[i].c[c] + r_i * flightLength * (crowMemory[j].position[c] - a[i].c[c]);
              
              // Шаг 6: Проверка допустимости новой позиции
              a[i].c[c] = u.SeInDiSp (a[i].c[c], rangeMin[c], rangeMax[c], rangeStep[c]);
            }
          }
          else
          {
            // Сценарий 2: Ворона j осознает слежку
            // Формула (2): Перемещение в случайную позицию
            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]);
            }
          }
        }
      }
      //————————————————————————————————————————————————————————————————————

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

      Сравнение текущей пригодности с запомненной. Метод получает индекс вороны "crowIndex", чью память нужно обновить. Он сравнивает значение пригодности текущей позиции этой вороны "a [crowIndex].f" с ранее сохраненным значением пригодности в ее памяти.

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

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

        //————————————————————————————————————————————————————————————————————
        //--- Обновление памяти вороны
        void C_AO_CrowSearchAlgorithm::UpdateMemory (int crowIndex)
        {
          // Формула (3): Обновляем память если новая позиция лучше
          if (a[crowIndex].f > crowMemory[crowIndex].fitness)
          {
            ArrayCopy (crowMemory[crowIndex].position, a[crowIndex].c, 0, 0, coords);
            crowMemory[crowIndex].fitness = a[crowIndex].f;
          }
        }
        //————————————————————————————————————————————————————————————————————

        Метод "Revision" отвечает за оценку и обновление результатов после каждого основного цикла перемещения ворон.

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

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

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

        Дополнительная проверка памяти. После оценки текущих позиций, проводится еще одна проверка, но уже по индивидуальной памяти каждой вороны. Если какая-либо из запомненных лучших (максимальных) позиций (crowMemory [i].fitness) оказывается лучше текущего глобального лучшего (fB), то глобальное лучшее решение обновляется. Это важно, так как метод "UpdateMemory" мог обновить память вороны, но затем ворона могла отойти от этой лучшей позиции в текущей итерации.

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

          //————————————————————————————————————————————————————————————————————
          //--- Обновление и проверка результатов
          void C_AO_CrowSearchAlgorithm::Revision ()
          {
            // Если это первая итерация после инициализации
            if (crowMemory[0].fitness == -DBL_MAX)
            {
              InitializeMemory();
            }
            
            // Шаг 8: Обновление памяти каждой вороны
            for (int i = 0; i < popSize; i++)
            {
              UpdateMemory(i);
            }
            
            // Обновляем глобальное лучшее решение
            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);
              }
            }
            
            // Дополнительно проверяем память на наличие лучших решений
            for (int i = 0; i < popSize; i++)
            {
              if (crowMemory[i].fitness > fB)
              {
                fB = crowMemory[i].fitness;
                ArrayCopy (cB, crowMemory[i].position, 0, 0, WHOLE_ARRAY);
              }
            }
          }
          //————————————————————————————————————————————————————————————————————


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

          Как видно из результатов, они средние, слабее на дискретной функции Megacity.

          CSA|Crow Search Algorithm|20.0|2.0|0.05|
          =============================
          5 Hilly's; Func runs: 10000; result: 0.7780377334241331
          25 Hilly's; Func runs: 10000; result: 0.5107983411957056
          500 Hilly's; Func runs: 10000; result: 0.2671707154561419
          =============================
          5 Forest's; Func runs: 10000; result: 0.9221928451900311
          25 Forest's; Func runs: 10000; result: 0.48104687904934185
          500 Forest's; Func runs: 10000; result: 0.16367212698152436
          =============================
          5 Megacity's; Func runs: 10000; result: 0.4846153846153848
          25 Megacity's; Func runs: 10000; result: 0.34215384615384614
          500 Megacity's; Func runs: 10000; result: 0.11487692307692406
          =============================
          All score: 4.06456 (45.16%)

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

          Hilly

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

          Forest

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

          Megacity

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

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

          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)
          1 DOAdingom dingo_optimization_algorithm_M 0,47968 0,45367 0,46369 1,39704 0,94145 0,87909 0,91454 2,73508 0,78615 0,86061 0,84805 2,49481 6,627 73,63
          2 ANS across neighbourhood search 0,94948 0,84776 0,43857 2,23581 1,00000 0,92334 0,39988 2,32323 0,70923 0,63477 0,23091 1,57491 6,134 68,15
          3 CLA code lock algorithm (joo) 0,95345 0,87107 0,37590 2,20042 0,98942 0,91709 0,31642 2,22294 0,79692 0,69385 0,19303 1,68380 6,107 67,86
          4 AMOm animal migration ptimization M 0,90358 0,84317 0,46284 2,20959 0,99001 0,92436 0,46598 2,38034 0,56769 0,59132 0,23773 1,39675 5,987 66,52
          5 (P+O)ES (P+O) evolution strategies 0,92256 0,88101 0,40021 2,20379 0,97750 0,87490 0,31945 2,17185 0,67385 0,62985 0,18634 1,49003 5,866 65,17
          6 CTA comet tail algorithm (joo) 0,95346 0,86319 0,27770 2,09435 0,99794 0,85740 0,33949 2,19484 0,88769 0,56431 0,10512 1,55712 5,846 64,96
          7 TETA time evolution travel algorithm (joo) 0,91362 0,82349 0,31990 2,05701 0,97096 0,89532 0,29324 2,15952 0,73462 0,68569 0,16021 1,58052 5,797 64,41
          8 SDSm stochastic diffusion search M 0,93066 0,85445 0,39476 2,17988 0,99983 0,89244 0,19619 2,08846 0,72333 0,61100 0,10670 1,44103 5,709 63,44
          9 BOAm billiards optimization algorithm M 0,95757 0,82599 0,25235 2,03590 1,00000 0,90036 0,30502 2,20538 0,73538 0,52523 0,09563 1,35625 5,598 62,19
          10 AAm archery algorithm M 0,91744 0,70876 0,42160 2,04780 0,92527 0,75802 0,35328 2,03657 0,67385 0,55200 0,23738 1,46323 5,548 61,64
          11 ESG evolution of social groups (joo) 0,99906 0,79654 0,35056 2,14616 1,00000 0,82863 0,13102 1,95965 0,82333 0,55300 0,04725 1,42358 5,529 61,44
          12 SIA simulated isotropic annealing (joo) 0,95784 0,84264 0,41465 2,21513 0,98239 0,79586 0,20507 1,98332 0,68667 0,49300 0,09053 1,27020 5,469 60,76
          13 EOm extremal_optimization_M 0,76166 0,77242 0,31747 1,85155 0,99999 0,76751 0,23527 2,00277 0,74769 0,53969 0,14249 1,42987 5,284 58,71
          14 BBO biogeography based optimization 0,94912 0,69456 0,35031 1,99399 0,93820 0,67365 0,25682 1,86867 0,74615 0,48277 0,17369 1,40261 5,265 58,50
          15 ACS artificial cooperative search 0,75547 0,74744 0,30407 1,80698 1,00000 0,88861 0,22413 2,11274 0,69077 0,48185 0,13322 1,30583 5,226 58,06
          16 DA dialectical algorithm 0,86183 0,70033 0,33724 1,89940 0,98163 0,72772 0,28718 1,99653 0,70308 0,45292 0,16367 1,31967 5,216 57,95
          17 BHAm black hole algorithm M 0,75236 0,76675 0,34583 1,86493 0,93593 0,80152 0,27177 2,00923 0,65077 0,51646 0,15472 1,32195 5,196 57,73
          18 ASO anarchy society optimization 0,84872 0,74646 0,31465 1,90983 0,96148 0,79150 0,23803 1,99101 0,57077 0,54062 0,16614 1,27752 5,178 57,54
          19 RFO royal flush optimization (joo) 0,83361 0,73742 0,34629 1,91733 0,89424 0,73824 0,24098 1,87346 0,63154 0,50292 0,16421 1,29867 5,089 56,55
          20 AOSm atomic orbital search M 0,80232 0,70449 0,31021 1,81702 0,85660 0,69451 0,21996 1,77107 0,74615 0,52862 0,14358 1,41835 5,006 55,63
          21 TSEA turtle shell evolution algorithm (joo) 0,96798 0,64480 0,29672 1,90949 0,99449 0,61981 0,22708 1,84139 0,69077 0,42646 0,13598 1,25322 5,004 55,60
          22 BSA backtracking_search_algorithm 0,97309 0,54534 0,29098 1,80941 0,99999 0,58543 0,21747 1,80289 0,84769 0,36953 0,12978 1,34700 4,959 55,10
          23 DE differential evolution 0,95044 0,61674 0,30308 1,87026 0,95317 0,78896 0,16652 1,90865 0,78667 0,36033 0,02953 1,17653 4,955 55,06
          24 SRA successful restaurateur algorithm (joo) 0,96883 0,63455 0,29217 1,89555 0,94637 0,55506 0,19124 1,69267 0,74923 0,44031 0,12526 1,31480 4,903 54,48
          25 CRO chemical reaction optimisation 0,94629 0,66112 0,29853 1,90593 0,87906 0,58422 0,21146 1,67473 0,75846 0,42646 0,12686 1,31178 4,892 54,36
          26 BIO blood inheritance optimization (joo) 0,81568 0,65336 0,30877 1,77781 0,89937 0,65319 0,21760 1,77016 0,67846 0,47631 0,13902 1,29378 4,842 53,80
          27 DOA dream_optimization_algorithm 0,85556 0,70085 0,37280 1,92921 0,73421 0,48905 0,24147 1,46473 0,77231 0,47354 0,18561 1,43146 4,825 53,62
          28 BSA bird swarm algorithm 0,89306 0,64900 0,26250 1,80455 0,92420 0,71121 0,24939 1,88479 0,69385 0,32615 0,10012 1,12012 4,809 53,44
          29 DEA dolphin_echolocation_algorithm 0,75995 0,67572 0,34171 1,77738 0,89582 0,64223 0,23941 1,77746 0,61538 0,44031 0,15115 1,20684 4,762 52,91
          30 HS harmony search 0,86509 0,68782 0,32527 1,87818 0,99999 0,68002 0,09590 1,77592 0,62000 0,42267 0,05458 1,09725 4,751 52,79
          31 SSG saplings sowing and growing 0,77839 0,64925 0,39543 1,82308 0,85973 0,62467 0,17429 1,65869 0,64667 0,44133 0,10598 1,19398 4,676 51,95
          32 BCOm bacterial chemotaxis optimization M 0,75953 0,62268 0,31483 1,69704 0,89378 0,61339 0,22542 1,73259 0,65385 0,42092 0,14435 1,21912 4,649 51,65
          33 ABO african buffalo optimization 0,83337 0,62247 0,29964 1,75548 0,92170 0,58618 0,19723 1,70511 0,61000 0,43154 0,13225 1,17378 4,634 51,49
          34 (PO)ES (PO) evolution strategies 0,79025 0,62647 0,42935 1,84606 0,87616 0,60943 0,19591 1,68151 0,59000 0,37933 0,11322 1,08255 4,610 51,22
          35 FBA fractal-based Algorithm 0,79000 0,65134 0,28965 1,73099 0,87158 0,56823 0,18877 1,62858 0,61077 0,46062 0,12398 1,19537 4,555 50,61
          36 TSm tabu search M 0,87795 0,61431 0,29104 1,78330 0,92885 0,51844 0,19054 1,63783 0,61077 0,38215 0,12157 1,11449 4,536 50,40
          37 BSO brain storm optimization 0,93736 0,57616 0,29688 1,81041 0,93131 0,55866 0,23537 1,72534 0,55231 0,29077 0,11914 0,96222 4,498 49,98
          38 WOAm wale optimization algorithm M 0,84521 0,56298 0,26263 1,67081 0,93100 0,52278 0,16365 1,61743 0,66308 0,41138 0,11357 1,18803 4,476 49,74
          39 AEFA artificial electric field algorithm 0,87700 0,61753 0,25235 1,74688 0,92729 0,72698 0,18064 1,83490 0,66615 0,11631 0,09508 0,87754 4,459 49,55
          40 AEO artificial ecosystem-based optimization algorithm 0,91380 0,46713 0,26470 1,64563 0,90223 0,43705 0,21400 1,55327 0,66154 0,30800 0,28563 1,25517 4,454 49,49
          41 CAm camel algorithm M 0,78684 0,56042 0,35133 1,69859 0,82772 0,56041 0,24336 1,63149 0,64846 0,33092 0,13418 1,11356 4,444 49,37
          42 ACOm ant colony optimization M 0,88190 0,66127 0,30377 1,84693 0,85873 0,58680 0,15051 1,59604 0,59667 0,37333 0,02472 0,99472 4,438 49,31
          43 CMAES covariance_matrix_adaptation_evolution_strategy 0,76258 0,72089 0,00000 1,48347 0,82056 0,79616 0,00000 1,61672 0,75846 0,49077 0,00000 1,24923 4,349 48,33
          44 DA_duelist duelist_algorithm 0,92782 0,53778 0,27792 1,74352 0,86957 0,47536 0,18193 1,52686 0,62153 0,33569 0,11715 1,07437 4,345 48,28
          45 BFO-GA bacterial foraging optimization - ga 0,89150 0,55111 0,31529 1,75790 0,96982 0,39612 0,06305 1,42899 0,72667 0,27500 0,03525 1,03692 4,224 46,93
          CSA_crow crow_search_algorithm 0,77804 0,51080 0,26717 1,55601 0,92219 0,48105 0,16367 1,56691 0,48461 0,34215 0,11487 0,94163 4,065 45,16
          RW random walk 0,48754 0,32159 0,25781 1,06694 0,37554 0,21944 0,15877 0,75375 0,27969 0,14917 0,09847 0,52734 2,348 26,09


          Выводы

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

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

          Минимум параметров —  всего 3 настройки (N, fl, AP), это упрощает настройку по сравнению с более сложными метаэвристиками. CSA —  это хороший учебный материал и достойный базовый метод оптимизации. Он не революционен, но надежен для несложных задач и его место в арсенале трейдера —  как запасной вариант, когда более сложные методы избыточны, или когда важна простота реализации.

          tab

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

          chart

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

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

          Плюсы:

          1. Красивая и перспективная идея как база для разработок.
          2. Небольшое количество параметров.

          Минусы:

          1. Большой разброс результатов.
          2. Низкая эффективность на задачах высокой размерности.

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


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

          # Имя Тип Описание
          1 #C_AO.mqh
          Включаемый файл
          Родительский класс популяционных алгоритмов оптимизации
          2 #C_AO_enum.mqh
          Включаемый файл
          Перечисление популяционных алгоритмов оптимизации
          3 TestFunctions.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_CSA_crow.mq5
          Скрипт Испытательный стенд для CSA_crow
          Прикрепленные файлы |
          CSA_CROW.ZIP (346.8 KB)
          Переходим на MQL5 Algo Forge (Часть 4): Работа с версиями и выпуск релизов Переходим на MQL5 Algo Forge (Часть 4): Работа с версиями и выпуск релизов
          Продолжим разработку проекта Simple Candles и Adwizard, описывая нюансы использования системы контроля версий и хранилища MQL5 Algo Forge.
          Торгуем опционы без опционов (Часть 3): Сложные опционные стратегии Торгуем опционы без опционов (Часть 3): Сложные опционные стратегии
          Рассматриваются флэтовые (не направленные) и трендовые (направленные) опционные стратегии и их реализация на MQL5. Модернизируется эксперт, написанный в предыдущей статье. Добавляется отображение опционных уровней. Теперь пора рассмотреть работу и реализовать те стратегии, которые используются на практике опционными трейдерами.
          Особенности написания экспертов Особенности написания экспертов
          Написание и тестирование экспертов в торговой системе MetaTrader 4.
          Нейросети в трейдинге: От трансформеров к спайковым нейронам (Основные компоненты) Нейросети в трейдинге: От трансформеров к спайковым нейронам (Основные компоненты)
          Предлагаем вниманию читателя реализацию подходов фреймворка SpikingBrain на основе рекуррентного линейного внимания с гейтами, подробно разобранного в этой статье. Алгоритмы прямого прохода, распределения градиентов и обновления весов обеспечивают эффективную обработку финансовых временных рядов и позволяют воплотить ключевые идеи фреймворка на практике.