Распараллеливание алгоритма муравьиной колонии на примере задачи о рюкзаке с использованием Python

Обложка

Цитировать

Полный текст

Аннотация

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

Полный текст

Введение

Алгоритм муравьиной колонии (АСО) является одним из самых мощных инструментов в комбинаторной оптимизации. Он используется в логистике, робототехнике, туризме, сетевом планировании и других областях [1–3]. При классическом подходе каждый муравей независимо строит свое решение, опираясь на информацию о феромонах и эвристические данные. И только после прохождения всех муравьев происходит обновление феромонов на основе их решений. В стандартной реализации это организуется как один цикл, где каждый муравей по очереди строит свое решение.

Метод исследования

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

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

Распараллелить задачу в Python можно, используя модуль multiprocessing, который позволяет запускать несколько процессов параллельно, существенно ускоряя выполнение задачи, особенно на многоядерных системах. Рассмотрим пример кода для распараллеливания АСО применительно к задаче о рюкзаке, в которой веса и стоимости предметов генерируются случайным образом:

def model(n, range_v=(100, 500), range_w=(30, 100)):

 np.random.seed(42)

 v = np.random.randint(range_v[0], range_v[1], size=n)

 w = np.random.randint(range_w[0], range_w[1], size=n)

 return v, w

Функции select_item и ant_solution аналогичны однопроцессорной работе, так как выполняют свои задачи для одного муравья и не требуют параллелизма.

def select_item(probabilities):

 return np.random.choice(len(probabilities), p=probabilities)

def ant_solution(pheromones, w, v, s, alpha, beta):

 num_items = len(w)

 selected_items = []

 current_weight = 0

 available_items = list(range(num_items))

 while available_items:

 pheromone = pheromones[available_items]

 heuristic = v[available_items] / (w[available_items] + 1e-10)

 probabilities = (pheromone ** alpha) * (heuristic ** beta)

 probabilities /= probabilities.sum()

 item = available_items.pop(select_item(probabilities))

 if current_weight + w[item] <= s:

 selected_items.append(item)

 current_weight += w[item]

 return selected_items

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

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

  • process_group - выполняет задачи для группы муравьев в отдельном процессе, осуществляя генерацию решений, обновление локальных феромонов и нахождение лучшего решения внутри своей группы;
  • ant_colony_opt - координирует работу процессов, собирает результаты от групп муравьев, обновляет глобальные феромоны и управляет итерациями оптимизации для поиска лучшего глобального решения.

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

def ant_colony_opt(pheromones, w, v, s, alpha, beta, rho, Q, iter, ants, proc):

 best_solution = None

 best_value = float('-inf')

 for i in range(iter):

 t0 = time.time()

 # Создание пула процессов

 pool = multiprocessing.Pool(processes=proc)

 results = []

 # Распараллеливание обработки муравьев

 for group_id in range(proc):

 result = pool.apply_async(process_group,

 args=(pheromones, w, v, s, alpha, beta, Q, ants, group_id, proc)

 )

 results.append(result)

 pool.close()

 pool.join()

 # Сбор и обработка результатов

 local_pheromones = np.zeros(len(w))

 for result in results:

 solution, value, group_pheromones = result.get()

 if value > best_value:

 best_solution = solution

 best_value = value

 local_pheromones += group_pheromones

 pheromones = (1 - rho) * pheromones + local_pheromones

 tk = time.time()

 print(f"Итерация {i+1}, Решение: {best_value}, t={tk-t0:.2f} сек")

 return best_solution, best_value

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

Внутри цикла для каждой группы муравьев, используя метод apply_async объекта multiprocessing.Pool, асинхронно вызывается функция process_group. Она будет обрабатывать группу муравьев, генерировать решения и обновлять локальные феромоны. Список results хранит все асинхронные задачи для последующего получения их результатов.

После запуска всех задач метод pool.close() закрывает пул процессов для новых задач, а метод pool.join() блокирует основной поток выполнения до тех пор, пока все процессы в пуле не завершат свою работу.

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

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

def process_group(pheromones, w, v, s, alpha, beta, Q, ants, group_id, proc):

 local_pheromones = np.zeros(len(w))

 local_best_solution = None

 local_best_value = float('-inf')

 ants_per_group = ants // proc

 for _ in range(ants_per_group):

 solution = ant_solution(pheromones, w, v, s, alpha, beta)

 value = np.sum(v[solution])

 weight = np.sum(w[solution])

 if value > local_best_value and weight <= s:

 local_best_solution = solution

 local_best_value = value

 for item in solution:

 local_pheromones[item] += Q / value

 print(f"----- Процесс {group_id} Решение группы - {local_best_value}")

 return local_best_solution, local_best_value, local_pheromones

На этапе инициализации определяются: local_pheromones - массив для хранения феромонов, обновляемых внутри этого процесса, local_best_solution - локальное лучшее решение, найденное в данном процессе, и local_best_value - значение лучшего решения. Значение ants_per_group определяет количество муравьев, обрабатываемых в рамках данного процесса. Общее число муравьев в АСО делится между всеми доступными процессами (proc).

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

На основе полученной цены решения обновляются локальные феромоны, выводится лучшее решение для данного процесса, а затем лучшее решение, его значение и обновленные феромоны функция возвращает в ant_colony_opt для последующей агрегации в рамках решения задачи о рюкзаке. Функция main() управляет процессом оптимизации АСО, отвечает за настройку параметров, инициализацию данных и запуск основной функции оптимизации.

def main():

 n = 100

 v, w = model(n)

 s = 500

 ants = 100 # Количество муравьев

 proc = 4 # Количество процессов для параллелизма

 alpha = 1.0 # Влияние феромонов

 beta = 2.0 # Влияние эвристической информации

 rho = 0.5 # Коэффициент испарения феромонов

 Q = 100 # Константа для обновления феромонов

 iter = 10 # Количество итераций

 pheromones = np.ones(len(w))

 # Запуск оптимизации муравьиной колонии

 start_time = time.time()

 best_solution, best_value = ant_colony_optimization(

 pheromones, w, v, s, alpha, beta, rho, Q, iter, ants, proc )

 print(f"Лучшее решение: {best_solution}; Общая цена: {best_value}")

 print(f"Общее время выполнения: {time.time() - start_time:.2f} секунд")

Результат исследования

С целью оценки работоспособности проекта и эффективности работы АСО был проведен тест на наборе из 100 предметов, с которыми работали 100 муравьев в течение 10 итераций, показавший, что многопоточная работа заметно снижает общее время решения задачи (рис. 1).

 

Рис. 1. Одно- и четырехпоточное решение задачи о рюкзаке

Fig. 1. One- and four-stream solution of the knapsack problem

 

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

 

Рис. 2. Загрузка 4 ядер при решении задачи о рюкзаке

Fig. 2. Loading of 4 cores when solving the knapsack problem

 

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

 

Рис. 3. Задача о рюкзаке: 100 предметов, 10 итераций и 100 муравьев

Fig. 3. Knapsack problem: 100 items, 10 iterations and 100 ants

 

Для более обоснованного вывода по качеству разработанного Python-кода было выполнено дополнительное тестирование на увеличенном наборе данных. Цель тестирования на наборе из 1000 предметов со 100 муравьями и 20 итерациями заключается в следующем:

Проверка масштабируемости – исследование, как алгоритм муравьиной колонии справляется с задачами большей сложности и размера.

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

В табл. 1 приведен анализ работы параллельного и простого алгоритмов муравьиной колонии при решении задачи о рюкзаке с увеличенным до 1000 набором предметов, который выполнили 100 муравьев за 20 итераций.

 

Таблица 1. Сравнительный анализ работы параллельного и простого АСО

Table 1. Comparative analysis of the operation of parallel and simple ACO

100 муравьев, 20 итераций, 1000 предметов

Параллельный алгоритм АСО

Простой алгоритм АСО

 

Iteration 1, Best Value: 4724, Time: 22.62 сек.

Iteration 2, Best Value: 4848, Time: 19.08 сек.

Iteration 3, Best Value: 5035, Time: 20.03 сек.

 

Iteration 18, Best Value: 7000, Time: 23.97 сек.

Iteration 19, Best Value: 7000, Time: 19.40 сек.

Iteration 20, Best Value: 7000, Time: 18.45 сек.

Лучшее найденное решение:

[718, 957, 918, 538, 190, 646, 639, 200, 533, 337, 520, 318, 70, 797, 759]

с общей ценностью 7000

 

Общее время выполнения: 397.23 секунд

 

 

Iteration 1, Time: 70.61 сек. Best Value: 4867

Iteration 2, Time: 74.44 sec, Best Value: 5027

Iteration 3, Time: 70.47 сек. Best Value: 5174

 . . . . .

Iteration 18, Time: 69.23 сек. Best Value: 6972

Iteration 19, Time: 68.30 сек. Best Value: 6972

Iteration 20, Time: 68.71 сек. Best Value: 6972

Лучшее найденное решение:

[318, 918, 639, 823, 957, 190, 646, 249, 70, 759, 797, 538, 819, 337, 522]

с общей ценностью 6972

 

Общее время выполнения: 1414.94 секунд

 

 

Из анализа полученных результатов следует, что параллельный алгоритм АСО значительно быстрее. Общее время выполнения составило 397 секунд по сравнению с 1415 секундами у простого алгоритма. То есть распараллеливание существенно сократило время выполнения задачи, особенно для больших наборов данных. Кроме этого, время выполнения одной итерации у параллельного алгоритма занимает от 18.45 до 23.97 секунд, что значительно меньше по сравнению с от 68.30 до 74.44 секундами у простого алгоритма. Это показывает более равномерную и эффективную обработку задач в параллельной реализации.

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

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

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

 

Рис. 4. Загрузка 4 ядер процессора при 1000 предметах

Fig. 4. Loading of 4 processor cores with 1000 items

 

Все это подтверждает эффективность использования параллельных вычислений для задач с высокой вычислительной сложностью.

Переход от последовательного выполнения к параллельному логично вытекает из понимания независимости муравьев в ACO и открывает новые возможности для решения сложных задач. Проведенное тестирование показало, что АСО может быстро находить решения, близкие к оптимальным, даже при малом количестве итераций. Это делает его полезным инструментом в условиях, когда время расчета играет критическую роль, и позволяет его использовать при решении широкого круга траекторных задач [4, 5, 6] или задач маршрутизации в беспроводных сетях [7]. По сравнению с точными методами типа динамического программирования ACO показал значительное сокращение времени выполнения при приемлемом уровне отклонения от оптимального решения.

Выводы

 В качестве основных выводов по реализации процесса распараллеливания алгоритма муравьиной колонии на примере задачи о рюкзаке при помощи языка программирования Python можно отметить следующие положительные направления:

  1. Распараллеливание позволяет одновременно запускать множество муравьев на разных процессорах, что значительно сокращает время поиска оптимального решения по сравнению с последовательным выполнением алгоритма.
  2. При использовании распараллеливания алгоритм максимально задействует доступные вычислительные ресурсы (ядра процессора), что особенно важно при работе с большими наборами данных или сложными проблемами, требующими значительных вычислительных ресурсов.
  3. Распараллеливание позволяет исследовать большее количество возможных решений за определенное время, что приводит к повышению вероятности нахождения более точного решения по сравнению с последовательным алгоритмом.
  4. Распараллеливание делает алгоритм муравьиной колонии более устойчивым к случайным колебаниям в поведении муравьев. Благодаря тому, что муравьи работают независимо друг от друга на разных процессорах, ошибки одного муравья не влияют на других.
  5. Библиотека multiprocessing в Python позволяет эффективно распараллелить алгоритм муравьиной колонии, что делает распараллеливание доступным для разработчиков любого уровня.
×

Об авторах

Марсель Равильевич Вагизов

Санкт-Петербургский государственный лесотехнический университет им. С. М. Кирова

Автор, ответственный за переписку.
Email: bars-tatarin@yandex.ru
ORCID iD: 0000-0003-4848-1619
SPIN-код: 4811-8943

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

Россия, 194021, Санкт-Петербург, Институтский пер., 5

Сергей Петрович Хабаров

Санкт-Петербургский государственный лесотехнический университет им. С. М. Кирова

Email: Serg.Habarov@mail.ru
ORCID iD: 0000-0003-1337-0150
SPIN-код: 4365-2033

канд. тех. наук, доцент кафедры информационных систем и технологий

Россия, 194021, Санкт-Петербург, Институтский пер., 5

Список литературы

  1. Даринцев О. В., Мигранов А. Б. Использование муравьиного алгоритма для поиска стратегии поведения группы мобильных роботов на рабочем поле с препятствиями // Многофазные системы. 2022. Т. 17. № 3-4. С. 177-186. doi: 10.21662/mfs2022.3.016
  2. Минин А. А., Немтинов В. А. Применение алгоритма муравьиных колоний для создания технологических процессов обработки резанием // Инженерные технологии. 2023. № 3. С. 31-36. EDN: LAMZQA
  3. Павловская К. А., Червинский В. В. Маршрутизация в сетях MANET на основе муравьиных алгоритмов с учетом энергосбережения // Вестник Донецкого национального университета. Серия Г: Технические науки. 2023. № 1. С. 4-10. EDN: SWIUGB
  4. Вагизов М. Р., Хабаров С. П. Построение программных траекторий движения на базе решения задачи «Машина Дубинса» // Информация и космос. 2021. № 3. С. 116–125. EDN: DDDWFN
  5. Коренев А. С., Скрыпка А. С., Хабаров С. П. Автономное судовождение на действующих судах // Морской вестник. 2022. № 1(81). С. 92–95. EDN: MIPIOP
  6. Хабаров С. П., Шилкина М. Л. Геометрический подход к решению задачи для машин Дубинса при формировании программных траекторий движения // Научно-технический вестник информационных технологий, механики и оптики. 2021. Т. 21. № 5. С. 653–663. doi: 10.17586/2226-1494-2021-21-5-653-663
  7. Думов М. И. Моделирование беспроводных сетей в среде OMNеT++ с использованием INET framework // Научно-технический вестник информационных технологий, механики и оптики. 2019. Т. 19. № 6. С. 1151–1161. doi: 10.17586/2226-1494-2019-19-6-1151-1161

Дополнительные файлы

Доп. файлы
Действие
1. JATS XML
2. Рис. 1. Одно- и четырехпоточное решение задачи о рюкзаке

Скачать (456KB)
3. Рис. 2. Загрузка 4 ядер при решении задачи о рюкзаке

Скачать (161KB)
4. Рис. 3. Задача о рюкзаке: 100 предметов, 10 итераций и 100 муравьев

Скачать (130KB)
5. Рис. 4. Загрузка 4 ядер процессора при 1000 предметах

Скачать (190KB)

© Вагизов М.Р., Хабаров С.П., 2024

Creative Commons License
Эта статья доступна по лицензии Creative Commons Attribution 4.0 International License.

Согласие на обработку персональных данных с помощью сервиса «Яндекс.Метрика»

1. Я (далее – «Пользователь» или «Субъект персональных данных»), осуществляя использование сайта https://journals.rcsi.science/ (далее – «Сайт»), подтверждая свою полную дееспособность даю согласие на обработку персональных данных с использованием средств автоматизации Оператору - федеральному государственному бюджетному учреждению «Российский центр научной информации» (РЦНИ), далее – «Оператор», расположенному по адресу: 119991, г. Москва, Ленинский просп., д.32А, со следующими условиями.

2. Категории обрабатываемых данных: файлы «cookies» (куки-файлы). Файлы «cookie» – это небольшой текстовый файл, который веб-сервер может хранить в браузере Пользователя. Данные файлы веб-сервер загружает на устройство Пользователя при посещении им Сайта. При каждом следующем посещении Пользователем Сайта «cookie» файлы отправляются на Сайт Оператора. Данные файлы позволяют Сайту распознавать устройство Пользователя. Содержимое такого файла может как относиться, так и не относиться к персональным данным, в зависимости от того, содержит ли такой файл персональные данные или содержит обезличенные технические данные.

3. Цель обработки персональных данных: анализ пользовательской активности с помощью сервиса «Яндекс.Метрика».

4. Категории субъектов персональных данных: все Пользователи Сайта, которые дали согласие на обработку файлов «cookie».

5. Способы обработки: сбор, запись, систематизация, накопление, хранение, уточнение (обновление, изменение), извлечение, использование, передача (доступ, предоставление), блокирование, удаление, уничтожение персональных данных.

6. Срок обработки и хранения: до получения от Субъекта персональных данных требования о прекращении обработки/отзыва согласия.

7. Способ отзыва: заявление об отзыве в письменном виде путём его направления на адрес электронной почты Оператора: info@rcsi.science или путем письменного обращения по юридическому адресу: 119991, г. Москва, Ленинский просп., д.32А

8. Субъект персональных данных вправе запретить своему оборудованию прием этих данных или ограничить прием этих данных. При отказе от получения таких данных или при ограничении приема данных некоторые функции Сайта могут работать некорректно. Субъект персональных данных обязуется сам настроить свое оборудование таким способом, чтобы оно обеспечивало адекватный его желаниям режим работы и уровень защиты данных файлов «cookie», Оператор не предоставляет технологических и правовых консультаций на темы подобного характера.

9. Порядок уничтожения персональных данных при достижении цели их обработки или при наступлении иных законных оснований определяется Оператором в соответствии с законодательством Российской Федерации.

10. Я согласен/согласна квалифицировать в качестве своей простой электронной подписи под настоящим Согласием и под Политикой обработки персональных данных выполнение мною следующего действия на сайте: https://journals.rcsi.science/ нажатие мною на интерфейсе с текстом: «Сайт использует сервис «Яндекс.Метрика» (который использует файлы «cookie») на элемент с текстом «Принять и продолжить».