preview
Алгоритм искусственного электрического поля — Artificial Electric Field Algorithm (AEFA)

Алгоритм искусственного электрического поля — Artificial Electric Field Algorithm (AEFA)

MetaTrader 5Примеры | 12 июля 2024, 09:35
454 0
Andrey Dik
Andrey Dik
Содержание
  1. Введение
  2. Реализация алгоритма
  3. Результаты тестов


Введение

Алгоритм искусственного электрического поля — это удивительное творение, олицетворяющее в себе гармонию технологий и природы. Вдохновленный законом Кулона об электростатической силе, этот алгоритм привлекает внимание своей уникальной способностью моделировать электрические явления для решения сложных задач оптимизации. В контексте уже известных и описанных ранее в статьях алгоритмов, связанных с законами природы, таких как Charged System Search (CSS), ElectroMagnetism-like algorithm (ЕМ), Gravitational Search Algorithm (GSA) и других, искусственное электрическое поле представляет собой захватывающее новшество, которое не оставит равнодушным ни одного исследователя. И так случилось, что я не смог пройти мимо...

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

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

Авторами алгоритма AEFA (Artificial Electric Field Algorithm) являются:

1. Anita - из Департамента наук и гуманитарных наук, Национального технологического института Уттаракханда, Шринагар, Индия.
2. Anupam Yadav - из Департамента математики, Национального технологического института доктора Б.Р. Амбедкара, Джаландхар, Индия.

Алгоритм AEFA был предложен в 2019 году и опубликован в журнале "Swarm and Evolutionary Computation". Авторы статьи отмечают, что концепция электрического поля и заряженных частиц дает нам сильную теорию для работы силы притяжения или отталкивания между двумя заряженными частицами. Таким образом, AEFA использует принципы электростатической силы для разработки популяционного оптимизационного алгоритма.


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

Алгоритм AEFA богат различными формулами и перед тем, как мы перейдём к их рассмотрению, отметим его ключевые моменты:

  • Агенты алгоритма (решения) представлены как заряженные частицы, движущиеся под действием электростатической силы.
  • Заряд частицы зависит от ее личного лучшего результата и текущих лучшего и худшего результатов в популяции (не лучших результатов, достигнутых на протяжении оптимизации, а именно текущих по популяции).
  • Сила, действующая на частицу, зависит от зарядов частиц и расстояния между ними.
  • Константа Кулона K(t) уменьшается с ростом итераций, контролируя баланс между глобальным поиском и локальной оптимизацией.

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

Пусть позиция i-й частицы в d-мерном пространстве поиска обозначается как X_i = (x_1, x_2, ..., x_d), где i = 1, 2, ..., N, где N - размер популяции.
Лучшая позиция, найденная i-й частицей, обозначается как P_i, а глобально лучшая позиция обозначается как P_best.

1. Формула Кулона для электростатической силы: F_ij = K * Q_i * Q_j / R^2, где:

  • F_ij - величина электростатической силы между двумя заряженными объектами i-го и j-го
  • K - постоянная Кулона
  • Q_i и Q_j - заряды i-го и j-го объектов соответственно
  • R - расстояние между двумя зарядами Q_i и Q_j

2. Формула электрического поля вокруг заряда Q_i: E_i = F_ij / Q_i, где:

  • E_i - электрическое поле вокруг заряда Q_i
  • F_ij - сила, действующая на заряд Q_i

3. Формула ускорения частицы по второму закону Ньютона: a_i = F_ij / M, где:

  • a_i - ускорение i-й частицы
  • F_ij - сила, действующая на частицу
  • M - масса частицы

4. Формула ускорения частицы через электрическое поле: a_i = E_i * Q_i / M, где:

  • a_i - ускорение i-й частицы
  • E_i - электрическое поле вокруг частицы
  • Q_i - заряд частицы
  • M - масса частицы

5. Формула обновления лучшей позиции частицы: p_di (t+1) = {p_di (t) и X_i (t+1) = X_i (t)}, если f (X_i (t+1)) > f (X_i (t)), где:

  • p_di (t) - значение приспособленности i-й частицы на момент времени t
  • p_di (t+1) - значение приспособленности i-й частицы на момент времени (t+1)
  • X_i (t) - позиция i-й частицы на момент времени t
  • X_i (t+1) - новая позиция i-й частицы на момент времени (t+1)
  • f(X_i (t)) - значение целевой функции на предыдущей позиции i-й частицы на момент времени t
  • f(X_i (t+1)) - значение целевой функции в новой позиции i-й частицы на момент времени (t+1)

6. Формула силы, действующей на i-ю частицу со стороны j-й частицы: F_dij (t) = K (t) * (Q_i (t) * Q_j (t) * (p_dj (t) - X_di (t))) / (R_ij (t)^2 + ε), где:

  • F_dij (t) - сила, действующая на i-ю частицу со стороны j-й частицы на момент времени t
  • K (t) - постоянная Кулона в момент времени t
  • Q_i (t) и Q_j (t) - заряды i-й и j-й частиц в момент времени t
  • p_dj (t) - лучшая позиция j-й частицы на момент времени t
  • X_di (t) - позиция i-й частицы на момент времени t
  • R_ij (t) - евклидово расстояние между i-й и j-й частицами в момент времени t
  • ε - малая положительная константа, используемая для предотвращения деления на 0.

7. Формула евклидова расстояния между i-й и j-й частицами: R_ij (t) = (X_i (t) - X_j (t))^2

8. Формула постоянной Кулона: K (t) = K_0 * exp (-α * t / maxiter), где:

  • K_0 - начальное значение постоянной Кулона
  • α - внешний параметр
  • t - текущий момент времени (эпоха)
  • maxiter - максимальное число итераций

9. Формула суммарной силы, действующей на i-ю частицу: F_di (t) = Σ (rand () * F_dij (t)), j=(от 1 до N), j≠i, где:

  • F_di (t) - суммарная сила, действующая на i-ю частицу на момент времени t
  • rand () - случайное число в интервале [0, 1]
  • N - число частиц в пространстве поиска

10. Формула электрического поля, действующего на i-ю частицу: E_di (t) = F_di (t) / Q_i (t), где:

  • E_di (t) - электрическое поле, действующее на i-ю частицу на момент времени t
  • F_di (t) - суммарная сила, действующая на i-ю частицу
  • Q_i (t) - заряд i-й частицы в момент времени t

11. Формула обновляет скорость частицы i в момент времени t+1: V_i (t+1) = rand () * V_i (t) + a_i (t), где:

  • V_i(t) - предыдущая скорость
  • a_i(t) - ускорение, действующее на частицу
  • rand () - случайное число в интервале [0, 1]

12. Формула обновляет положение частицы i в момент времени t+1: X_i (t+1) = X_i (t) + V_i (t+1), где:

  • V_i (t+1) - новая скорость

13. Формула задает, что заряд всех частиц в популяции одинаков и равен Q_i (t) = Q_j (t) для всех i, j = 1, 2,..., N

14. Формула вычисляет относительный заряд q_i (t) каждой частицы i в момент времени tq_i (t) = exp ((fit_p_i (t) - worst (t)) / (best (t) - worst (t))), где:

  • fit_p_i (t) - значение приспособленности личного лучшего положения частицы
  • fit_best (t) -  значение приспособленности глобального лучшего положения
  • fit_worst(t) - значение приспособленности худшей частицы в популяции

15. Формула: Q_i (t) = q_i (t) / Σ_j=1...N q_j (t).
Эта формула нормализует относительные заряды q_i (t) всех частиц, чтобы их сумма была равна 1. Таким образом получаются нормализованные заряды Q_i (t) каждой частицы.

16. Формула: best (t) = max (fit_j (t)) для j = 1, 2,..., N
Эта формула вычисляет текущее лучшее фитнес — значение best (t) в популяции в момент времени t как максимальное из фитнес — значений всех частиц.

17. Формула: worst (t) = min (fit_j (t)) для j = 1, 2,..., N
Эта формула вычисляет текущее худшее фитнес — значение worst (t) в популяции в момент времени t как минимальное из фитнес — значений всех частиц.

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


Псевдокод алгоритма AEFA:

Шаг 1: Инициализация

- Случайно инициализировать позиции частиц.
- Вычислить значения целевой функции для каждой частицы.
- Установить текущую итерацию t = 0.

Шаг 2: Обновление позиций частиц пока не выполнено условие останова:  

