Алгоритм стрельбы из лука — Archery Algorithm (AA)
Содержание
Введение
В условиях, когда задачи становятся всё более сложными, а ресурсы всё более дефицитными, оптимизация становится не только необходимостью, но и настоящим алгоритмическим искусством в современном мире. Как найти наилучшее решение среди множества возможных? Как минимизировать затраты, повысить эффективность и достичь максимальной прибыли? Эти вопросы волнуют специалистов в самых разных областях — от экономики до инженерии, от социальных систем до экологии. Прежде чем приступить к решению задачи оптимизации, важно правильно смоделировать проблему, определив ключевые переменные и математические взаимосвязи, которые позволят адекватно воспроизвести реальность. Оптимизация находит широкое применение в финансах и трейдинге, помогая не только разрабатывать новые инвестиционные стратегии, но и совершенствовать уже существующие. Однако, несмотря на универсальность подходов, методы оптимизации можно условно разделить на две категории: детерминированные и стохастические.
Детерминированные методы, такие как градиентный спуск, предлагают строгие и предсказуемые решения, используя математические производные для нахождения оптимума, что позволяет эффективно моделировать различные сценарии, однако, как только задачи становятся нелинейными или многомерными, их эффективность может резко снизиться. В таких случаях на помощь приходят стохастические методы, которые, полагаясь на случайные процессы, способны находить приемлемые решения в сложных условиях, что делает их особенно полезными в условиях изменчивости рынков.Сочетания детерминированных и стохастических методов играют ключевую роль в современных подходах к оптимизации. Комбинируя эти два подхода, аналитики могут создавать более гибкие и адаптивные модели, которые способны учитывать как стабильные, так и изменяющиеся условия. Это позволяет не только улучшить качество прогнозов, но и минимизировать риски, что является критически важным для успешного управления инвестициями.
В данной статье мы представим новый подход к решению задач оптимизации — Алгоритм стрельбы из лука (Archery Algorithm, AA). Алгоритм был разработан Фатеме Ахмади Зейдабади с коллегами и был опубликован в феврале 2022 года. Этот метод, вдохновленный искусством стрельбы из лука, предлагает квази-оптимальные решения, обновляя положение членов популяции в пространстве поиска на основе случайно выбранного элемента. Мы протестируем эффективность AA на стандартных целевых функциях и сравним полученные результаты с уже известными нам алгоритмами. Погружаясь в детали, мы раскроем, как этот инновационный подход может изменить наше восприятие оптимизации и открыть новые горизонты для решения сложных задач.
Реализация алгоритма
Алгоритм стрельбы из лука (AA) — это совершенно новый стохастический метод оптимизации, разработанный для нахождения оптимальных решений в задачах оптимизации и вдохновленный поведением стрелка, нацеливающегося на мишень. AA имитирует процесс стрельбы стрелами по мишени. Каждый член популяции представляет собой потенциальное решение задачи оптимизации, и их позиции в пространстве поиска обновляются на основе производительности случайно выбранного "целевого" члена, что похоже на то, как стрелок корректирует прицел в зависимости от того, куда он хочет попасть.
Популяция представлена в виде матрицы, где каждая строка соответствует члену (решению), а каждый столбец — измерению задачи. Это позволяет структурировано оценивать и обновлять решения на основе их значений целевой функции. Эффективность каждого члена оценивается с помощью целевой функции, которая количественно определяет, насколько хорошее решение найдено. Результаты хранятся в векторе, что позволяет алгоритму сравнивать эффективность различных решений.
Целевая мишень делится на секции, ширина каждой из которых соответствует производительности членов популяции. Рассчитывается функция вероятности, чтобы определить вероятность выбора каждого члена на основе его значения целевой функции, при этом более эффективные лучники имеют большую вероятность быть выбранными. Член популяции случайным образом выбирается на основе накопленной вероятности, имитируя выбор цели стрелком. Этот выбор влияет на то, как позиции других членов обновляются. Алгоритм обновляет позицию каждого стрелка в пространстве поиска с использованием определенных уравнений. Обновление зависит от того, имеет ли выбранный лучник лучшее или худшее значение целевой функции по сравнению с текущим. Этот процесс включает в себя случайность для исследования пространства поиска. AA работает итеративно, обновляя популяцию до тех пор, пока не будет достигнуто условие остановки (максимальное количество итераций). В течение этого процесса алгоритм отслеживает лучшее найденное решение.
В представленной выше оригинальной версии алгоритма AA описывается матрица как популяция, а векторы как члены популяции. Однако в тексте не указаны конкретные операции, характерные для работы с матрицами. На самом деле, алгоритм включает в себя стандартные действия с поисковыми агентами, как и в большинстве ранее рассмотренных алгоритмов.
Также стоит отметить, что фраза "целевая мишень делится на секции, ширина каждой из которых соответствует производительности членов популяции" подразумевает использование метода рулетки для отбора. В этом случае вероятность выбора сектора пропорциональна его ширине.
Таким образом, сложные формулировки, описывающие многие концепции, можно объяснить гораздо проще, что может значительно облегчить реализацию идеи.
Итак, алгоритм стрельбы из лука — это метод оптимизации на основе популяции, который использует принципы стрельбы по мишеням для управления поиском оптимальных решений. Он сочетает элементы случайности по нормальному распределению для исследования и эксплуатации пространства поиска. Ключевые компоненты алгоритма:
1. Популяция агентов (лучников)
2. Вектор вероятностей и кумулятивных вероятностей
3. Механизм наследования (отсутствует в оригинальной версии)
4. Механизм обновления позиций
5. Параметр интенсивности обучения (I)
Для начала представим псевдокод алгоритма:
Инициализация:
Создать популяцию из popSize агентов
Для каждого агента:
Инициализировать случайную позицию в пределах диапазона поиска
Инициализировать предыдущую позицию и приспособленность
Главный цикл:
Пока не достигнуто условие остановки:
Для каждого агента i в популяции:
Рассчитать вектор вероятностей P и кумулятивных вероятностей C
Для каждой координаты c:
Выбрать лучника k с использованием кумулятивной вероятности
Если (случайное_число < вероятность_наследования):
новая_позиция [c] = позиция_лучника_k [c]
Иначе:
I = округление (1 + случайное_число_от_0_до_1) // Параметр интенсивности обучения
случайное_гауссово = сгенерировать_гауссово_число (среднее = 0, мин =-1, макс = 1)
Если (приспособленность_лучника_k > приспособленность_агента_i):
новая_позиция [c] = предыдущая_позиция [c] + случайное_гауссово * (позиция_лучника_k [c] - I * предыдущая_позиция [c])
Иначе:
новая_позиция [c] = предыдущая_позиция [c] + случайное_гауссово * (предыдущая_позиция [c] - I * позиция_лучника_k [c])
Ограничить новую_позицию [c] в пределах диапазона поиска
Обновить позицию агента i
Оценить приспособленность всех агентов
Обновить лучшее глобальное решение
Для каждого агента:
Если новая приспособленность лучше предыдущей:
Обновить предыдущую позицию и приспособленность
Вернуть лучшее найденное решение
Особенности реализации в коде:
1. Алгоритм использует вероятностный подход для выбора лучников, у которых нужно учиться.
2. Механизм наследования позволяет агентам напрямую копировать позиции успешных лучников с некоторой вероятностью.
3. При обновлении позиций используется гауссово распределение для внесения случайности в процесс обучения лучников.
4. Алгоритм сохраняет предыдущие лучшие позиции агентов, что позволяет ему иметь некоторую "память" о хороших решениях.
5. Реализация будет включать механизм ограничения новых позиций в пределах допустимого диапазона поиска.
6. Параметр интенсивности обучения (I), описанный авторами будет использоваться для регулирования степени влияния текущей позиции на новую.
Параметр I (интенсивность обучения) — это случайная величина, которая может принимать значение 1 или 2. Он определяется следующим образом: I = округление до целого числа (1 + случайно сгенерированное число от 0 до 1). Это означает, что I будет равно 1 с вероятностью 0.5 и 2 с той же вероятностью. Роль параметра I в алгоритме:
1. Когда I = 1, алгоритм производит более мелкие корректировки позиции.
2. Когда I = 2, алгоритм может сделать более резкие изменения в позиции.
Переходим непосредственно к написанию кода алгоритма. Опишем структуру "лучник" - "S_AA_Agent", она представляет собой агента в алгоритме оптимизации с множеством координат в пространстве решений и содержит информацию о своей эффективности по фитнес-функции.
- cPrev [] - массив хранит предыдущие координаты агента.
- fPrev - переменная хранит предыдущее значение "фитнеса" (fitness) агента.
Метод "Init" позволяет подготовить агента к работе, задав начальные значения для его координат и фитнеса. Далее устанавливается значение "fPrev" в минимально возможное значение для типа "double", потому что приспособленность еще не была рассчитана.
//—————————————————————————————————————————————————————————————————————————————— struct S_AA_Agent { double cPrev []; // previous coordinates double fPrev; // previous fitness void Init (int coords) { ArrayResize (cPrev, coords); fPrev = -DBL_MAX; } }; //——————————————————————————————————————————————————————————————————————————————
Давайте разберем класс "C_AO_AAm", который реализует сам алгоритм и наследуется от класса "C_AO".
- popSize - указывает на размер популяции.
- inhProbab - представляет собой значение вероятности наследования признака от другого лучника.
Затем происходит инициализация массива "params" размером 2, где сохраняются параметры алгоритма: размер популяции и вероятность наследования.
- SetParams - метод устанавливает параметры алгоритма на основе значений, хранящихся в массиве "params". Он извлекает значения для "popSize" и "inhProbab", преобразуя их в соответствующие типы.
- Init - метод инициализирует алгоритм, принимая минимальные и максимальные границы поиска, шаг поиска и количество эпох.
- Moving и Revision - методы отвечают за логику перемещения агентов в пространстве решений и их ревизию (обновление).
S_AA_Agent agent [] - массив агентов, которые будут использоваться для выполнения оптимизации.
Класс "C_AO_AAm" реализует алгоритм оптимизации, а методы "SetParams", "Init", "Moving" и "Revision" управляют настройкой и поведением алгоритма в процессе его работы.
//—————————————————————————————————————————————————————————————————————————————— class C_AO_AAm : public C_AO { public: //-------------------------------------------------------------------- ~C_AO_AAm () { } C_AO_AAm () { ao_name = "AAm"; ao_desc = "Archery Algorithm M"; ao_link = "https://www.mql5.com/ru/articles/15782"; popSize = 50; // population size inhProbab = 0.3; ArrayResize (params, 2); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "inhProbab"; params [1].val = inhProbab; } void SetParams () { popSize = (int)params [0].val; inhProbab = params [1].val; } bool Init (const double &rangeMinP [], // minimum search range const double &rangeMaxP [], // maximum search range const double &rangeStepP [], // step search const int epochsP = 0); // number of epochs void Moving (); void Revision (); //---------------------------------------------------------------------------- double inhProbab; //probability of inheritance S_AA_Agent agent []; private: //------------------------------------------------------------------- }; //——————————————————————————————————————————————————————————————————————————————
Метод "Init" в классе "C_AO_AAm" отвечает за инициализацию алгоритма оптимизации. Он принимает четыре параметра: массивы для минимальных и максимальных границ поиска, шаг поиска и количество эпох, которые по умолчанию равны нулю.
- Если стандартная инициализация прошла успешно, метод затем изменяет размер массива "agent", чтобы он соответствовал установленному размеру популяции "popSize". Это позволяет создать необходимое количество агентов, которые будут использоваться в алгоритме.
- В цикле "for" каждый агент из массива инициализируется с помощью метода "Init", который задает начальные координаты для каждого агента.
В конце метод возвращает "true", указывая на успешное завершение инициализации алгоритма. Таким образом, метод "Init" обеспечивает подготовку алгоритма к работе, настраивая необходимые параметры и создавая агентов, которые будут участвовать в процессе оптимизации.
//—————————————————————————————————————————————————————————————————————————————— bool C_AO_AAm::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //---------------------------------------------------------------------------- ArrayResize (agent, popSize); for (int i = 0; i < popSize; i++) agent [i].Init (coords); return true; } //——————————————————————————————————————————————————————————————————————————————
Метод "Moving" в классе "C_AO_AAm" отвечает за перемещение агентов в пространстве решений, основываясь на их текущих позициях и значениях функции, которую они оптимизируют. Разберем его по частям:
- Если метод вызывается впервые ("revision" равно "false"), то для каждого агента и каждой координаты инициализируется случайное значение в пределах заданных границ "rangeMin" и "rangeMax".
- Затем, это значение корректируется с помощью метода "SeInDiSp", который обеспечивает соответствие значения заданному шагу.
После этого флаг "revision" устанавливается в "true", и метод завершает свою работу.
- Далее создаются два массива: "P" для вероятностей и "C" для кумулятивных вероятностей.
- Находится наихудшее значение функции "F_worst", чтобы нормализовать значения фитнес-функции для агентов.
- Затем вычисляются вероятности для каждого агента, которые нормализуются так, чтобы их сумма равнялась 1.
- Кумулятивные вероятности "C" рассчитываются на основе вероятностей "P".
- Для каждого агента и каждой координаты происходит выбор стрелка-напарника, агента на основе кумулятивной вероятности.
- Если случайное значение меньше заданной вероятности наследования "inhProbab", агент принимает координату выбранного агента (обеспечивая наследование признаков с заданной вероятностью).
- В противном случае, агент обновляет свою позицию на основе формулы, которая учитывает текущее положение, случайное значение и позицию стрелка-напарника.
- Наконец, новое значение координаты также корректируется с помощью метода "SeInDiSp".
Метод "Moving" реализует перемещение агентов в пространстве решений с учетом их текущих позиций и значений функции и использует вероятностные методы для выбора направлений движения и обновления позиций.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_AAm::Moving () { //---------------------------------------------------------------------------- if (!revision) { for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; return; } //------------------------------------------------------------------------- // Calculate probability vector P and cumulative probability C double P [], C []; ArrayResize (P, popSize); ArrayResize (C, popSize); double F_worst = DBL_MAX; double sum = 0; for (int i = 0; i < popSize; i++) { if (a [i].f < F_worst) F_worst = a [i].f; } for (int i = 0; i < popSize; i++) { P [i] = a [i].f - F_worst; sum += P [i]; } for (int i = 0; i < popSize; i++) { P [i] /= sum; C [i] = (i == 0) ? P [i] : C [i - 1] + P [i]; } double x; for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { // Select archer (k) using cumulative probability int k = 0; double r = u.RNDprobab (); while (k < popSize - 1 && C [k] < r) k++; if (u.RNDbool () < inhProbab) { x = a [k].c [c]; } else { // Update position using Eq. (5) and (6) double I = MathRound (1 + u.RNDprobab ()); double rnd = u.GaussDistribution (0, -1, 1, 8); if (a [k].f > a [i].f) { x = agent [i].cPrev [c] + rnd * (a [k].c [c] - I * agent [i].cPrev [c]); } else { x = agent [i].cPrev [c] + rnd * (agent [i].cPrev [c] - I * a [k].c [c]); } } a [i].c [c] = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]); } } } //——————————————————————————————————————————————————————————————————————————————
Метод "Revision" в классе "C_AO_AAm" отвечает за обновление информации о лучших агентах в популяции. Метод выполняет следующие действия:
- Переменная "ind" инициализируется значением "-1". Она будет использоваться для хранения индекса агента с наилучшим значением функции.
- Цикл "for" проходит по всем агентам в популяции "popSize" и если значение функции текущего агента "a [i].f" больше, чем текущее лучшее значение функции "fB":
- обновляется "fB" на новое лучшее значение "a [i].f".
- сохраняется индекс этого агента в переменной "ind".
- Если значение функции текущего агента "a [i].f" больше, чем его предыдущее значение фитнес-функции "agent [i].fPrev":
- обновляется предыдущее значение "fPrev" для этого агента.
- копируются текущие координаты агента в массив "cPrev" с помощью "ArrayCopy".
Метод "Revision" служит для обновления информации о лучшем глобальном решении, а также для обновления лучших позиций агентов.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_AAm::Revision () { //---------------------------------------------------------------------------- int ind = -1; for (int i = 0; i < popSize; i++) { if (a [i].f > fB) { fB = a [i].f; ind = i; } } if (ind != -1) ArrayCopy (cB, a [ind].c, 0, 0, WHOLE_ARRAY); //---------------------------------------------------------------------------- for (int i = 0; i < popSize; i++) { if (a [i].f > agent [i].fPrev) { agent [i].fPrev = a [i].f; ArrayCopy (agent [i].cPrev, a [i].c, 0, 0, WHOLE_ARRAY); } } } //——————————————————————————————————————————————————————————————————————————————
Результаты тестов
Данный алгоритм я немного модифицировал. Оригинальный алгоритм авторов не предусматривает прямого обмена информацией между лучниками. Обмен происходит косвенно через взаимодействие координат посредством нормального распределения, поэтому я подумал, что необходимо добавить обмен такой информацией. Для этого добавлен дополнительный параметр "inhProbab", отвечающий за реализацию такого обмена с заданной вероятностью.
if (u.RNDbool () < inhProbab)
{
x = a [k].c [c];
}
Результаты представленные ниже, соответствуют оригинальной версии алгоритма по замыслу авторов.
AA|Archery Algorithm|50.0|
=============================
5 Hilly's; Func runs: 10000; result: 0.6699547926310098
25 Hilly's; Func runs: 10000; result: 0.37356238340164605
500 Hilly's; Func runs: 10000; result: 0.257542163368952
=============================
5 Forest's; Func runs: 10000; result: 0.38166669771790607
25 Forest's; Func runs: 10000; result: 0.199300365268835
500 Forest's; Func runs: 10000; result: 0.15337954055780398
=============================
5 Megacity's; Func runs: 10000; result: 0.4076923076923077
25 Megacity's; Func runs: 10000; result: 0.17907692307692308
500 Megacity's; Func runs: 10000; result: 0.10004615384615476
=============================
All score: 2.72222 (30.25%)
Алгоритм набирает по итогам тестирования 30.25%, однако с моей модификацией алгоритм улучшил свою работу больше, чем на 13%. Ниже представлены результаты работы модифицированной версии:
AAm|Archery Algorithm M|50.0|0.3|
=============================
5 Hilly's; Func runs: 10000; result: 0.9353194829441194
25 Hilly's; Func runs: 10000; result: 0.6798262991897616
500 Hilly's; Func runs: 10000; result: 0.2596620178276653
=============================
5 Forest's; Func runs: 10000; result: 0.5735062785421186
25 Forest's; Func runs: 10000; result: 0.22007188891556378
500 Forest's; Func runs: 10000; result: 0.1486980566819649
=============================
5 Megacity's; Func runs: 10000; result: 0.6307692307692309
25 Megacity's; Func runs: 10000; result: 0.344
500 Megacity's; Func runs: 10000; result: 0.10193846153846249
=============================
All score: 3.89379 (43.26%)
Итак, я решил остановиться на алгоритме с модификацией и занести его в рейтинговую таблицу. Ниже можно увидеть как работает алгоритм на визуализации. Считаю, что достаточно хорошо, есть конечно разброс результатов, однако, он не является критичным и только на функциях с малым количеством координат.
AAm на тестовой функции Hilly
AAm на тестовой функции Forest
AAm на тестовой функции Megacity
По результатам работы модифицированная версия алгоритма занимает 26 место.
№ | 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 | 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 |
2 | 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 |
3 | 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 |
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 | 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 |
11 | 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 |
12 | 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 |
13 | 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 |
14 | 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 |
15 | 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 |
16 | 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 |
17 | 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 |
18 | (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 |
19 | 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 |
20 | 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 |
21 | 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 |
22 | 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 |
23 | 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 |
24 | 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 |
25 | ABHA | artificial bee hive algorithm | 0,84131 | 0,54227 | 0,26304 | 1,64663 | 0,87858 | 0,47779 | 0,17181 | 1,52818 | 0,50923 | 0,33877 | 0,10397 | 0,95197 | 4,127 | 45,85 |
26 | AAm | archery algorithm M | 0,93532 | 0,67983 | 0,25966 | 1,87481 | 0,57351 | 0,22007 | 0,14870 | 0,94228 | 0,63077 | 0,34400 | 0,10194 | 1,07671 | 3,894 | 43,26 |
27 | ASBO | adaptive social behavior optimization | 0,76331 | 0,49253 | 0,32619 | 1,58202 | 0,79546 | 0,40035 | 0,26097 | 1,45677 | 0,26462 | 0,17169 | 0,18200 | 0,61831 | 3,657 | 40,63 |
28 | 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 |
29 | 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 |
30 | 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 |
31 | 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 |
32 | 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 |
33 | 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 |
34 | 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 |
35 | 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 |
36 | 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 |
37 | 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 |
38 | 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 |
39 | AAA | algae adaptive algorithm | 0,50007 | 0,32040 | 0,25525 | 1,07572 | 0,37021 | 0,22284 | 0,16785 | 0,76089 | 0,27846 | 0,14800 | 0,09755 | 0,52402 | 2,361 | 26,23 |
40 | 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 |
41 | 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 |
42 | 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 |
43 | 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 |
44 | 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 |
45 | 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 |
Выводы
Вашему вниманию представлены две версии алгоритма: авторская и моя, модифицированная, которая включает небольшие изменения, но обеспечивает значительное улучшение результативности. Эта статья наглядно демонстрирует, что даже незначительные коррективы в логике алгоритма могут привести к заметному приросту эффективности в различных задачах. Также становится очевидным, что сложные описания могут затруднять понимание работы алгоритма, что в свою очередь мешает его улучшению. Напротив, сложные концепции, изложенные простым языком, открывают путь к более эффективным решениям.
Рисунок 1. Цветовая градация алгоритмов по соответствующим тестам. Белым цветом подсвечены результаты больше или равные 0.99
Рисунок 2. Гистограмма результатов тестирования алгоритмов (по шкале от 0 до 100, чем больше, тем лучше, где 100 - максимально возможный теоретический результат, в архиве скрипт для расчета рейтинговой таблицы)
Когда статья была практически готова к публикации, у меня возникла идея, которую я решил обязательно проверить. Что если, следуя логике авторов о мишенях и лучниках, использующих метод "рулетка" для отбора, изменять размеры самих мишеней обратно пропорционально качеству найденных решений? Если решение оказывается хорошим, его следует уточнить и исследовать его окрестности. В противном случае, если результат малозначителен, необходимо расширить зону поиска для выявления новых, потенциально перспективных направлений.
Рисунок 3. Количество стрел, попадающих в мишени, прямо пропорционально качеству самих мишеней, в то время как размер мишеней обратно пропорционален их качеству.
Давайте посмотрим на код, использующий идею увеличения мишеней обратно пропорционально их качества.
void C_AO_AAm::Moving () { //---------------------------------------------------------------------------- if (!revision) { for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { a [i].c [c] = u.RNDfromCI (rangeMin [c], rangeMax [c]); a [i].c [c] = u.SeInDiSp (a [i].c [c], rangeMin [c], rangeMax [c], rangeStep [c]); } } revision = true; return; } //------------------------------------------------------------------------- // Calculate probability vector P and cumulative probability C double P [], C []; ArrayResize (P, popSize); ArrayResize (C, popSize); double F_worst = DBL_MAX; // a[ArrayMaximum(a, WHOLE_ARRAY, 0, popSize)].f; double sum = 0; for (int i = 0; i < popSize; i++) { if (a [i].f < F_worst) F_worst = a [i].f; } for (int i = 0; i < popSize; i++) { P [i] = a [i].f - F_worst; ////F_worst - a[i].f; sum += P [i]; } for (int i = 0; i < popSize; i++) { P [i] /= sum; C [i] = (i == 0) ? P [i] : C [i - 1] + P [i]; } double x; double maxFF = fB; double minFF = DBL_MAX; double prob1; double prob2; for (int i = 0; i < popSize; i++) { if (a [i].f < minFF) minFF = a [i].f; } for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { // Select archer (k) using cumulative probability int k = 0; double r = u.RNDprobab (); while (k < popSize - 1 && C [k] < r) k++; if (u.RNDbool () < inhProbab) { x = a [k].c [c]; } else { // Update position using Eq. (5) and (6) //double I = MathRound (1 + u.RNDprobab ()); double rnd = u.GaussDistribution (0, -1, 1, 8); /* if (a [k].f > a [i].f) { x = agent [i].cPrev [c] + rnd * (a [k].c [c] - I * agent [i].cPrev [c]); } else { x = agent [i].cPrev [c] + rnd * (agent [i].cPrev [c] - I * a [k].c [c]); } */ prob1 = u.Scale (a [i].f, minFF, maxFF, 0, 1); prob2 = u.Scale (a [k].f, minFF, maxFF, 0, 1); x = agent [i].cPrev [c] + rnd * (a [k].c [c] - agent [i].cPrev [c]) * (1 - prob1 - prob2); } a [i].c [c] = u.SeInDiSp (x, rangeMin [c], rangeMax [c], rangeStep [c]); } } } //—
1. В закомментированной секции в первоначальной версии использовалась условная конструкция "if-else" для определения способа обновления позиции агента. Эта логика была удалена и заменена новым расчетом.
2. Три новые строки кода:
prob1 = u.Scale(a[i].f, minFF, maxFF, 0, 1); prob2 = u.Scale(a[k].f, minFF, maxFF, 0, 1); x = agent[i].cPrev[c] + rnd * (a[k].c[c] - agent[i].cPrev[c]) * (1 - prob1 - prob2);
Эти строки вводят новый подход к расчету обновленной позиции:
а) Вычисляются две вероятностные величины, "prob1" и "prob2", с использованием функции "Scale", которая нормализует значения приспособленности агентов "i" и "k" в диапазоне от 0 до 1, основываясь на минимальном и максимальном значениях приспособленности, "minFF" и "maxFF".
б) Затем новая позиция "x" рассчитывается с использованием этих вероятностей. Она использует предыдущую позицию "i" агента "agent [i].cPrev [c]", позицию "k" выбранного лучника "a [k].c [c]" и случайный фактор "rnd".
в) Теперь на движение влияет сумма приспособленности обоих агентов с вычитанием из 1. Этот фактор служит масштабным коэффициентом, позволяя расширять или сужать мишень обратно пропорционально приспособленности выбранных лучников. Чем менее опытны лучники, тем больше разброс стрел, однако распределение вероятностей попадания по мишеням по-прежнему подчиняется нормальному распределению.
Теперь посмотрим на результаты:
AAm|Archery Algorithm M|50.0|0.3|
=============================
5 Hilly's; Func runs: 10000; result: 0.9174358826544864
25 Hilly's; Func runs: 10000; result: 0.7087620527831496
500 Hilly's; Func runs: 10000; result: 0.42160091427958263
=============================
5 Forest's; Func runs: 10000; result: 0.9252690259821034
25 Forest's; Func runs: 10000; result: 0.7580206359203926
500 Forest's; Func runs: 10000; result: 0.353277934084795
=============================
5 Megacity's; Func runs: 10000; result: 0.6738461538461538
25 Megacity's; Func runs: 10000; result: 0.552
500 Megacity's; Func runs: 10000; result: 0.23738461538461658
=============================
All score: 5.54760 (61.64%)
Результатам можно только порадоваться, значительно улучшилась работа алгоритма. На визуализации ниже можно заметить уверенную сходимость алгоритма и выявление значимых участков поверхности функции.
AAm на тестовой функции Hilly
Проведем еще один небольшой эксперимент. Результаты выше получены при вычитании суммы вероятностей лучников из единицы.
//x = agent [i].cPrev [c] + rnd * (a [k].c [c] - agent [i].cPrev [c]) * (1 - prob1 - prob2); x = agent [i].cPrev [c] + rnd * (a [k].c [c] - agent [i].cPrev [c]) * (2 - prob1 - prob2);
Основное изменение заключается в вычитании суммы не из единицы а из двойки. Давайте разберем, как такое простое действие может повлиять на поведение алгоритма:
- в предыдущей версии результат этой операции может быть отрицательным в том случае, если приспособленность обоих лучников высока, что приводит к эффекту "мутации" в результирующих координатах нового лучника.
- в новой версии множитель будет давать значение от 0 до 2.
Это изменение приводит к тому, что движение агентов происходит более размашисто и к более агрессивному исследованию пространства решений, так как агенты будут совершать более крупные шаги при каждом обновлении позиции.
Таким образом, как видно из распечатки результатов работы алгоритма, это изменение улучшило сходимость алгоритма на функциях средней размерности, но также дало ухудшение на функциях большой размерности (помечено желтым), хотя в целом алгоритм набрал итоговое значение выше.
AAm|Archery Algorithm M|50.0|0.3|
=============================
5 Hilly's; Func runs: 10000; result: 0.9053229410164233
25 Hilly's; Func runs: 10000; result: 0.8259118221071665
500 Hilly's; Func runs: 10000; result: 0.2631661675236262
=============================
5 Forest's; Func runs: 10000; result: 0.9714247249319152
25 Forest's; Func runs: 10000; result: 0.9091052022399436
500 Forest's; Func runs: 10000; result: 0.2847632249786224
=============================
5 Megacity's; Func runs: 10000; result: 0.7169230769230768
25 Megacity's; Func runs: 10000; result: 0.6378461538461538
500 Megacity's; Func runs: 10000; result: 0.10473846153846252
=============================
All score: 5.61920 (62.44%)
Предыдущий результат выглядит более практичным и останется как основной вариант модифицированной версии алгоритма AAm. Приведу рейтинговую таблицу с тепловой градацией ещё раз, в которой AAm занимает теперь достойное 7-е место. Алгоритм можно охарактеризовать как очень сбалансированный (хорошая сходимость на функциях различной размерности) и может быть рекомендован для решения различных задач.
Рисунок 4. Цветовая градация алгоритмов по соответствующим тестам. Белым цветом подсвечены результаты больше или равные 0.99
Плюсы и минусы алгоритма AAm:
Плюсы:
- Достаточно быстрый.
- Самоадаптивный.
- Всего лишь один внешний параметр.
- Хорошая сходимость.
- Хорошая масштабируемость.
- Простая реализация (несмотря на сложное описание авторами).
Минусы:
- Небольшая склонность к застреванию на функциях малой размерности.
Дальнейшее добавление новых алгоритмов в рейтинговую таблицу стало вызывать трудности — она стала плохо читаемой. Поэтому я решил ограничить количество участников сравнения до 45 алгоритмов, и теперь соревнование проходит в формате "на вылет". Чтобы читатели могли легко и наглядно получить доступ ко всем статьям, я подготовил HTML-файл со списком всех ранее рассмотренных алгоритмов, упорядоченных по их рейтингу в таблице. Этот файл присутствует в архиве к статьям уже некоторое время, а для тех, кто открывает его впервые, приготовлен небольшой сюрприз.
К статье прикреплён архив с актуальными версиями кодов алгоритмов. Автор статьи не несёт ответственности за абсолютную точность в описании канонических алгоритмов, во многие из них были внесены изменения для улучшения поисковых возможностей. Выводы и суждения, представленные в статьях, основываются на результатах проведённых экспериментов.
- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования