Parallelizing the ant colony algorithm for solving the knapsack problem as an example using Python

Cover Page

Cite item

Full Text

Abstract

The paper considers the ant colony algorithm and describes the process of its parallelization using Python and multiprocessing module. Using the example of the knapsack problem, it is shown that distributing tasks among a number of processes allows to improve the performance of the algorithm while maintaining its efficiency. Compared to exact methods, like dynamic programming, the use of the ant colony algorithm showed a significant reduction in execution time with an acceptable level of deviation from the optimal solution. The advantage of parallelization algorithms is the efficient utilization of the computing system, where all available processor cores are used, resulting in faster execution of more iterations in the same time. The results obtained confirm the potential of AСO for solving complex problems with limited computation time.

Full Text

Введение

Алгоритм муравьиной колонии (АСО) является одним из самых мощных инструментов в комбинаторной оптимизации. Он используется в логистике, робототехнике, туризме, сетевом планировании и других областях [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 позволяет эффективно распараллелить алгоритм муравьиной колонии, что делает распараллеливание доступным для разработчиков любого уровня.
×

About the authors

Marsel R. Vagizov

St. Petersburg State Forestry Engineering University named after S.M. Kirov

Author for correspondence.
Email: bars-tatarin@yandex.ru
ORCID iD: 0000-0003-4848-1619
SPIN-code: 4811-8943

Candidate of Technical Sciences, Head of the Department of Information Systems and Technologies

Russian Federation, 194021, St. Petersburg, 5 Institutsky lane

Sergey P. Khabarov

St. Petersburg State Forestry Engineering University named after S.M. Kirov

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

Candidate of Technical Sciences, Associate Professor of the Department of Information Systems and Technologies

Russian Federation, 194021, St. Petersburg, 5 Institutsky lane

References

  1. Darintsev O.V., Migranov A.B. Using the ant algorithm to search for a strategy for the behavior of a group of mobile robots on a work field with obstacles. Multiphase systems. 2022. Vol. 17. No. 3-4. Pp. 177-186. doi: 10.21662/mfs2022.3.016. (In Russian)
  2. Minin A.A., Nemtinov V.A. Application of the ant colony algorithm for creating technological processes of cutting. Engineering technologies. 2023. No. 3. Pp. 31-36. EDN: LAMZQA. (In Russian)
  3. Pavlovskaya K.A., Chervinsky V.V. Routing in MANET networks based on ant algorithms taking into account energy saving. Bulletin of Donetsk National University. Series G: Technical Sciences. 2023. No. 1. Pp. 4-10. EDN: SWIUGB. (In Russian)
  4. Vagizov M.R., Khabarov S.P. Constructing program trajectories of motion based on solving the Dubins Machine problem. Information and Space. 2021. No. 3. Pp. 116–125. EDN: DDDWFN. (In Russian)
  5. Korenev A.S., Skrypka A.S., Khabarov S.P. Autonomous navigation on operating vessels. Marine Bulletin. 2022. № 1(81). Pp. 92–95. EDN: MIPIOP. (In Russian)
  6. Khabarov S.P., Shilkina M.L. Geometric approach to solving the problem for Dubins machines in the formation of program trajectories of motion. Bulletin of information technologies, mechanics and optics. 2021. Vol. 21. No. 5. Pp. 653–663. doi: 10.17586/2226-1494-2021-21-5-653-663. (In Russian)
  7. Dumov M.I. Modeling of wireless networks in the OMNeT++ environment using the INET framework. Scientific and Technical Bulletin of Information Technologies, Mechanics and Optics. 2019. Vol. 19. No. 6. Pp. 1151–1161. doi: 10.17586/2226-1494-2019-19-6-1151-1161. (In Russian)

Supplementary files

Supplementary Files
Action
1. JATS XML
2. Fig. 1. One- and four-stream solution of the knapsack problem

Download (456KB)
3. Fig. 2. Loading of 4 cores when solving the knapsack problem

Download (161KB)
4. Fig. 3. Knapsack problem: 100 items, 10 iterations and 100 ants

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

Download (190KB)

Copyright (c) 2024 Вагизов М.R., Хабаров С.P.

Creative Commons License
This work is licensed under a 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») на элемент с текстом «Принять и продолжить».