preview
Алгоритм хаотической оптимизации — Chaos optimization algorithm (COA): Продолжение

Алгоритм хаотической оптимизации — Chaos optimization algorithm (COA): Продолжение

MetaTrader 5Тестер | 2 мая 2025, 11:04
317 0
Andrey Dik
Andrey Dik

Содержание

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


Введение

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

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

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



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

Метод "ApplyMutation" предназначен для внедрения мутаций в агента. Его основной задачей является случайное изменение параметров определенного агента.

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

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

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

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

//——————————————————————————————————————————————————————————————————————————————
void C_AO_COA_chaos::ApplyMutation (int agentIdx)
{
  // Определяем количество координат для мутации (от 1 до 30% координат)
  int mutationCount = 1 + (int)(u.RNDprobab () * coords * 0.3);
  mutationCount = MathMin (mutationCount, coords);

  // Создаем массив индексов для мутации без повторений
  int mutationIndices [];
  ArrayResize (mutationIndices, coords);

  // Заполняем массив индексами
  for (int i = 0; i < coords; i++)
  {
    mutationIndices [i] = i;
  }

  // Перемешиваем индексы
  for (int i = coords - 1; i > 0; i--)
  {
    int j = (int)(u.RNDprobab () * (i + 1));
    if (j <= i) // Дополнительная проверка для безопасности
    {
      int temp = mutationIndices [i];
      mutationIndices [i] = mutationIndices [j];
      mutationIndices [j] = temp;
    }
  }

  // Применяем мутации к выбранным координатам
  for (int m = 0; m < mutationCount; m++)
  {
    int c = mutationIndices [m];

    // Различные типы мутаций для разнообразия
    double r = u.RNDprobab ();
    double x;

    if (r < 0.3)
    {
      // Полная случайная мутация
      x = rangeMin [c] + u.RNDprobab () * (rangeMax [c] - rangeMin [c]);
    }
    else
      if (r < 0.6)
      {
        // Мутация относительно глобального лучшего
        double offset = (u.RNDprobab () - 0.5) * (rangeMax [c] - rangeMin [c]) * 0.2;
        x = cB [c] + offset;
      }
      else
      {
        // Мутация с использованием хаотического отображения
        agent [agentIdx].gamma [c] = SelectChaosMap (agent [agentIdx].gamma [c], (epochNow + c) % 3);
        x = rangeMin [c] + agent [agentIdx].gamma [c] * (rangeMax [c] - rangeMin [c]);
      }

    // Сбрасываем скорость
    agent [agentIdx].velocity [c] = 0.0;

    // Применяем новое значение с проверкой диапазона
    a [agentIdx].c [c] = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]);
  }
}
//——————————————————————————————————————————————————————————————————————————————

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

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

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

В конце метод гарантирует, что значение текущего штрафа остается в приемлемых пределах. Если оно становится слишком маленьким (менее 10% от базового значения), штраф увеличивается до этого уровня. Аналогично, если штраф превышает 500% от базового значения, он ограничивается до этого уровня.

//——————————————————————————————————————————————————————————————————————————————
void C_AO_COA_chaos::UpdateSigma ()
{
  // Динамическая адаптация параметра штрафа
  // Начинаем с малого значения и увеличиваем его при необходимости

  if (epochNow == 1)
  {
    currentSigma = sigma * 0.5;
    return;
  }

  // Подсчет количества допустимых решений
  int feasibleCount = 0;
  for (int i = 0; i < popSize; i++)
  {
    if (IsFeasible (i))
    {
      feasibleCount++;
    }
  }

  double feasibleRatio = (double)feasibleCount / MathMax (1, popSize);

  // Адаптация параметра штрафа в зависимости от доли допустимых решений
  if (feasibleRatio < 0.3)
  {
    // Слишком мало допустимых решений - увеличиваем штраф
    currentSigma *= 1.2;
  }
  else
    if (feasibleRatio > 0.7)
    {
      // Слишком много допустимых решений - уменьшаем штраф
      currentSigma *= 0.9;
    }

  // Ограничиваем значение sigma
  if (currentSigma < sigma * 0.1) currentSigma = sigma * 0.1;
  else
    if (currentSigma > sigma * 5.0) currentSigma = sigma * 5.0;
}
//——————————————————————————————————————————————————————————————————————————————