1. Вычислить константу Кулона K(t) по формуле (8)
2. Вычислить лучшее fit_best (t) и худшее fit_worst(t) значения целевой функции на текущей итерации.
3. Для каждой частицы i = 1, 2, ..., N:
   a. Вычислить заряд частицы Q_i (t) по формуле (14)
   b. Вычислить силу, действующую на частицу i со стороны частицы j по формуле (6)
   c. Вычислить суммарную силу, действующую на частицу i по формуле (9)
   d. Вычислить ускорение частицы i по формуле (4)
   e. Обновить скорость по формуле (11) и положение частицы i по формуле (12)
4. Увеличить счетчик итераций t = t + 1

Шаг 3: Условие останова. Проверить условие останова (например, достижение максимального числа итераций). Если условие не выполнено, перейти к Шагу 2.

K0

Рисунок 1. Зависимость электростатической силы по формуле Кулона от коэффициента α (внешний параметр).

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

Напишем структуру "S_AEFA_Agent", которая служит для описания агентов в алгоритме. В данной структуре содержатся следующие поля:

  • best_fitness - переменная представляет лучшую приспособленность агента.
  • best_position[] - массив координат лучшей позиции агента в пространстве поиска.
  • velocity[] - массив, представляющий вектор скорости движения частицы.
  • charge - заряд агента.
  • relative_charge - относительный заряд частицы.

Также в структуре определена функция "Init", которая инициализирует частицу (агента). Она принимает параметр "coords", обозначающий количество координат агента, и изменяет размеры массивов "best_position" и "velocity" соответственно. После этого устанавливает "best_fitness" в минимально возможное значение типа double "-DBL_MAX", а "charge" и "relative_charge" в 0.

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

//——————————————————————————————————————————————————————————————————————————————
struct S_AEFA_Agent
{
    double best_fitness;
    double best_position [];
    double velocity      [];
    double charge;
    double relative_charge;

    void Init (int coords)
    {
      ArrayResize (best_position, coords);
      ArrayResize (velocity,      coords);

      best_fitness    = -DBL_MAX;
      charge          = 0;
      relative_charge = 0;
    }
};
//——————————————————————————————————————————————————————————————————————————————

Опишем класс "C_AO_AEFA", который наследуется от класса "C_AO". Внутри класса определены следующие методы и переменные:

  • C_AO_AEFA -  конструктор, в котором устанавливаются значения переменных и параметров.
  • SetParams -  метод устанавливает параметры на основе значений, хранящихся в массиве "params".
  • Init - метод принимает набор значений и выполняет инициализацию.
  • "Moving" и "Revision" - методы выполняют основную логику алгоритма.
  • Несколько переменных типа "double", таких, как "K0", "alpha", "particleMass", "epsilon", а также массив "agent" и другие приватные переменные.
  • Приватные методы "CalculateK", "UpdateCharges", "CalculateForces", "CalculateDistance", "UpdateVelocityAndPosition" выполняют операции по перемещению частиц в пространстве поиска.
//——————————————————————————————————————————————————————————————————————————————
class C_AO_AEFA : public C_AO
{
  public: //--------------------------------------------------------------------
  ~C_AO_AEFA () { }
  C_AO_AEFA ()
  {
    ao_name = "AEFA";
    ao_desc = "Artificial Electric Field Algorithm";
    ao_link = ""; //"https://www.mql5.com/ru/articles/15162";

    popSize      = 50;
    K0           = 500.0;
    alpha        = 20.0;
    particleMass = 1.0;

    ArrayResize (params, 4);

    params [0].name = "popSize";       params [0].val = popSize;
    params [1].name = "K0";            params [1].val = K0;
    params [2].name = "alpha";         params [2].val = alpha;
    params [3].name = "particleMass";  params [3].val = particleMass;
  }

  void SetParams ()
  {
    popSize      = (int)params [0].val;
    K0           = params      [1].val;
    alpha        = params      [2].val;
    particleMass = params      [3].val;
  }

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

  void Moving ();
  void Revision ();

  //----------------------------------------------------------------------------
  double K0;
  double alpha;
  double particleMass;
  double epsilon;

  S_AEFA_Agent agent [];

  private: //-------------------------------------------------------------------
  int    epochs;
  int    epochNow;
  double K;
  double CalculateK                (int t);
  void   UpdateCharges             (double best, double worst);
  void   CalculateForces           ();
  double CalculateDistance         (const double &x1 [], const double &x2 []);
  void   UpdateVelocityAndPosition (int i);
};
//——————————————————————————————————————————————————————————————————————————————

Далее переходим к реализации метода "Init" класса "C_AO_AEFA". В данном методе выполняется инициализация алгоритма AEFA с заданными параметрами.

Входные параметры метода "Init":

  • rangeMinP - массив значений минимальных границ диапазона.
  • rangeMaxP - массив значений максимальных границ диапазона.
  • rangeStepP - массив значений шагов диапазона.
  • epochsP - количество эпох (по умолчанию равно 0).

Действия, выполняемые в методе "Init":

1. Вызывается метод "StandardInit" с передачей ему массивов "rangeMinP", "rangeMaxP" и "rangeStepP". Если метод "StandardInit" возвращает "false", то метод "Init" возвращает "false".
2. Устанавливаются значения переменных "epochs" и "epochNow" равными значениям параметров "epochsP" и "0" соответственно.
3. Выполняется изменение размера массива "agent" до значения "popSize".
4. В цикле происходит инициализация каждого элемента массива "agent" вызовом метода "Init" с передачей ему массива "coords".
5. Устанавливается значение переменной "epsilon" равным "1e-10".
6. Метод возвращает "true".

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

  //----------------------------------------------------------------------------
  epochs   = epochsP;
  epochNow = 0;

  ArrayResize (agent, popSize);
  for (int i = 0; i < popSize; i++) agent [i].Init (coords);

  epsilon = 1e-10;

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

Переходим к методу "Moving()" класса C_AO_AEFA. В этом методе происходит обновление позиций частиц в алгоритме оптимизации. Вот краткое описание того, что происходит:

1. Увеличивается значение переменной "epochNow".
2. Если переменная "revision" равна "false", то происходит инициализация начальных позиций частиц. Каждая координата частицы устанавливается в случайное значение в заданном диапазоне, затем происходит преобразование этого значения в соответствии с заданным шагом.
3. Если переменная "revision" равна "true", то происходит вычисление значения "K", лучшей и худшей оценок функции для каждой частицы, обновление зарядов, вычисление сил и обновление скорости и позиции каждой частицы.

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

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

  //----------------------------------------------------------------------------
  if (!revision)
  {
    for (int i = 0; i < popSize; i++)
    {
      for (int c = 0; c < coords; c++)
      {
        a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]);
        a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]);
      }
    }

    revision = true;
    return;
  }

  //----------------------------------------------------------------------------
  K            = CalculateK (epochNow);
  double best  = -DBL_MAX;
  double worst =  DBL_MAX;

  for (int i = 0; i < popSize; i++)
  {
    if (a [i].f > best)  best  = a [i].f;
    if (a [i].f < worst) worst = a [i].f;
  }

  UpdateCharges   (best, worst);
  CalculateForces ();

  for (int i = 0; i < popSize; i++)
  {
    UpdateVelocityAndPosition (i);
  }
}
//——————————————————————————————————————————————————————————————————————————————

В методе "CalculateK()" класса "C_AO_AEFA" вычисляется значение параметра "K" на основе времени "t" (номер текущей эпохи) и других параметров "K0", "alpha" и "epochs". Вот что происходит в этом методе:

1. Метод принимает на вход параметр "t", который представляет собой номер текущей эпохи в алгоритме.
2. Затем происходит вычисление значения "K".
3. Результат вычисления по формуле возвращается как значение метода.

//——————————————————————————————————————————————————————————————————————————————
double C_AO_AEFA::CalculateK (int t)
{
  return K0 * MathExp (-alpha * t / epochs);
}
//——————————————————————————————————————————————————————————————————————————————

В методе "UpdateCharges()" класса "C_AO_AEFA" происходит обновление зарядов частиц на основе лучшего и худшего значения приспособленности. Краткое описание действий в методе:

1. Создается переменная "sum_q" и устанавливается в "0".
2. Затем происходит цикл по всем частицам в диапазоне от "0" до "popSize".
3. Внутри цикла для каждой частицы вычисляется относительный заряд с использованием формулы.
4. Значение относительного заряда добавляется к переменной "sum_q".
5. Затем происходит второй цикл по всем частицам, в котором каждой частице присваивается значение заряда, равное относительному заряду, деленному на "sum_q".

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

//——————————————————————————————————————————————————————————————————————————————
void C_AO_AEFA::UpdateCharges (double best, double worst)
{
  double sum_q = 0;

  for (int i = 0; i < popSize; i++)
  {
    agent [i].relative_charge = MathExp ((a [i].f - worst) / (best - worst));
    sum_q += agent [i].relative_charge;
  }
  for (int i = 0; i < popSize; i++)
  {
    agent [i].charge = agent [i].relative_charge / sum_q;
  }
}
//——————————————————————————————————————————————————————————————————————————————

Представим методы "CalculateForces()" и "CalculateDistance()" класса "C_AO_AEFA".

Метод "CalculateDistance()" принимает два массива "x1[]" и "x2[]", представляющих координаты двух частиц, и вычисляет расстояние между ними в пространстве. Для этого происходит итерация по всем координатам массивов, и для каждой координаты вычисляется квадрат разности соответствующих элементов массивов, после чего эти квадраты суммируются. Затем из полученной суммы извлекается корень, и это значение возвращается как расстояние между двумя точками в пространстве (Евклидово расстояние).

Метод "CalculateForces()" вычисляет силы, действующие на каждую частицу. Для каждой частицы происходит вычисление вектора сил по отношению ко всем другим частицам. Для каждой пары частиц, кроме одинаковых (i != j), вычисляется расстояние между ними с помощью метода "CalculateDistance()". Затем для каждой координаты пространства вычисляется сила, действующая на частицу, используя формулу, включающую заряды частиц, их положения и расстояние между ними. Результаты суммируются для каждой координаты, и затем полученные силы делятся на заряд частицы, чтобы учесть их влияние на скорость частицы.

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

//——————————————————————————————————————————————————————————————————————————————
void C_AO_AEFA::CalculateForces ()
{
  double force [];
  ArrayResize (force, coords);

  for (int i = 0; i < popSize; i++)
  {
    ArrayInitialize (force, 0);

    for (int j = 0; j < popSize; j++)
    {
      if (i != j)
      {
        double R = CalculateDistance (a [i].c, a [j].c);

        for (int d = 0; d < coords; d++)
        {
          force [d] += u.RNDprobab () * K *
                       (agent [i].charge * agent [j].charge * (agent [j].best_position [d] - a [i].c [d])) /
                       (R * R + epsilon);
        }
      }
    }

    for (int d = 0; d < coords; d++)
    {
      agent [i].velocity [d] = force [d] / agent [i].charge;
    }
  }
}
//——————————————————————————————————————————————————————————————————————————————

//——————————————————————————————————————————————————————————————————————————————
double C_AO_AEFA::CalculateDistance (const double &x1 [], const double &x2 [])
{
  double sum = 0;
  for (int d = 0; d < coords; d++)
  {
    sum += (x1 [d] - x2 [d]) * (x1 [d] - x2 [d]);
  }
  return MathSqrt (sum);
}
//——————————————————————————————————————————————————————————————————————————————

Далее представляем метод "UpdateVelocityAndPosition()" класса "C_AO_AEFA". Этот метод обновляет скорость и позицию частицы с индексом "i". Для каждой координаты частицы в пространстве происходит следующее:

1. Вычисляется ускорение, которое зависит от заряда частицы, её текущей скорости и массы частицы.
2. Скорость частицы обновляется, добавляя случайную составляющую, умноженную на текущую скорость, и ускорение.
3. Позиция частицы обновляется, добавляя к текущей позиции новую скорость. Затем новая позиция ограничивается в пределах заданных минимальных и максимальных значений для каждой координаты с помощью метода "u.SeInDiSp()".

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

//——————————————————————————————————————————————————————————————————————————————
void C_AO_AEFA::UpdateVelocityAndPosition (int i)
{
  for (int d = 0; d < coords; d++)
  {
    double acceleration = (agent [i].charge * agent [i].velocity [d]) / particleMass;

    agent [i].velocity [d] = (u.RNDfromCI (0, 1)) * agent [i].velocity [d] + acceleration;

    a [i].c [d] += agent [i].velocity [d];
    a [i].c [d] = u.SeInDiSp (a [i].c [d], rangeMin [d], rangeMax [d], rangeStep [d]);
  }
}
//——————————————————————————————————————————————————————————————————————————————

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

