
Алгоритм оптимизации Ройял Флеш — Royal Flush Optimization (RFO)
Содержание
Введение
Для решения задач оптимизации существует множество подходов, среди которых генетические алгоритмы занимают особое место, благодаря своей способности эффективно исследовать пространство поиска путем имитации процессов естественной эволюции. Классический генетический алгоритм использует бинарное кодирование решений, что требует преобразования вещественных чисел в двоичный формат. Это преобразование вносит не только дополнительную сложность, но и значительно утяжеляет алгоритм. В современном мире, минимизация затрат на расчеты играет решающую роль, и продуктивность метода стремится быть прямо пропорциональной его скорости. Решая эту проблему, мне пришла в голову мысль, каким образом можно сохранить генетические операторы, при этом заменив самые тяжелые вычисления перевода вещественных чисел на более простые и менее энергозатратные операции.
Предлагаемый мной алгоритм "Royal Flush Optimization" (RFO) представляет собой новый подход к решению задач оптимизации, который сохраняет основные преимущества генетических алгоритмов, но использует более прямой способ представления решений. Ключевая идея заключается в разбиении каждой координаты пространства поиска на сектора, подобно тому, как покерная комбинация состоит из отдельных карт определенного ранга. Вместо работы с битовыми строками, алгоритм оперирует рангами карт (номерами секторов), что позволяет естественным образом сохранить топологию пространства поиска.
На мой взгляд, основными преимуществами предлагаемого подхода являются как простота реализации и интуитивная понятность (работа с "картами" более наглядна, чем с битовыми строками), так и отсутствие необходимости в кодировании и декодировании вещественных чисел при сохранении комбинаторных свойств генетического алгоритма. В представленной статье рассмотрим подробно реализацию алгоритма и особенности операторов модификации решений.
Метафора покера не только дает название алгоритму, но и хорошо описывает его суть: подобно тому, как в покере игрок стремится собрать наилучшую комбинацию карт, алгоритм комбинирует сектора разных решений, постепенно формируя оптимальные "руки". Как в покере каждая карта имеет свой ранг и масть, так и в алгоритме каждый сектор имеет свое значение и положение в пространстве поиска. При этом, как и в реальной игре, важна не только ценность отдельных карт, но и их взаимодействие в общей комбинации.
Стоит отметить, что данный подход можно рассматривать, как обобщение идей дискретной оптимизации на случай непрерывных пространств, что открывает интересные перспективы для теоретического анализа и практического применения. Подобно тому, как покер сочетает в себе элементы случайности и стратегии, RFO объединяет случайный поиск с направленной оптимизацией, что делает его эффективным для сложных многомерных задач.
Реализация алгоритма
Принцип работы алгоритма Royal Flush Optimization (RFO) основан на идее представления пространства поиска в виде дискретных секторов, подобно тому, как в покере карты имеют определенные ранги. В классическом генетическом алгоритме значения (оптимизируемые параметры) по всем координатам преобразуются в двоичный код и складываются в последовательность в виде хромосомы, что требует дополнительных вычислительных затрат. В RFO мы отказываемся от этого подхода в пользу более простого и интуитивно понятного представления.
Вместо бинарного кодирования, мы разбиваем каждую координату пространства поиска на сектора, присваивая им значения, аналогичные картам в покере — от валета до туза (J, Q, K, A). Количество рангов (секторов) задается во внешнем параметре алгоритма и может быть любым целочисленным значением. Таким образом, любая точка в пространстве поиска может быть представлена как комбинация карт, где каждая карта соответствует определенному сектору по своей координате. Этот подход не только упрощает вычисления, но и сохраняет комбинаторные свойства генетического алгоритма.
В процессе оптимизации алгоритм работает с "руками" — наборами карт, представляющими различные решения. Операторы кроссовера и мутации применяются непосредственно к "рукам" (наборы карт), где каждая рука — аналог хромосомы, что позволяет исследовать пространство поиска без необходимости преобразования в двоичный код и обратно.
Предложенная ниже иллюстрация (рисунок 1) наглядно демонстрирует этот принцип. На ней показано трехмерное пространство поиска с координатами X, Y и Z, где каждая координата разделена на сектора, соответствующие рангам карт. Справа представлены примеры "рук" - различные комбинации карт, которые алгоритм формирует в процессе поиска оптимального решения.
Рисунок 1. Алгоритм RFO и разбиение координат на ранги карточной колоды
Переходим к написанию псевдокода алгоритма Royal Flush Optimization (RFO):
- Задаем количество игроков "покерного стола" (размер популяции)
- Определяем размер "колоды" (количество секторов на каждое измерение)
- Устанавливаем вероятность "блефа" (мутации)
- Создаем начальную "раздачу" — случайно генерируем ранги карт для каждой координаты
- Преобразуем ранги в реальные значения со случайным смещением внутри сектора
- Для каждой позиции за столом:
- Выбираем "оппонента" с помощью квадратичной селекции (более сильные "руки" имеют больший шанс быть выбранными)
- Копируем текущую "руку" (решение)
- Выполняем трехточечный обмен картами:
- Случайно выбираем три точки "разрезания"
- Сортируем точки разрезания
- Случайно выбираем начальную точку (0 или 1)
- Обмениваем карты между текущей рукой и рукой оппонента
- Возможен "блеф" (вероятность мутации) — случайное изменение ранга карты с заданной вероятностью
- Преобразуем полученные ранги карт в реальные значения координат
- Вычисляем ценность каждой "руки" (значение целевой функции)
- Запоминаем лучшую найденную комбинацию (глобальное лучшее решение)
- Объединяем текущие руки с предыдущей колодой
- Сортируем все "руки" по их ценности
- Лучшие "руки" переходят в следующее поколение
Перейдем к написанию кода алгоритма. Напишем структуру "S_RFO_Agent", представляющую объект, содержащий информацию о "руке" в контексте игрового процесса.
Поля структуры:
- card [] — массив для хранения вещественного значения карты.
- f — ценность руки (значение функции пригодности).
- cardRanks [] — массив целых чисел, представляющий "ранги карт" (номера секторов).
Метод Init () — инициализирует структуру, принимает один параметр "coords", который указывает количество карт в "руке".
//—————————————————————————————————————————————————————————————————————————————— // Структура для представления отдельной "руки" struct S_RFO_Agent { double card []; // карты double f; // значение функции пригодности ("ценность руки") int cardRanks []; // номера секторов ("ранги карт") void Init (int coords) { ArrayResize (cardRanks, coords); ArrayResize (card, coords); f = -DBL_MAX; // инициализация минимальным значением } }; //——————————————————————————————————————————————————————————————————————————————
Класс "C_AO_RFO" реализует алгоритм и наследует свойства и методы от базового класса "C_AO". Давайте разберем его более подробно.
Конструктор C_AO_RFO () — устанавливаются значения для переменных класса, инициализация параметров:- popSize — размер популяции (покерного стола) устанавливается в 50.
- deckSize — количество карт в колоде (или секторов) - 1000.
- dealerBluff — вероятность блефа (мутации) устанавливается на уровне 3% (0.03).
- Массив params — служит для хранения параметров, изменяется на размер 3 и заполняется значениями, соответствующими "popSize", "deckSize" и "dealerBluff".
Метод SetParams ()— метод извлекает значения из массива "params" и присваивает их соответствующим переменным класса.
Метод Init ()— предназначен для инициализации класса с переданными параметрами, такими как минимальные и максимальные значения оптимизируемых параметров и их шаг.
Методы Moving() и Revision() — служат для выполнения операций, связанных с тасованием карт на "руках" и пересмотром лучшей их комбинации.
Поля класса:- deckSize — количество секторов в измерении.
- dealerBluff — вероятность мутации.
- deck [], tempDeck [], hand [] — массивы типа "S_RFO_Agent", представляющие основную колоду, временную колоду для сортировки и текущую руку (потомков) соответственно.
- cutPoints — количество точек "разрезания" набора карт на руках, которые используются для комбинации вариантов наборов карт.
- tempCuts [] и finalCuts [] — массивы для хранения временных и окончательных индексов точек "разрезаний".
- Методы, используемые в Evolution () — отвечает за выполнение основной эволюции карточных перестановок, а DealCard () - за преобразование сектора в его реальное значение. Метод "ShuffleRanks ()" отвечает за мутацию рангов (случайный выбор среди доступных рангов).
//—————————————————————————————————————————————————————————————————————————————— class C_AO_RFO : public C_AO { public: C_AO_RFO () { ao_name = "RFO"; ao_desc = "Royal Flush Optimization"; ao_link = "https://www.mql5.com/ru/articles/17063"; popSize = 50; // размер "покерного стола" (популяции) deckSize = 1000; // количество "карт" в колоде (секторов) dealerBluff = 0.03; // вероятность "блефа" (мутации) ArrayResize (params, 3); params [0].name = "popSize"; params [0].val = popSize; params [1].name = "deckSize"; params [1].val = deckSize; params [2].name = "dealerBluff"; params [2].val = dealerBluff; } void SetParams () { popSize = (int)params [0].val; deckSize = (int)params [1].val; dealerBluff = params [2].val; } bool Init (const double &rangeMinP [], // минимальные значения const double &rangeMaxP [], // максимальные значения const double &rangeStepP [], // шаг изменения const int epochsP = 0); // количество эпох void Moving (); void Revision (); //---------------------------------------------------------------------------- int deckSize; // количество секторов в измерении double dealerBluff; // вероятность мутации S_RFO_Agent deck []; // основная колода (популяция) S_RFO_Agent tempDeck []; // временная колода для сортировки S_RFO_Agent hand []; // текущая рука (потомки) private: //------------------------------------------------------------------- int cutPoints; // количество точек разрезания int tempCuts []; // временные индексы точек разрезания int finalCuts []; // финальные индексы с учетом начала и конца void Evolution (); // основной процесс эволюции double DealCard (int rank, int suit); // преобразование сектора в реальное значение void ShuffleRanks (int &ranks []); // мутация рангов }; //——————————————————————————————————————————————————————————————————————————————
Метод "Init" предназначен для инициализации объекта класса "C_AO_RFO".
Метод начинает с вызова функции, которая выполняет стандартную инициализацию заданных параметров, таких как минимальные и максимальные значения, а также шаги изменения параметров. Если эта инициализация не проходит успешно, метод завершает выполнение и возвращает значение "false". После успешной инициализации параметров, метод приступает к подготовке структур данных для хранения информации о "руках" и "колодах". Это включает в себя изменение размеров массивов для хранения "рук" и "колод", чтобы они соответствовали количеству популяции.
Далее метод инициализирует каждый элемент массива "рук" с помощью специального метода, который настраивает их с учетом указанных координат. Аналогично, массивы "колоды" и "временной колоды" также подготавливаются и инициализируются. Метод устанавливает количество точек разрезания, необходимых для алгоритма кроссовера. В данном случае, устанавливается три точки разрезания (таково лучшее значение, найденное после проведенных экспериментов). Затем осуществляется настройка массивов для хранения временных и окончательных точек разрезания. В конце метод возвращает значение "true", что подтверждает успешное завершение инициализации.
//—————————————————————————————————————————————————————————————————————————————— bool C_AO_RFO::Init (const double &rangeMinP [], const double &rangeMaxP [], const double &rangeStepP [], const int epochsP = 0) { if (!StandardInit (rangeMinP, rangeMaxP, rangeStepP)) return false; //---------------------------------------------------------------------------- // Инициализация структур для хранения "рук" и "колод" ArrayResize (hand, popSize); for (int i = 0; i < popSize; i++) hand [i].Init (coords); ArrayResize (deck, popSize * 2); ArrayResize (tempDeck, popSize * 2); for (int i = 0; i < popSize * 2; i++) { deck [i].Init (coords); tempDeck [i].Init (coords); } // Инициализация массивов для точек разрезания cutPoints = 3; // три точки разрезания для "трехкарточного" кроссовера ArrayResize (tempCuts, cutPoints); ArrayResize (finalCuts, cutPoints + 2); return true; } //——————————————————————————————————————————————————————————————————————————————
Метод "Moving" отвечает за процесс "движения" или обновления состояния популяции в рамках алгоритма RFO.
Проверка состояния — метод начинает выполнение с проверки условия, определяющего, была ли закончена начальная инициализация "раздача" карт. Если это не так (revision == false), метод выполняет инициализацию.
Инициализация начальной раздачи — метод проходит по всем элементам популяции, для каждого элемента популяции (каждой "руки") создается набор карт. Внутренний цикл проходит по каждому необходимому количеству карт и выполняет следующие действия:
- Случайным образом выбирается ранг карты из колоды.
- Затем вызывается метод для раздачи карты, основываясь на сгенерированном ранге.
- Полученная карта корректируется с помощью функции, которая проверяет, находится ли она в заданном диапазоне и производит необходимые изменения, в зависимости от указанных параметров.
- Наконец, в массив "a" записывается значение полученной карты.
Обновление состояния — после завершения инициализации "revision" устанавливается в "true", что указывает, начальная раздача завершена, и больше не требуется её повторная инициализация.
Вызов метода Evolution () — если начальная раздача уже была выполнена, метод переходит к выполнению эволюционного процесса тасования и раздачи карт на "руки".
//—————————————————————————————————————————————————————————————————————————————— void C_AO_RFO::Moving () { //---------------------------------------------------------------------------- if (!revision) { // Инициализация начальной "раздачи" for (int i = 0; i < popSize; i++) { for (int c = 0; c < coords; c++) { hand [i].cardRanks [c] = u.RNDminusOne (deckSize); hand [i].card [c] = DealCard (hand [i].cardRanks [c], c); hand [i].card [c] = u.SeInDiSp (hand [i].card [c], rangeMin [c], rangeMax [c], rangeStep [c]); a [i].c [c] = hand [i].card [c]; } } revision = true; return; } //---------------------------------------------------------------------------- Evolution (); } //——————————————————————————————————————————————————————————————————————————————
Метод "Revision" отвечает за поиск наилучшей "комбинации" на "руках", оценку их пригодности и обновление общей колоды.
Поиск лучшей комбинации:
- Метод начинает с инициализации переменной "bestHand", которая будет хранить индекс наилучшей руки среди всех членов популяции.
- Затем выполняется цикл, проходящий по всем элементам популяции (от 0 до popSize). Внутри цикла метод сравнивает значение пригодности каждой руки "a" с текущим наилучшим значением "fB".
- Если значение пригодности текущей руки больше, чем "fB", происходит обновление с новым наилучшим значением, а переменной "bestHand" присваивается индекс этой "руки".
Если была найдена наилучшая рука, ее карты копируются в массив "cB", что позволяет сохранить состояние наилучшей комбинации (наилучшее глобальное решение). Метод затем обновляет значения пригодности для каждой руки в массиве "hand", устанавливая их равными соответствующим значениям в массиве "a". Это необходимо для обеспечения актуальности данных о пригодности каждой руки. После обновления значений пригодности, текущие руки из массива "hand" добавляются в общий массив "deck", начиная с позиции "popSize" (то есть, в конец популяции, ее вторую половину).
В завершение, метод выполняет сортировку массива "deck", используя отдельный временный массив "tempDeck", чтобы упорядочить колоду по ценности комбинаций. Это позволяет использовать повышенную вероятность отбора ценных комбинаций карт при селекции с последующим их комбинированием.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_RFO::Revision () { // Поиск лучшей "комбинации" int bestHand = -1; for (int i = 0; i < popSize; i++) { if (a [i].f > fB) { fB = a [i].f; bestHand = i; } } if (bestHand != -1) ArrayCopy (cB, a [bestHand].c, 0, 0, WHOLE_ARRAY); //---------------------------------------------------------------------------- // Обновление значений пригодности for (int i = 0; i < popSize; i++) { hand [i].f = a [i].f; } // Добавление текущих рук в общую колоду for (int i = 0; i < popSize; i++) { deck [popSize + i] = hand [i]; } // Сортировка колоды по ценности комбинаций u.Sorting (deck, tempDeck, popSize * 2); } //——————————————————————————————————————————————————————————————————————————————
Метод "Evolution" отвечает за основную логику алгоритма, в котором происходит обмен картами между "руками" игроков за столом, "блеф" и обновление вещественных значений карт.
Метод начинается с цикла, который проходит по всем элементам популяции. Для каждого элемента выполняются следующие действия:
Выбор оппонента:
- Для выбора оппонента используется генерация случайного числа, которое затем поднимается в квадрат для усиления селекции (вероятность выбора оппонента пересекается с его рейтингом). Это позволяет более вероятно выбирать лучшее сочетание рук.
- Случайное число масштабируется с использованием функции "u.Scale", чтобы получить индекс оппонента.
Текущая рука (из массива "deck") копируется в массив "hand". Метод генерирует случайные точки "разрезания" набора карт на руках. Эти точки определяют, какие карты будут обмениваться между двумя руками. Точки разрезания сортируются, и к ним добавляются границы; первая граница устанавливается на "0", а последняя — на "coords - 1". Метод выбирает случайную начальную точку для начала обмена картами, используя "u.RNDbool ()". После выполнения обмена картами, производится шанс на "блеф".
Преобразование рангов в реальные значения:- В заключительном цикле происходит преобразование рангов карт в их значения, используя метод "DealCard", и осуществляется проверка на соответствие установленным границам.
- После этого обновляются значения в массиве "a", который, содержит финальные карты.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_RFO::Evolution () { // Для каждой позиции за столом for (int i = 0; i < popSize; i++) { // Выбор оппонента с учетом его рейтинга (квадрат вероятности для усиления селекции) double rnd = u.RNDprobab (); rnd *= rnd; int opponent = (int)u.Scale (rnd, 0.0, 1.0, 0, popSize - 1); // Копирование текущей руки hand [i] = deck [i]; // Определение точек разрезания для обмена картами for (int j = 0; j < cutPoints; j++) { tempCuts [j] = u.RNDminusOne (coords); } // Сортировка точек разрезания и добавление границ ArraySort (tempCuts); ArrayCopy (finalCuts, tempCuts, 1, 0, WHOLE_ARRAY); finalCuts [0] = 0; finalCuts [cutPoints + 1] = coords - 1; // Случайный выбор начальной точки для обмена int startPoint = u.RNDbool (); // Обмен картами между руками for (int j = startPoint; j < cutPoints + 2; j += 2) { if (j < cutPoints + 1) { for (int len = finalCuts [j]; len < finalCuts [j + 1]; len++) hand [i].cardRanks [len] = deck [opponent].cardRanks [len]; } } // Возможность "блефа" (мутации) ShuffleRanks (hand [i].cardRanks); // Преобразование рангов в реальные значения for (int c = 0; c < coords; c++) { hand [i].card [c] = DealCard (hand [i].cardRanks [c], c); hand [i].card [c] = u.SeInDiSp (hand [i].card [c], rangeMin [c], rangeMax [c], rangeStep [c]); a [i].c [c] = hand [i].card [c]; } } } //——————————————————————————————————————————————————————————————————————————————
Метод "DealCard" является ключевым элементом алгоритма Royal Flush Optimization, преобразующим дискретные сектора пространства поиска в непрерывные значения координат. На вход метод принимает два параметра: "rank" — ранг карты и "suit" — индекс координаты (масть).
Процесс преобразования состоит из двух этапов. Сначала вычисляется размер одного сектора (suitRange), путем деления всего диапазона поиска на количество секторов. Затем генерируется конкретное значение внутри выбранного сектора. Случайное смещение "u.RNDprobab ()" обеспечивает равномерное исследование пространства внутри каждого сектора, а "rank" определяет базовое положение в пространстве поиска.
Такой подход позволяет сочетать дискретное представление решений через сектора с непрерывным пространством поиска, обеспечивая баланс между глобальным и локальным поиском.
//—————————————————————————————————————————————————————————————————————————————— double C_AO_RFO::DealCard (int rank, int suit) { // Преобразование ранга карты в реальное значение с случайным смещением внутри сектора double suitRange = (rangeMax [suit] - rangeMin [suit]) / deckSize; return rangeMin [suit] + (u.RNDprobab () + rank) * suitRange; } //——————————————————————————————————————————————————————————————————————————————
Метод "ShuffleRanks" реализует механизм мутации в алгоритме Royal Flush Optimization, выступая в роли "блефа" при работе с рангами карт. Принимая на вход массив рангов по ссылке, метод проходит по каждой координате и, с вероятностью "dealerBluff", заменяет текущий ранг на случайное значение из диапазона допустимых рангов колоды. Этот процесс можно сравнить с ситуацией в покере, когда игрок неожиданно меняет карту в своей руке, внося элемент непредсказуемости в игру. Такой механизм мутации призван помогать алгоритму избегать застревания в локальных оптимумах и поддерживает разнообразие возможных решений в процессе оптимизации.
//—————————————————————————————————————————————————————————————————————————————— void C_AO_RFO::ShuffleRanks (int &ranks []) { // Перетасовка рангов (мутация) for (int i = 0; i < coords; i++) { if (u.RNDprobab () < dealerBluff) ranks [i] = (int)MathRand () % deckSize; } } //——————————————————————————————————————————————————————————————————————————————
Результаты тестов
Распечатка результатов тестирования алгоритма RFO:
RFO|Royal Flush Optimization|50.0|1000.0|0.03|
=============================
5 Hilly's; Func runs: 10000; result: 0.8336125672709654
25 Hilly's; Func runs: 10000; result: 0.7374210861383783
500 Hilly's; Func runs: 10000; result: 0.34629436610445113
=============================
5 Forest's; Func runs: 10000; result: 0.8942431024645086
25 Forest's; Func runs: 10000; result: 0.7382367793268382
500 Forest's; Func runs: 10000; result: 0.24097956383750824
=============================
5 Megacity's; Func runs: 10000; result: 0.6315384615384616
25 Megacity's; Func runs: 10000; result: 0.5029230769230771
500 Megacity's; Func runs: 10000; result: 0.16420769230769366
=============================
All score: 5.08946 (56.55%)
Итоговая оценка в 56.55% — очень достойный результат. На визуализации алгоритм не демонстрирует какого-то специфичного поведения, выглядит как хаотичное блуждание отдельных точек.
RFO на тестовой функции Hilly
RFO на тестовой функции Forest
RFO на тестовой функции Megacity
По результатам тестирования алгоритм оптимизации RFO занимает 15-е место, пополнив список самых сильнейших известных алгоритмов.
№ | 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 (joo) | 0,95345 | 0,87107 | 0,37590 | 2,20042 | 0,98942 | 0,91709 | 0,31642 | 2,22294 | 0,79692 | 0,69385 | 0,19303 | 1,68380 | 6,107 | 67,86 |
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 (joo) | 0,95346 | 0,86319 | 0,27770 | 2,09435 | 0,99794 | 0,85740 | 0,33949 | 2,19484 | 0,88769 | 0,56431 | 0,10512 | 1,55712 | 5,846 | 64,96 |
6 | TETA | time evolution travel algorithm (joo) | 0,91362 | 0,82349 | 0,31990 | 2,05701 | 0,97096 | 0,89532 | 0,29324 | 2,15952 | 0,73462 | 0,68569 | 0,16021 | 1,58052 | 5,797 | 64,41 |
7 | 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 |
8 | AAm | archery algorithm M | 0,91744 | 0,70876 | 0,42160 | 2,04780 | 0,92527 | 0,75802 | 0,35328 | 2,03657 | 0,67385 | 0,55200 | 0,23738 | 1,46323 | 5,548 | 61,64 |
9 | ESG | evolution of social groups (joo) | 0,99906 | 0,79654 | 0,35056 | 2,14616 | 1,00000 | 0,82863 | 0,13102 | 1,95965 | 0,82333 | 0,55300 | 0,04725 | 1,42358 | 5,529 | 61,44 |
10 | SIA | simulated isotropic annealing (joo) | 0,95784 | 0,84264 | 0,41465 | 2,21513 | 0,98239 | 0,79586 | 0,20507 | 1,98332 | 0,68667 | 0,49300 | 0,09053 | 1,27020 | 5,469 | 60,76 |
11 | 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 |
12 | DA | dialectical algorithm | 0,86183 | 0,70033 | 0,33724 | 1,89940 | 0,98163 | 0,72772 | 0,28718 | 1,99653 | 0,70308 | 0,45292 | 0,16367 | 1,31967 | 5,216 | 57,95 |
13 | BHAm | black hole algorithm M | 0,75236 | 0,76675 | 0,34583 | 1,86493 | 0,93593 | 0,80152 | 0,27177 | 2,00923 | 0,65077 | 0,51646 | 0,15472 | 1,32195 | 5,196 | 57,73 |
14 | 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 |
15 | RFO | royal flush optimization (joo) | 0,83361 | 0,73742 | 0,34629 | 1,91733 | 0,89424 | 0,73824 | 0,24098 | 1,87346 | 0,63154 | 0,50292 | 0,16421 | 1,29867 | 5,089 | 56,55 |
16 | AOSm | atomic orbital search M | 0,80232 | 0,70449 | 0,31021 | 1,81702 | 0,85660 | 0,69451 | 0,21996 | 1,77107 | 0,74615 | 0,52862 | 0,14358 | 1,41835 | 5,006 | 55,63 |
17 | TSEA | turtle shell evolution algorithm (joo) | 0,96798 | 0,64480 | 0,29672 | 1,90949 | 0,99449 | 0,61981 | 0,22708 | 1,84139 | 0,69077 | 0,42646 | 0,13598 | 1,25322 | 5,004 | 55,60 |
18 | 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 |
19 | 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 |
20 | 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 |
21 | 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 |
22 | 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 |
23 | 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 |
24 | ABO | african buffalo optimization | 0,83337 | 0,62247 | 0,29964 | 1,75548 | 0,92170 | 0,58618 | 0,19723 | 1,70511 | 0,61000 | 0,43154 | 0,13225 | 1,17378 | 4,634 | 51,49 |
25 | (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 |
26 | 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 |
27 | 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 |
28 | 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 |
29 | 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 |
30 | AEO | artificial ecosystem-based optimization algorithm | 0,91380 | 0,46713 | 0,26470 | 1,64563 | 0,90223 | 0,43705 | 0,21400 | 1,55327 | 0,66154 | 0,30800 | 0,28563 | 1,25517 | 4,454 | 49,49 |
31 | 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 |
32 | 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 |
33 | SOA | simple optimization algorithm | 0,91520 | 0,46976 | 0,27089 | 1,65585 | 0,89675 | 0,37401 | 0,16984 | 1,44060 | 0,69538 | 0,28031 | 0,10852 | 1,08422 | 4,181 | 46,45 |
34 | 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 |
35 | ACMO | atmospheric cloud model optimization | 0,90321 | 0,48546 | 0,30403 | 1,69270 | 0,80268 | 0,37857 | 0,19178 | 1,37303 | 0,62308 | 0,24400 | 0,10795 | 0,97503 | 4,041 | 44,90 |
36 | ADAMm | adaptive moment estimation M | 0,88635 | 0,44766 | 0,26613 | 1,60014 | 0,84497 | 0,38493 | 0,16889 | 1,39880 | 0,66154 | 0,27046 | 0,10594 | 1,03794 | 4,037 | 44,85 |
37 | ATAm | artificial tribe algorithm M | 0,71771 | 0,55304 | 0,25235 | 1,52310 | 0,82491 | 0,55904 | 0,20473 | 1,58867 | 0,44000 | 0,18615 | 0,09411 | 0,72026 | 3,832 | 42,58 |
38 | ASHA | artificial showering algorithm | 0,89686 | 0,40433 | 0,25617 | 1,55737 | 0,80360 | 0,35526 | 0,19160 | 1,35046 | 0,47692 | 0,18123 | 0,09774 | 0,75589 | 3,664 | 40,71 |
39 | 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 |
40 | 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 |
41 | 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 |
42 | 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 |
43 | 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 |
44 | 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 |
45 | 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 |
RW | random walk | 0,48754 | 0,32159 | 0,25781 | 1,06694 | 0,37554 | 0,21944 | 0,15877 | 0,75375 | 0,27969 | 0,14917 | 0,09847 | 0,52734 | 2,348 | 26,09 |
Выводы
В процессе исследования и разработки новых методов оптимизации мы часто сталкиваемся с необходимостью поиска баланса между эффективностью и сложностью реализации. Работа над алгоритмом Royal Flush Optimization (RFO) привела к интересным результатам, которые заставляют задуматься о природе оптимизационных процессов и путях их совершенствования.
Наблюдая за работой алгоритма, достигающего 57% от максимально возможной теоретически производительности, мы видим интересный феномен: иногда упрощение может быть более ценным, чем усложнение. RFO демонстрирует, что отказ от сложного бинарного кодирования в пользу более прямолинейного секторного подхода может привести к существенному ускорению работы алгоритма, при сохранении достаточно высокого качества решений. Это напоминает ситуацию в покере, где иногда более простая, но быстрая стратегия может оказаться эффективнее сложной и требующей длительных расчетов.
Размышляя о месте RFO в семействе оптимизационных алгоритмов, можно провести аналогию с эволюцией транспортных средств. Подобно тому, как наряду с мощными спортивными автомобилями существует потребность в экономичных городских моделях, так и в мире алгоритмов оптимизации есть место для методов, ориентированных на разные приоритеты. RFO можно рассматривать как "экономичный" вариант генетического алгоритма, предлагающий разумный компромисс между результативностью и ресурсоэффективностью.
В заключение стоит отметить, что разработка RFO открывает интересные перспективы для дальнейших исследований. Возможно, это только первый шаг в развитии целого семейства алгоритмов, основанных на секторном подходе к оптимизации. Простота и элегантность метода, сочетающаяся с его практической эффективностью, может послужить источником вдохновения для создания новых алгоритмов, балансирующих между производительностью и вычислительной эффективностью.
Стоит отметить, что разбивка на сектора происходит виртуально, без выделения на это памяти в виде массивов. Данная основа RFO является отличной отправной точкой для дальнейшей разработки усовершенствованных версий покерного алгоритма.
Рисунок 2. Цветовая градация алгоритмов по соответствующим тестам
Рисунок 3. Гистограмма результатов тестирования алгоритмов (по шкале от 0 до 100, чем больше, тем лучше, где 100 - максимально возможный теоретический результат, в архиве скрипт для расчета рейтинговой таблицы)
Плюсы и минусы алгоритма RFO:
Плюсы:
- Мало внешних параметров, только два, не считая размер популяции.
- Простая реализация.
- Быстрый.
- Сбалансированные, хорошие показатели на задачах различных размерностей.
Минусы:
- Средняя точность сходимости.
К статье прикреплён архив с актуальными версиями кодов алгоритмов. Автор статьи не несёт ответственности за абсолютную точность в описании канонических алгоритмов, во многие из них были внесены изменения для улучшения поисковых возможностей. Выводы и суждения, представленные в статьях, основываются на результатах проведённых экспериментов.
Программы, используемые в статье
# | Имя | Тип | Описание |
---|---|---|---|
1 | #C_AO.mqh | Включаемый файл | Родительский класс популяционных алгоритмов оптимизации |
2 | #C_AO_enum.mqh | Включаемый файл | Перечисление популяционных алгоритмов оптимизации |
3 | TestFunctions.mqh | Включаемый файл | Библиотека тестовых функций |
4 | TestStandFunctions.mqh | Включаемый файл | Библиотека функций тестового стенда |
5 | Utilities.mqh | Включаемый файл | Библиотека вспомогательных функций |
6 | CalculationTestResults.mqh | Включаемый файл | Скрипт для расчета результатов в сравнительную таблицу |
7 | Testing AOs.mq5 | Скрипт | Единый испытательный стенд для всех популяционных алгоритмов оптимизации |
8 | Simple use of population optimization algorithms.mq5 | Скрипт | Простой пример использования популяционных алгоритмов оптимизации без визуализации |
9 | Test_AO_RFO.mq5 | Скрипт | Испытательный стенд для RFO |





- Бесплатные приложения для трейдинга
- 8 000+ сигналов для копирования
- Экономические новости для анализа финансовых рынков
Вы принимаете политику сайта и условия использования
Во всех случаях, когда необходимо найти лучшее решение среди множества возможных. Например, советники с самооптимизацией.