Метод "IsFeasible" определяет, является ли конкретное решение, представленное агентом с заданным индексом "agentIdx", допустимым (feasible) относительно заданных ограничений.

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

Функция "CalculateConstraintValue" возвращает значение, которое отражает степень нарушения ограничений для конкретной координаты решения. Если после проверки всех координат, ни одно ограничение не нарушено, то есть, все значения нарушений меньше или равны  "eps", то решение считается допустимым, и метод возвращает "true".

//——————————————————————————————————————————————————————————————————————————————
bool C_AO_COA_chaos::IsFeasible (int agentIdx)
{
  // Проверяем, находится ли решение в допустимой области
  for (int c = 0; c < coords; c++)
  {
    double violation = CalculateConstraintValue (agentIdx, c);
    if (violation > eps)
    {
      return false;
    }
  }
  return true;
}
//——————————————————————————————————————————————————————————————————————————————

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

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

//——————————————————————————————————————————————————————————————————————————————
void C_AO_COA_chaos::UpdateBestHistory (double newBest)
{
  // Защита от некорректных значений
  if (!MathIsValidNumber (newBest)) return;

  // Обновляем историю лучших значений
  globalBestHistory [historyIndex] = newBest;
  historyIndex = (historyIndex + 1) % 10;
}
//——————————————————————————————————————————————————————————————————————————————

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

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

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

//——————————————————————————————————————————————————————————————————————————————
bool C_AO_COA_chaos::IsConverged ()
{
  // Проверка достаточного количества данных в истории
  int validValues = 0;
  double sum      = 0.0;
  double minVal   = DBL_MAX;
  double maxVal   = -DBL_MAX;

  // Находим min, max и sum значений в истории
  for (int i = 0; i < 10; i++)
  {
    if (globalBestHistory [i] == -DBL_MAX || !MathIsValidNumber (globalBestHistory [i])) continue;

    minVal = MathMin (minVal, globalBestHistory [i]);
    maxVal = MathMax (maxVal, globalBestHistory [i]);
    sum += globalBestHistory [i];
    validValues++;
  }

  // Если недостаточно данных или все значения одинаковые
  if (validValues < 5 || minVal == maxVal) return false;

  // Вычисляем среднее значение
  double average = sum / validValues;

  // Проверка случая, когда среднее близко к нулю
  if (MathAbs (average) < eps) return MathAbs (maxVal - minVal) < eps * 10.0;

  // Относительная разница для проверки сходимости
  double relDiff = MathAbs (maxVal - minVal) / MathAbs (average);

  return relDiff < 0.001; // Порог сходимости - 0.1%
}
//——————————————————————————————————————————————————————————————————————————————

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

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

Если агент находится в стагнации более 5 итераций, метод вычисляет вероятность сброса (resetProb). Эта вероятность пропорциональна текущему значению счетчика стагнации, что означает, что чем дольше агент стагнирует, тем выше вероятность его сброса. Формула для расчета вероятности учитывает фиксированное значение (0.2) и относительное значение счетчика стагнации. Если случайно сгенерированное значение меньше рассчитанной вероятности "resetProb", к агенту применяется мутация с помощью функции "ApplyMutation". После применения мутации, счетчик стагнации агента обнуляется, а счетчик "resetCount" увеличивается.

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

//——————————————————————————————————————————————————————————————————————————————
void C_AO_COA_chaos::ResetStagnatingAgents ()
{
  int resetCount = 0;

  for (int i = 0; i < popSize; i++)
  {
    // Увеличиваем счетчик стагнации, если нет улучшения
    if (a [i].f <= a [i].fB)
    {
      agent [i].stagnationCounter++;
    }
    else
    {
      agent [i].stagnationCounter = 0;
    }

    // Сбрасываем агента, если он находится в стагнации слишком долго
    if (agent [i].stagnationCounter > 5)
    {
      // Сброс только части агентов с вероятностью, зависящей от стагнации
      double resetProb = 0.2 * (1.0 + (double)agent [i].stagnationCounter / 10.0);

      if (u.RNDprobab () < resetProb)
      {
        ApplyMutation (i);
        agent [i].stagnationCounter = 0;
        resetCount++;
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

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

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

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

//——————————————————————————————————————————————————————————————————————————————
double C_AO_COA_chaos::CalculateWeightedGradient (int agentIdx, int coordIdx)
{
  // Вычисляем максимальное значение нарушения ограничений
  double maxViolation = eps;

  for (int c = 0; c < coords; c++)
  {
    double violation = CalculateConstraintValue (agentIdx, c);
    maxViolation = MathMax (maxViolation, violation);
  }

  // Нарушение для текущей координаты
  double violation = CalculateConstraintValue (agentIdx, coordIdx);

  // Если нет значительного нарушения, возвращаем 0
  if (violation <= eps) return 0.0;

  // Вычисляем градиент
  double gradient = CalculateConstraintGradient (agentIdx, coordIdx);

  // Нормализуем влияние в зависимости от степени нарушения
  double weight = violation / maxViolation;

  return gradient * weight;
}
//——————————————————————————————————————————————————————————————————————————————

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

Сначала метод проверяет индексы агента (agentIdx) и координаты (coordIdx) на корректность. Метод получает текущее значение координаты "x" из состояния агента, а также определяет минимальную (min) и максимальную (max) допустимые границы для этой координаты соответственно. Далее метод проверяет, находится ли значение "x" в пределах допустимого диапазона (min и max). В завершение, метод возвращает значение "violation", которое представляет собой суммарную величину нарушения ограничений для данной координаты данного агента.

Ключевые особенности:

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

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

//——————————————————————————————————————————————————————————————————————————————
double C_AO_COA_chaos::CalculateConstraintValue (int agentIdx, int coordIdx)
{
  double x = a [agentIdx].c [coordIdx];
  double min = rangeMin [coordIdx];
  double max = rangeMax [coordIdx];

  // Сглаженная функция нарушения ограничений с плавным переходом на границе
  double violation = 0.0;

  // Проверка нижней границы
  if (x < min)
  {
    violation += min - x;
  }
  // Проверка верхней границы
  else
    if (x > max)
    {
      violation += x - max;
    }

  return violation;
}
//——————————————————————————————————————————————————————————————————————————————

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

Сначала метод проверяет входные индексы агента (agentIdx) и координаты (coordIdx) для обеспечения их корректности. Если хотя бы один из индексов находится вне допустимого диапазона, метод возвращает 0.0, что указывает на невозможность вычисления градиента. Метод извлекает текущее значение координаты агента, а также минимальные и максимальные границы, установленные для этой координаты. Эти значения необходимы для дальнейших оценок. Далее, метод проверяет, находится ли текущее значение координаты "x" за пределами установленных границ.

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

//——————————————————————————————————————————————————————————————————————————————
double C_AO_COA_chaos::CalculateConstraintGradient (int agentIdx, int coordIdx)
{
  double x = a [agentIdx].c [coordIdx];
  double min = rangeMin [coordIdx];
  double max = rangeMax [coordIdx];

  // Сглаженный градиент для лучшей стабильности
  if (x < min) return -1.0; // Нужно увеличивать значение
  else
    if (x > max) return 1.0;  // Нужно уменьшать значение

  return 0.0;
}
//——————————————————————————————————————————————————————————————————————————————

Метод "CalculatePenaltyFunction" вычисляет значения целевой функции с учетом штрафа за нарушение ограничений для конкретного агента. Он комбинирует базовое значение целевой функции со штрафом, основанным на величине нарушений ограничений.

Метод начинает работу с проверки корректности индекса агента (agentIdx), и если индекс корректен, метод получает базовое (не оштрафованное) значение целевой функции этого агента (a [agentIdx].f) и сохраняет его в переменной "baseValue". Затем метод перебирает все координаты (coords) каждого агента. Для каждой координаты вычисляется величина нарушения ограничений. Если величина нарушения (violation) превышает заданную небольшую величину "eps", то квадрат величины нарушения добавляется к сумме штрафов "penaltySum".

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

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

//——————————————————————————————————————————————————————————————————————————————
double C_AO_COA_chaos::CalculatePenaltyFunction (int agentIdx)
{
  // Базовое значение целевой функции
  double baseValue = a [agentIdx].f;

  // Штрафной терм
  double penaltySum = 0.0;

  for (int c = 0; c < coords; c++)
  {
    double violation = CalculateConstraintValue (agentIdx, c);

    // Квадратичный штраф для лучшего разрешения близких к границе решений
    if (violation > eps)
    {
      penaltySum += violation * violation;
    }
  }

  // Применяем динамический коэффициент штрафа
  double penalty = currentSigma * penaltySum;

  // Для задачи максимизации: f_penalty = f - penalty
  return baseValue - penalty;
}
//——————————————————————————————————————————————————————————————————————————————

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

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

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

//——————————————————————————————————————————————————————————————————————————————
void C_AO_COA_chaos::Moving ()
{
  epochNow++;

  if (!revision)
  {
    InitialPopulation ();

    revision = true;
    return;
  }

  // Динамическое обновление параметра штрафа
  UpdateSigma ();

  // Сброс агентов, находящихся в стагнации
  if (epochNow % 5 == 0)
  {
    ResetStagnatingAgents ();
  }

  // Определяем фазу поиска
  if (epochNow <= S1)
  {
    FirstCarrierWaveSearch ();
  }
  else
    if (epochNow <= S1 + S2)
    {
      SecondCarrierWaveSearch ();
    }
}
//——————————————————————————————————————————————————————————————————————————————

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

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

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

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

//——————————————————————————————————————————————————————————————————————————————
void C_AO_COA_chaos::Revision ()
{
  int    improvementCount = 0;
  double bestImprovement  = 0.0;

  // Оценка всех решений
  for (int i = 0; i < popSize; i++)
  {
    double newValue = CalculatePenaltyFunction (i);

    // Проверка на валидность нового значения
    if (!MathIsValidNumber (newValue)) continue;

    // Сохраняем текущее значение для следующей итерации
    a [i].f = newValue;

    // Обновление личного лучшего решения (перед глобальным для эффективности)
    if (newValue > a [i].fB)
    {
      a [i].fB = newValue;
      ArrayCopy (a [i].cB, a [i].c, 0, 0, WHOLE_ARRAY);
      improvementCount++;
    }

    // Обновление глобального лучшего решения
    if (newValue > fB)
    {
      double improvement = newValue - fB;
      fB = newValue;
      ArrayCopy (cB, a [i].c, 0, 0, WHOLE_ARRAY);

      // Обновляем историю лучших значений
      UpdateBestHistory (fB);

      bestImprovement = MathMax (bestImprovement, improvement);
      improvementCount++;
    }
  }

  // Адаптация параметров поиска в зависимости от фазы и успешности
  if (epochNow > 1)
  {
    // Коэффициент успешности поиска (предотвращение деления на ноль)
    double successRate = (double)improvementCount / MathMax (1, 2 * popSize);

    // Адаптация параметра alpha
    for (int c = 0; c < coords; c++)
    {
      double oldAlpha = alpha [c];

      if (epochNow <= S1)
      {
        // В фазе глобального поиска
        if (successRate < 0.1)
        {
          // Очень мало улучшений - увеличиваем область поиска
          alpha [c] *= t3;
        }
        else
          if (successRate < 0.3)
          {
            // Мало улучшений - слегка увеличиваем область
            alpha [c] *= 1.2;
          }
          else
            if (successRate > 0.7)
            {
              // Много улучшений - сужаем область
              alpha [c] *= 0.9;
            }
      }
      else
      {
        // В фазе локального поиска
        if (successRate < 0.05)
        {
          // Очень мало улучшений - увеличиваем область поиска
          alpha [c] *= t3;
        }
        else
          if (successRate > 0.2)
          {
            // Достаточно улучшений - сужаем область
            alpha [c] *= 0.8;
          }
      }

      // Ограничения на диапазон alpha с безопасной проверкой границ
      if (c < ArraySize (rangeMax) && c < ArraySize (rangeMin))
      {
        double maxAlpha = 0.2 * (rangeMax [c] - rangeMin [c]);
        double minAlpha = 0.0001 * (rangeMax [c] - rangeMin [c]);

        if (alpha [c] > maxAlpha)
        {
          alpha [c] = maxAlpha;
        }
        else
          if (alpha [c] < minAlpha)
          {
            alpha [c] = minAlpha;
          }
      }
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

Теперь, когда все методы мы разобрали, можем перейти непосредственно к тестированию алгоритма.


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

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

COA(CHAOS)|Chaos Optimization Algorithm|50.0|10.0|40.0|2.0|1.2|0.0001|
=============================
5 Hilly's; Func runs: 10000; result: 0.6083668756744218
25 Hilly's; Func runs: 10000; result: 0.4079413401686229
500 Hilly's; Func runs: 10000; result: 0.2678056430575926
=============================
5 Forest's; Func runs: 10000; result: 0.668343763134901
25 Forest's; Func runs: 10000; result: 0.38894787694645416
500 Forest's; Func runs: 10000; result: 0.2198998763835145
=============================
5 Megacity's; Func runs: 10000; result: 0.32307692307692304
25 Megacity's; Func runs: 10000; result: 0.1987692307692308
500 Megacity's; Func runs: 10000; result: 0.10598461538461638
=============================
All score: 3.18914 (35.43%)

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

Hilly

COA(CHAOS) на тестовой функции Hilly

Forest

COA(CHAOS) на тестовой функции Forest

Megacity

COA(CHAOS) на тестовой функции Megacity

По результатам тестирования алгоритм COA(CHAOS) представлен информационно в нашей рейтинговой таблице.

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


Выводы

Результат в 35% после тестирования показывает неплохой потенциал алгоритма COA(CHAOS), особенно учитывая его сложность и количество настраиваемых внешних параметров, в том числе внутренних, которые не оптимизировались. Алгоритм просто не показал еще свою максимальную производительность.

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

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

Tab

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

chart

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

Плюсы и минусы алгоритма COA(CHAOS):

Плюсы:

  1. Разнообразие поисковых методов.
  2. Перспективная разработка.
  3. Стабильные результаты.

Минусы:

  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_COA_chaos.mq5
СкриптИспытательный стенд для COA(CHAOS)
Прикрепленные файлы |
COAcCHAOSd.ZIP (268.29 KB)
MQL5-советник, интегрированный в Telegram (Часть 7): Анализ команд для автоматизации индикаторов на графиках MQL5-советник, интегрированный в Telegram (Часть 7): Анализ команд для автоматизации индикаторов на графиках
В этой статье мы узнаем, как интегрировать команды Telegram с MQL5 для автоматизации добавления индикаторов на торговые графики. Мы рассмотрим процесс анализа пользовательских команд, их выполнение на языке MQL5 и тестирование системы для обеспечения бесперебойной торговли на основе индикаторов.
Скальпинг по потоку ордеров (Order Flow Scalping) с MQL5 Скальпинг по потоку ордеров (Order Flow Scalping) с MQL5
Данный советник для MetaTrader 5 реализует стратегию Scalping OrderFlow (стратегия скальпирования потока ордеров) с расширенным управлением рисками. В нем используется множество технических индикаторов для определения торговых возможностей на основе дисбалансов в потоке ордеров. Бэк-тестирование показывает потенциальную прибыльность, но подчеркивает необходимость дальнейшей оптимизации, особенно в области управления рисками и соотношения результатов торговли. Он подходит для опытных трейдеров и требует тщательного тестирования и понимания перед практическим применением.
Компьютерное зрение для трейдинга (Часть 1): Создаем базовый простой функционал Компьютерное зрение для трейдинга (Часть 1): Создаем базовый простой функционал
Система прогнозирования EURUSD с применением компьютерного зрения и глубокого обучения. Узнайте, как сверточные нейронные сети могут распознавать сложные ценовые паттерны на валютном рынке и предсказывать движение курса с точностью до 54%. Статья раскрывает методологию создания алгоритма, использующего технологии искусственного интеллекта для визуального анализа графиков вместо традиционных технических индикаторов. Автор демонстрирует процесс трансформации ценовых данных в «изображения», их обработку нейронной сетью и уникальную возможность заглянуть в «сознание» ИИ через карты активации и тепловые карты внимания. Практический код на Python с использованием библиотеки MetaTrader 5 позволяет читателям воспроизвести систему и применить ее в собственной торговле.
Прогнозируем Ренко — бары при помощи ИИ CatBoost Прогнозируем Ренко — бары при помощи ИИ CatBoost
Как использовать Ренко-бары вместе с ИИ? Рассмотрим Ренко-трейдинг на Форекс с точностью прогнозов до 59.27%. Исследуем преимущества Ренко-баров для фильтрации рыночного шума, узнаем, почему объемные показатели важнее ценовых паттернов, и как настроить оптимальный размер блока Ренко для EURUSD. Пошаговое руководство по интеграции CatBoost, Python и MetaTrader 5 для создания собственной системы прогнозирования Ренко Форекс. Идеально для трейдеров, стремящихся выйти за рамки традиционного технического анализа.