1. Создается переменная "ind" как флаг и индекс положения в массиве лучшего решения и устанавливается в "-1".
2. Затем происходит цикл по всем частицам в диапазоне от "0" до "popSize".
3. Внутри цикла проверяется, превышает ли значение фитнес — функции частицы значение "fB". Если да, то "fB" обновляется, и переменная "ind" устанавливается в значение "i".
4. Затем проверяется, превышает ли приспособленность частицы лучшую приспособленность этой частицы на протяжении всех эпох (хранящуюся в agent[i].best_fitness). Если да, то лучшая оценка обновляется, и лучшая позиция также обновляется.
5. Наконец, если "ind" не равно "-1", то позиция "cB" обновляется копированием позиции частицы с индексом "ind".

//——————————————————————————————————————————————————————————————————————————————
void C_AO_AEFA::Revision ()
{
  int ind = -1;

  for (int i = 0; i < popSize; i++)
  {
    if (a [i].f > fB)
    {
      fB = a [i].f;
      ind = i;
    }

    if (a [i].f > agent [i].best_fitness)
    {
      agent [i].best_fitness = a [i].f;
      ArrayCopy (agent [i].best_position, a [i].c, 0, 0, WHOLE_ARRAY);
    }
  }

  if (ind != -1) ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY);
}
//——————————————————————————————————————————————————————————————————————————————


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

Распечатка работы алгоритма AEFA на стенде с тестовыми функциями:

AEFA|Artificial Electric Field Algorithm|20.0|1000.0|10.0|100.0|
=============================
5 Hilly's; Func runs: 10000; result: 0.8769988087850553
25 Hilly's; Func runs: 10000; result: 0.617530930198765
500 Hilly's; Func runs: 10000; result: 0.2523539056281608
=============================
5 Forest's; Func runs: 10000; result: 0.927287032866128
25 Forest's; Func runs: 10000; result: 0.7269761843712702
500 Forest's; Func runs: 10000; result: 0.18063577020760296
=============================
5 Megacity's; Func runs: 10000; result: 0.6661538461538462
25 Megacity's; Func runs: 10000; result: 0.11630769230769236
500 Megacity's; Func runs: 10000; result: 0.0950769230769239
=============================
All score: 4.45932 (49.55%)

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

Hilly

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

Forest

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

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

Хотелось бы выделить один важный аспект алгоритма AEFA. После проведения стандартных исследований с 10'000 запусков фитнес функции, было принято решение провести дополнительный эксперимент с увеличением количества запусков до 100'000, и результат превзошел мои ожидания. На многих функциях с небольшим количеством параметров алгоритм достиг 100% сходимости при увеличении количества запусков фитнес функции. Важно отметить, что не все алгоритмы в рейтинговой таблице, даже занимающие верхние позиции, могут достичь 100% сходимости при увеличении количества запусков, поскольку застревают в локальных экстремумах. В данном случае алгоритм демонстрирует устойчивость к застреванию. Тем не менее алгоритм сталкивается с теми же трудностями при поиске в многомерных пространствах, особенно это заметно на дискретных функциях.

Распечатка работы алгоритма AEFA на стенде с запуском 100'000 тестовых функций:

SDSm|Stochastic Diffusion Search M|100.0|100.0|0.05|
=============================
5 Hilly's; Func runs: 100000; result: 0.9874670077970368
25 Hilly's; Func runs: 100000; result: 0.9355482229513405
500 Hilly's; Func runs: 100000; result: 0.5943074120378588
=============================
5 Forest's; Func runs: 100000; result: 0.994126703499673
25 Forest's; Func runs: 100000; result: 0.9627011069578397
500 Forest's; Func runs: 100000; result: 0.5628894538341265
=============================
5 Megacity's; Func runs: 100000; result: 0.9015384615384615
25 Megacity's; Func runs: 100000; result: 0.7264615384615385
500 Megacity's; Func runs: 100000; result: 0.37464615384615396
=============================
All score: 7.03969 (78.22%)

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

MrunsHilly

MrunsForest

MrunsMegacity

Рейтинговая таблица популяционных алгоритмов оптимизации:

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 BGA binary genetic algorithm 0,99989 0,99518 0,42835 2,42341 0,96153 0,96181 0,32027 2,24360 0,91385 0,95908 0,24220 2,11512 6,782 75,36
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 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 (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
5 CTA comet tail algorithm 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
6 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
7 ESG evolution of social groups 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
8 SIA simulated isotropic annealing 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
9 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
10 TSEA turtle shell evolution algorithm 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
11 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
12 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
13 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
14 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
15 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
16 (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
17 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
18 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
19 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
20 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
21 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
22 MEC mind evolutionary computation 0,69533 0,53376 0,32661 1,55569 0,72464 0,33036 0,07198 1,12698 0,52500 0,22000 0,04198 0,78698 3,470 38,55
23 IWO invasive weed optimization 0,72679 0,52256 0,33123 1,58058 0,70756 0,33955 0,07484 1,12196 0,42333 0,23067 0,04617 0,70017 3,403 37,81
24 Micro-AIS micro artificial immune system 0,79547 0,51922 0,30861 1,62330 0,72956 0,36879 0,09398 1,19233 0,37667 0,15867 0,02802 0,56335 3,379 37,54
25 COAm cuckoo optimization algorithm M 0,75820 0,48652 0,31369 1,55841 0,74054 0,28051 0,05599 1,07704 0,50500 0,17467 0,03380 0,71347 3,349 37,21
26 SDOm spiral dynamics optimization M 0,74601 0,44623 0,29687 1,48912 0,70204 0,34678 0,10944 1,15826 0,42833 0,16767 0,03663 0,63263 3,280 36,44
27 NMm Nelder-Mead method M 0,73807 0,50598 0,31342 1,55747 0,63674 0,28302 0,08221 1,00197 0,44667 0,18667 0,04028 0,67362 3,233 35,92
28 FAm firefly algorithm M 0,58634 0,47228 0,32276 1,38138 0,68467 0,37439 0,10908 1,16814 0,28667 0,16467 0,04722 0,49855 3,048 33,87
29 GSA gravitational search algorithm 0,64757 0,49197 0,30062 1,44016 0,53962 0,36353 0,09945 1,00260 0,32667 0,12200 0,01917 0,46783 2,911 32,34
30 BFO bacterial foraging optimization 0,61171 0,43270 0,31318 1,35759 0,54410 0,21511 0,05676 0,81597 0,42167 0,13800 0,03195 0,59162 2,765 30,72
31 ABC artificial bee colony 0,63377 0,42402 0,30892 1,36671 0,55103 0,21874 0,05623 0,82600 0,34000 0,14200 0,03102 0,51302 2,706 30,06
32 BA bat algorithm 0,59761 0,45911 0,35242 1,40915 0,40321 0,19313 0,07175 0,66810 0,21000 0,10100 0,03517 0,34617 2,423 26,93
33 SA simulated annealing 0,55787 0,42177 0,31549 1,29513 0,34998 0,15259 0,05023 0,55280 0,31167 0,10033 0,02883 0,44083 2,289 25,43
34 IWDm intelligent water drops M 0,54501 0,37897 0,30124 1,22522 0,46104 0,14704 0,04369 0,65177 0,25833 0,09700 0,02308 0,37842 2,255 25,06
35 PSO particle swarm optimisation 0,59726 0,36923 0,29928 1,26577 0,37237 0,16324 0,07010 0,60572 0,25667 0,08000 0,02157 0,35823 2,230 24,77
36 Boids boids algorithm 0,43340 0,30581 0,25425 0,99346 0,35718 0,20160 0,15708 0,71586 0,27846 0,14277 0,09834 0,51957 2,229 24,77
37 MA monkey algorithm 0,59107 0,42681 0,31816 1,33604 0,31138 0,14069 0,06612 0,51819 0,22833 0,08567 0,02790 0,34190 2,196 24,40
38 SFL shuffled frog-leaping 0,53925 0,35816 0,29809 1,19551 0,37141 0,11427 0,04051 0,52618 0,27167 0,08667 0,02402 0,38235 2,104 23,38
39 FSS fish school search 0,55669 0,39992 0,31172 1,26833 0,31009 0,11889 0,04569 0,47467 0,21167 0,07633 0,02488 0,31288 2,056 22,84
40 RND random 0,52033 0,36068 0,30133 1,18234 0,31335 0,11787 0,04354 0,47476 0,25333 0,07933 0,02382 0,35648 2,014 22,37
41 GWO grey wolf optimizer 0,59169 0,36561 0,29595 1,25326 0,24499 0,09047 0,03612 0,37158 0,27667 0,08567 0,02170 0,38403 2,009 22,32
42 CSS charged system search 0,44252 0,35454 0,35201 1,14907 0,24140 0,11345 0,06814 0,42299 0,18333 0,06300 0,02322 0,26955 1,842 20,46
43 EM electroMagnetism-like algorithm 0,46250 0,34594 0,32285 1,13129 0,21245 0,09783 0,10057 0,41085 0,15667 0,06033 0,02712 0,24412 1,786 19,85


Выводы

Исходя из результатов работы алгоритма Artificial Electric Field Algorithm (AEFA) на тестовых функциях разной размерности, можно сделать следующие выводы:

1. AEFA показывает удовлетворительные результаты на различных тестовых функциях, включая Hilly, Forest и Megacity. Однако результаты на дискретной функции Megacity уступают по сравнению с другими функциями.
2. Алгоритм демонстрирует хорошую производительность при большом количестве запусков функций (100'000), что позволяет улучшить точность сходимости вплоть до 100% на малых размерностях.
3. AEFA показывает общий балл 4.45932 (49.55%) и занимает 19 место в рейтинговой таблице, находясь где-то в середине списка среди популяционных алгоритмов оптимизации.

Несмотря на то, что в рамках нашего стандартного тестирования алгоритм AEFA демонстрирует не самые лучшие результаты на тестовых функциях различной размерности, однако обладает дополнительным преимуществом с ростом запусков фитнес — функции добивается общего впечатляющего результата 7.03969 (78.22%), что делает его востребованным для задач с небольшой размерностью, в особенности имеющих гладкую поверхность.

Tab

Рисунок 2. Цветовая градация алгоритмов по соответствующим тестам. Белым цветом подсвечены результаты больше или равные 0.99

chart

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

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


Плюсы и минусы алгоритма искусственного электрического поля (AEFA):

Плюсы:

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

Минусы:

  1. Большой разброс результатов на функциях нашего стандартного тестирования.
  2. Слабые результаты на дискретных функциях.
  3. Очень низкая масштабируемость.
  4. Сложная реализация.

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

Прикрепленные файлы |
AEFA.ZIP (25.91 KB)
Разрабатываем мультивалютный советник (Часть 14): Адаптивное изменение объёмов в риск-менеджере Разрабатываем мультивалютный советник (Часть 14): Адаптивное изменение объёмов в риск-менеджере
Разработанный ранее риск-менеджер содержал только базовую функциональность. Попробуем рассмотреть возможные пути его развития, позволяющие повысить торговые результаты без вмешательства в логику торговых стратегий.
Модель глубокого обучения GRU на Python с использованием ONNX в советнике, GRU vs LSTM Модель глубокого обучения GRU на Python с использованием ONNX в советнике, GRU vs LSTM
Статья посвящена разработке модели глубокого обучения GRU ONNX на Python. В практической части мы реализуем эту модель в торговом советнике, а затем сравним работу модели GRU с LSTM (долгой краткосрочной памятью).
Проблема разногласий: объяснимость и объяснители в ИИ Проблема разногласий: объяснимость и объяснители в ИИ
В этой статье мы будем говорить о проблемах, связанных с объяснителями и объяснимостью в ИИ. Модели ИИ часто принимают решения, которые трудно объяснить. Более того, использование нескольких объяснителей часто приводит к так называемой "проблеме разногласий". А ведь ясное понимание того, как работают модели, является ключевым для повышения доверия к ИИ.
Нейросети в трейдинге: Модель двойного внимания для прогнозирования трендов Нейросети в трейдинге: Модель двойного внимания для прогнозирования трендов
Продолжаем разговор об использовании кусочно-линейного представления временных рядов, начатый в предыдущей статье. И сегодня мы поговорим о комбинировании данного метода с другими подходами к анализу временных рядов для повышения качества прогнозирования трендов ценовых движений.