1. Определения и понятия генетических алгоритмов

advertisement
СОДЕРЖАНИЕ
ВВЕДЕНИЕ .............................................................................................................. 3
1.
Определения и понятия генетических алгоритмов ..................................... 4
1.1 История появление и описание генетических алгоритмов .................... 4
1.2 Понятия и определения теории генетических алгоритмов .................... 6
1.3 Эффективность генетического алгоритма ............................................... 8
2.
Генетические операторы ................................................................................ 9
2.1 Оператор репродукции (селекция) ........................................................... 9
2.2 Оператор кроссинговера (скрещивание) ................................................ 10
2.2.1
Простой (одноточечный) оператор кроссинговера ..................... 10
2.2.2
Двухточечный оператор кроссинговера ....................................... 11
2.3 «Жадный оператор» ................................................................................. 12
2.4 Оператор мутации .................................................................................... 12
3.
Простой пример генетического алгоритма ................................................ 13
4.
Генетический алгоритм разбиения графа ................................................... 18
4.1 Постановка задачи и краткое описание.................................................. 18
4.2 Программная реализация ......................................................................... 20
ЗАКЛЮЧЕНИЕ ..................................................................................................... 24
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ ........................................... 25
Приложение А ....................................................................................................... 26
2
ВВЕДЕНИЕ
Большинство задач науки и техники относятся к обширному классу
проблем поиска оптимальных решений, то есть к оптимизационным задачам.
Часть задач оптимизации относится к классу комбинаторных и они, в
большинстве случаев, имеют не одно, а множество решений различного
качества. Существует множество алгоритмов для решения таких задач.
Данная работа посвящена одному из оптимизационных методов. Как
известно, оптимизационные задачи заключаются в нахождении минимума
(максимума) заданной функции. Такую функцию называют целевой. Как
правило, целевая функция — сложная функция, зависящая от некоторых
входных параметров. В оптимизационной задаче требуется найти значения
входных параметров, при которых целевая функция достигает минимального
(максимального) значения. Существует целый класс оптимизационных
методов. С их помощью можно найти экстремальное значение целевой
функции, но проблема в том, не всегда можно быть уверенным, что получено
значение глобального экстремума.
Помимо этой проблемы существует другая — время процесса
вычислений. Зачастую более точные оптимизационные методы работают
очень долго. Для решения поставленных проблем и проводится поиск новых
оптимизационных алгоритмов. Предложенные сравнительно недавно — в
1975 году — Джоном Холландом генетические алгоритмы (ГА) основаны на
принципах естественного отбора Ч. Дарвина. ГА относятся к стохастическим
методам. Эти алгоритмы успешно применяются в различных областях
деятельности (экономика, физика, технические науки и т.п.). Созданы
различные модификации ГА и разработан ряд тестовых функций. Цель
данной работы — рассмотреть, как работают ГА и их программная
реализация в решении задачи разбиения графов.
3
1.
1.1
Определения и понятия генетических алгоритмов
История появление и описание генетических алгоритмов
Генетические алгоритмы — это новая область исследований, которая
появилась в результате работ американского ученого Д.Холланда и его
коллег. Генетические алгоритмы, описанные Д.Холландом, заимствуют в
своей терминологии многое из естественной генетики. Далее будут приведены технические толкования терминов из биологии и генетики, которые
используются в теории и практике генетических алгоритмов. Впервые
генетические алгоритмы были применены к таким научным проблемам, как
распознавание образов и оптимизация. Генетический алгоритм представляет
собой адаптивный поисковый метод, основанный на селекции лучших
элементов в популяции, подобно эволюционной теории Ч. Дарвина.
Основой для возникновения генетических алгоритмов послужили
модель биологической эволюции и методы случайного поиска. Русский
ученый Л. Растригин отмечал, что случайный поиск возник как реализация
простейшей модели эволюции, когда случайные мутации моделировались
случайными шагами оптимального решения, а отбор — «устранением»
неудачных вариантов.
Эволюционный поиск с точки зрения преобразования информации — это
последовательное преобразование одного конечного нечеткого множества
промежуточных решений в другое. Само преобразование можно назвать
алгоритмом поиска, или генетическим алгоритмом. Генетические алгоритмы
— это не просто случайный поиск. Они эффективно используют
информацию, накопленную в процессе эволюции. Цель генетических
алгоритмов состоит в том, чтобы:
 абстрактно
и формально объяснять адаптацию процессов в есте-
ственной системе и интеллектуальной исследовательской системе;
4
 моделировать
естественные эволюционные процессы для эффек-
тивного решения оптимизационных задач науки и техники.
В настоящее время используется новая парадигма решений оптимизационных задач на основе генетических алгоритмов и их различных
модификаций. Генетические алгоритмы осуществляют поиск баланса между
эффективностью и качеством решений за счет «выживания сильнейших
альтернативных решений» в неопределенных и нечетких условиях.
Генетические алгоритмы отличаются от других оптимизационных и
поисковых процедур следующим:
 работают
в основном не с параметрами задачи, а с закодированным
множеством параметров;
 осуществляют
поиск не путем улучшения одного решения, а путем
использования сразу нескольких альтернатив на заданном множестве
решений;
 используют
целевую функцию, а не ее различные приращения для
оценки качества принятия решений;
 применяют
не детерминированные, а вероятностные правила анализа
оптимизационных задач.
Для работы генетических алгоритмов выбирают множество натуральных
параметров
оптимизационной
проблемы
и
кодируют
их
в
по-
следовательность конечной длины в некотором алфавите. Они работают до
тех пор, пока не будет выполнено заданное число генераций (итераций
алгоритма)
или
на
некоторой
генерации
будет
получено
решение
определенного качества, или когда найден локальный оптимум, т. е. возникла
преждевременная сходимость и алгоритм не может найти выход из этого
состояния. В отличие от других методов оптимизации эти алгоритмы, как
правило,
анализируют
различные
области
пространства
решений
одновременно и поэтому они более приспособлены к нахождению новых
областей с лучшими значениями целевой функции.
5
1.2
Понятия и определения теории генетических алгоритмов
Приведем некоторые понятия и определения из теории генетических
алгоритмов. Все генетические алгоритмы работают на основе начальной
информации, в качестве которой выступает популяция альтернативных
решений P. Популяция Pt = { P1, P2, … , Pi, … , PNp } есть множество
элементов Pi, t = 0,1,2… – номер генерации генетического алгоритма, Np –
размер популяции. Каждый элемент этой популяции Pi, как правило,
представляет собой одну или несколько хромосом или особей, или
индивидуальностей (альтернативных упорядоченных или неупорядоченных
решений). Хромосомы состоят из генов Pi = {g 1 , g 2 , … , g v } (элементы,
части закодированного решения), и позиции генов в хромосоме называются
лоци или локус для одной позиции, т. е, ген — подэлемент (элемент в
хромосоме), локус — позиция в хромосоме, аллель — функциональное
значение гена.
Гены могут иметь числовые или функциональные значения. Обычно эти
числовые значения берутся из некоторого алфавита. Генетический материал
элементов обычно кодируется на основе двоичного алфавита {0,1}, хотя
можно использовать буквенные, а также десятичные и другие алфавиты.
Примером закодированной хромосомы длины девять на основе двоичного
алфавита может служить хромосома Pi = (001001101).
Элементы в генетических алгоритмах часто называют родителями.
Родители выбираются из популяции на основе заданных правил, а затем
смешиваются (скрещиваются) для производства «детей» (потомков). Дети и
родители в результате генерации, т. е. одного цикла (подцикла) эволюции,
создают новую популяцию. Генерация, то есть процесс реализации одной
итерации алгоритма, называется поколением.
По аналогии с процессами, происходящими в живой природе, в технике
считают, что эволюция популяции — это чередование поколений, в которых
6
хромосомы изменяют свои значения так, чтобы каждое новое поколение
наилучшим способом приспосабливалось к внешней среде. Тогда общая
генетическая упаковка называется генотипом, а организм формируется
посредством связи генетической упаковки с окружающей средой и
называется фенотипом.
Каждый элемент в популяции имеет определенный уровень качества,
который характеризуется значением целевой функции (в литературе иногда
называется функция полезности, приспособленности или пригодности
(fitness)). Эта функция используется в генетических алгоритмах для
сравнения альтернативных решений между собой и выбора лучших.
Следовательно, основная задача генетических алгоритмов состоит в
оптимизации целевой функции. Другими словами, генетические алгоритмы
анализируют популяцию хромосом, представляющих комбинацию элементов
из некоторого множества, и оптимизируют целевую функцию, оценивая
каждую
хромосому.
Генетические
алгоритмы
анализируют
и
преобразовывают популяции хромосом на основе механизма натуральной
эволюции. Каждая популяция обладает наследственной изменчивостью. Это
означает наличие возможностей случайных отклонений от наиболее
вероятного среднего значения целевой функции.
Отклонения описываются нормальным законом распределения случайных величин. При этом наследственные признаки закрепляются, если они
имеют приспособительный характер, т. е. обеспечивают популяции лучшие
условия существования и размножения.
Так же как процесс эволюции начинается с начальной популяции, так и
алгоритм начинает свою работу с создания начального множества
конкурирующих между собой решений оптимизационной задачи. Затем эти
«родительские»
решения
создают
«потомков»
путем
случайных
и
направленных изменений. После этого оценивается эффективность этих
решений, и они подвергаются селекции. Аналогично естественным системам
здесь
действует
принцип
«выживания
7
сильнейших»,
наименее
приспособленные решения «погибают», а затем процесс повторяется вновь и
вновь.
Традиционные оптимизационные алгоритмы для нахождения лучшего
решения используют большое количество допущений при оценке целевой
функции. Эволюционный подход не требует таких допущений, что
расширяет класс задач, которые можно решать с помощью генетических
алгоритмов. Согласно существующим исследованиям можно сказать, что
генетические алгоритмы позволяют решать те проблемы, решение которых
традиционными алгоритмами затруднительно.
1.3
Эффективность генетического алгоритма
Эффективность генетического алгоритма — степень реализации
запланированных действий алгоритма и достижение требуемых значений
целевой функции. Эффективность во многом определяется структурой и
составом начальной популяции. При создании начального множества
решений происходит формирование популяции на основе четырех основных
принципов:
 «одеяло»
— генерируется полная популяция, включающая все воз-
можные решения в некоторой заданной области;
 «дробовик»
— подразумевает случайный выбор альтернатив из всей
области решений данной задачи.
 «фокусировка»
— реализует случайный выбор допустимых альтер-
натив из заданной области решений данной задачи.
 «комбинирование»
— состоит в различных совместных реализациях
первых трех принципов.
Популяция обязательно является конечным множеством.
8
2.
Генетические операторы
В каждой генерации генетического алгоритма хромосомы являются
результатом применения некоторых генетических операторов.
Оператор — это языковая конструкция, представляющая один шаг из
последовательности действий или набора описаний алгоритма.
Генетический алгоритм состоит из набора генетических операторов.
Генетический оператор по аналогии с оператором алгоритма —
средство отображения одного множества на другое. Другими словами, это
конструкция, представляющая один шаг из последовательности действий
генетического алгоритма.
Рассмотрим основные операторы генетических алгоритмов.
Оператор репродукции (селекция)
2.1
Оператор репродукции (селекция) — это процесс, посредством которого
хромосомы (альтернативные решения), имеющие более высокое значение
целевой
функции
(с
«лучшими»
признаками),
получают
большую
возможность для воспроизводства (репродукции) потомков, чем «худшие»
хромосомы.
Элементы,
выбранные
для
репродукции,
обмениваются
генетическим материалом, создавая аналогичных или различных потомков.
Оператор репродукции считается эффективным, если он создает
возможность перехода из одной подобласти альтернативных решений
области поиска в другую. Это повышает вероятность нахождения глобального оптимума целевой функции. Выделяют два основных типа
реализации оператора репродукции:
 случайный
 выбор
выбор хромосом;
хромосом на основе значений целевой функции.
9
При случайном выборе хромосом частота образования родительских пар
не зависит от значения целевой функции хромосом и полностью
определяется численностью популяции.
Другой способ реализации оператора репродукции связан с использованием значений целевой функции. Существуют две основные
стратегии (стратегия — это оптимальный набор правил и приемов, которые
позволяют реализовать общую цель, достигнуть глобальных и локальных
целей решаемой задачи). В первой предпочтение отдается хромосомам с
близкими и «лучшими» (наибольшими при максимизации и наименьшими —
при минимизации) значениями целевой функции. Во второй — хромосомам,
со значениями целевой функции, сильно различающимися между собой.
2.2
Оператор кроссинговера (скрещивание)
Оператор кроссинговера — это языковая конструкция, позволяющая на
основе преобразования (скрещивания) хромосом родителей (или их частей)
создавать хромосомы потомков. Существует огромное число операторов
кроссинговера, так как их структура в основном и определяет эффективность
генетических
алгоритмов.
Кратко
рассмотрим
основные
известные
операторы кроссинговера.
2.2.1 Простой (одноточечный) оператор кроссинговера
Перед
определяется
началом
работы
так
называемая
одноточечного
точка
оператора
оператора
кроссинговера
кроссинговера,
или
разрезающая точка оператора кроссинговера, которая обычно определяется
случайно. Эта точка определяет место в двух хромосомах, где они должны
быть «разрезаны». Например, пусть популяция P состоит из хромосом P1 и
P 2 , которые выступают в качестве родителей, P = {P 1 , P 2 }. Пусть первый и
10
второй родители имеют вид P1 : 11111, Р2 : 00000. Выберем точку оператора
кроссинговера между вторым и третьим генами в P 1 , P 2 . Тогда, меняя
элементы после точки оператора кроссинговера между двумя родителями,
можно создать два новых потомка. В нашем примере получим:
После применения оператора кроссинговера имеем две старые хромосомы и всегда получаем две новые хромосомы. Схематически простой
оператор кроссинговера показывает преобразование двух хромосом и
частичный обмен информацией между ними, использующий точку разрыва,
выбранную случайно.
2.2.2 Двухточечный оператор кроссинговера
В каждой хромосоме определяются две точки оператора кроссинговера,
и хромосомы обмениваются участками, расположенными между двумя
точками оператора кроссинговера. Например:
Существует большое количество модификаций двухточечного оператора
кроссинговера. Развитием двухточечного оператора кроссинговера является
многоточечный или N-точечный оператор кроссинговера. Многоточечный
оператор кроссинговера выполняется аналогично двухточечному, хотя
11
большое число «разрезающих» точек может привести к потере «хороших»
родительских свойств.
2.3
«Жадный оператор»
Для решения многих оптимизационных задач можно использовать
некоторые классы алгоритмов, называемых «жадными». Такой алгоритм
делает на каждом шаге локально оптимальный выбор, в надежде, что
итоговое решение также окажется оптимальным. Это не всегда так, но для
многих задач такие эвристические алгоритмы дают оптимальный результат.
Говорят, что к оптимизационной задаче применим принцип «жадного»
выбора,
если
последовательность
локально-оптимальных
(«жадных»)
выборов дает оптимальное решение.
«Жадный» оператор — это языковая конструкция, позволяющая
создавать новые решения на основе частичного выбора на каждом шаге
преобразования локально оптимального значения целевой функции.
2.4
Оператор мутации
Оператор мутации — языковая конструкция, позволяющая на основе
преобразования
родительской
хромосомы
(или
ее
части)
создавать
хромосому потомка.
Оператор мутации обычно состоит из двух этапов:
1.
В хромосоме А = (a1, a2, a3, … , aL-2, aL-1, aL) определяются слу-
чайным образом две позиции (например, a2 и a L-1 ).
1.
Гены, соответствующие выбранным позициям, переставляются, и
формируется новая хромосома А’ = (a1, aL-1, a3, … , aL-2, a2, aL).
Например:
12
Существует еще достаточно большое количество операторов, но они в
той или иной степени являются производными от уже перечисленных,
поэтому их описание опустим.
3.
Простой пример генетического алгоритма
Рассмотрим принципы работы генетических алгоритмов на максимально
простом примере. Пусть требуется найти глобальный минимум функции
на отрезке [0; 7] (рис. 1). На этом отрезке функция принимает
минимальное значение в точке x = 1. Очевидно, что в точке x = 6 функция
попадает в локальный минимум. Если для нахождения глобального
минимума использовать градиентные методы, то в зависимости от
начального приближения можно попасть в данный локальный минимум.
Рассмотрим на примере данной задачи принцип работы генетических
алгоритмов. Для простоты положим, что x принимает лишь целые значения,
т.е. x ∈ {0,1,2,3,4,5,6,7}. Это предположение существенно упростит
изложение, сохранив все основные особенности работы генетического
алгоритма.
Выберем случайным образом несколько чисел на отрезке [0; 7]:{2,3,5,4}.
Будем рассматривать эти числа в качестве пробных решений нашей задачи.
13
Рис. 1. График целевой функции с выбранными значениями
пробных решений
Основной
идеей
генетических
алгоритмов
является
организация
«борьбы за существование» и «естественного отбора» среди этих пробных
решений. Запишем пробные решения в двоичной форме: {010,011,101,100}.
Как известно, принцип естественного отбора заключается в том, что в
конкурентной борьбе выживает наиболее приспособленный. В нашем случае
приспособленность особи определяется целевой функцией: чем меньше
значение целевой функции, тем более приспособленной является особь, т.е.
пробное решение, использовавшееся в качестве аргумента целевой функции
(см. таблицу 1).
Таблица 1 – Исходная популяция
№
1
2
3
4
Целое число
2
3
5
4
Особь
Двоичное число
010
011
101
100
14
Приспособленность
-0,33
7,25
7,92
10,33
Теперь приступим к процессу размножения: попробуем на основе
исходной популяции создать новую, так чтобы пробные решения в новой
популяции были бы ближе к искомому глобальному минимуму целевой
функции. Для этого сформируем из исходной популяции брачные пары для
скрещивания. Поставим в соответствие каждой особи исходной популяции
случайное целое число из диапазона от 1 до 4. Будем рассматривать эти
числа как номера членов популяции. При таком выборе какие-то из членов
популяции не будут участвовать в процессе размножения, так как образуют
пару сами с собой. Какие-то члены популяции примут участие в процессе
размножения неоднократно с различными особями популяции. Процесс
размножения (рекомбинация) заключается в обмене участками хромосом
между родителями. Например, пусть скрещиваются две хромосомы 111111 и
000000. Определяем случайным образом точку разрыва хромосомы, пусть,
это будет 3: 111|111 и 000|000. Теперь хромосомы обмениваются частями,
стоящими после точки разрыва, и образуют двух новых потомков: 111000 и
000111.
Для нашей популяции процесс создания первого поколения потомков
показан в таблице 2.
Таблица 2 – Одноточечный кроссинговер
№
1
2
3
4
Особь
популяции
010
011
101
100
Выбранный
номер
1
4
3
1
Вторая
Точка
особь-родитель кроссинговера
010
1
100
101
2
010
Особи
потомки
000
110
100
011
Следующим шагом в работе генетического алгоритма являются
мутации, т.е. случайные изменения полученных в результате скрещивания
хромосом. Пусть вероятность мутации равна 0,3. Для каждого потомка
возьмем случайное число на отрезке [0; 1], и если это число меньше 0,3, то
15
инвертируем случайно выбранный ген (заменим 0 на 1 или наоборот) (см.
таблицу 3)
Таблица 3 – Мутация потомков
№ Особи- Случай Выбранн Потомок Приспособлен
потомки
ное
ый ген
после
ность до
число
для
мутации
мутации
мутации
1
000
0,1
3
001
5
2
110
0,6
110
5
3
100
0,5
100
10,33
4
011
0,2
1
111
7,25
Приспособлен
ность после
мутации
-5,42
5
10,33
12,58
Как видно на примере, мутации способны улучшить (первый потомок)
или ухудшить (четвертый потомок) приспособленность особи-потомка. В
результате
скрещивания
хромосомы
обмениваются
«хвостами»,
т.е.
младшими разрядами в двоичном представлении числа. В результате
мутаций изменению может подвергнуться любой разряд, в том числе,
старший. Таким образом, если скрещивание приводит к относительно
небольшим изменениям пробных решений, то мутации могут привести к
существенным изменениям значений пробных решений (см. рис. 2).
Рисунок 2 – Изменение популяции в процессе естественного отбора
16
Теперь из четырех особей-родителей и четырех полученных особей
потомков необходимо сформировать новую популяцию. В новую популяцию
отберем четыре наиболее приспособленных особей из числа «старых» особей
и особей-потомков (см. таблицу 4).
Таблица 4 – Формирование новой популяции
№ Особи Приспособлен
Новая
ность
популяция
1
2
3
4
5
6
7
8
010
011
101
100
001
110
100
111
-0,33
7,25
7,92
10,33
-5,42
5
10,33
12,58
001
010
110
011
Приспособленность
особей в новой
популяции
-5,42
-0,33
5
7,25
В результате получим новое поколение, которое представлено на рис. 3.
Рисунок 3 – Минимизируемая функция и особи новой популяции
Получившуюся
популяцию
можно
будет
вновь
подвергнуть
кроссинговеру, мутации и отбору особей в новое поколение. Таким образом,
17
через несколько поколений мы получим популяцию из похожих и наиболее
приспособленных особей. Значение приспособленности наиболее «хорошей»
особи (или средняя приспособленность по популяции) и будет являться
решением нашей задачи. Следуя этому, в данном случае, взяв наиболее
приспособленную особь 001 во втором поколении, можно сказать, что
минимумом целевой функции является значение −5,42, соответствующее
аргументу x = 1. Тем самым попадания в локальный минимум удалось
избежать. На данном примере разобран вариант простого генетического
алгоритма. При дальнейшем использования ГА к разным задачам возможно
моделирование основных операторов алгоритма.
4. Генетический алгоритм разбиения графа
4.1
Постановка задачи и краткое описание
Пусть задан граф G = (X, U), где X представляет множество вершин
графа, U – множество ребер. Пусть B = {B1, B2, … , Bs} – множество
разбиений графа G на части B1, B2, … , Bs, такие, что
и
,
. Пусть каждое разбиение Bi состоит из элементов
Bi = {b1, b2, … , bn},
. Тогда задача разбиения графа G на части
заключается в получении разбиения
, удовлетворяющего трем
условиям:
,
,
Целевая функция для разбиения графа G запишется так:
18
Где Ki,j – число связей между частями Bi и Bj при разбиении графа G на
части; s – количество частей в разбиении; K – суммарное количество ребер
при разбиении графа на части.
Стандартная
задача
разбиения
заключается
в
минимизации
K
. Минимизация K при разбиении графа на части позволяет
косвенно учитывать многие критерии исследуемой модели при решении
оптимизационной задачи.
Рассмотрим последовательный генетический алгоритм разбиения графа
G = (X, U). Первоначально упорядочим все вершины графа по возрастанию
локальных степеней вершин. Это соответствует списку вершин, а также
может соответствовать тривиальному разбиению, когда количество групп
разбиения
равно
упорядоченным
вершинам
графа.
Далее
начинаем
составлять пары вершин и для каждой вычислять значение целевой функции,
которая в данном случае является оценкой связности:
где ei,j – число ребер между вершинами xi и xj, образовавшими пару; ei,t и
ej,t – число ребер, соединяющих выбранную пару со всеми остальными
вершинами графа, причем t = 1,2, … , n-2.
Составим второй список, где будут оставлены такие пары вершин, у
которых
. Если пары
не находятся, то возможно два случая. В
первом строятся пары с наименее отрицательным
, а во стразу происходит
переход к образованию групп из трех вершин. Далее процесс повторяется
аналогично, пока не будет выполнено разбиение графа на заданное или
произвольное число частей. Это основная идея стандартной процедуры
разбиения. При небольшом количестве вершин она дает удовлетворительные
результаты, так как практически здесь осуществляется полный перебор
19
вариантов решений. С увеличением числа вершин использование напрямую
такого подхода становится нецелесообразно и в таких случаях применяются
модифицированные схемы с применением генетических операторов. Для
сокращения
числа
модифицированные
просматриваемых
генетические
пар
операторы.
применим
Отметим,
простые
что
и
вместо
выражения (4.1) можно использовать выражение
где
– внутренние ребра разбиения,
(t = 1,2, … , n-2) – внешние ребра
разбиения.
4.2
Для
Программная реализация
реализованной
в
данной
работе
программы
применен
последовательный генетический алгоритм разбиения графа с использованием
концепции «жадного» генетического оператор. Программа осуществляет
разбиение графа на максимально малые равные части. Если количество
вершин графа является простым числом, то для такого графа возможно лишь
тривиальное разбиение на равные части, когда каждая вершина является
частью разбиения.
Входные данные: граф G = (X, U), количество вершин которого и ребра
вводятся в начале выполнения программы.
Выходные данные: множество частей разбиения графа, выводимые на
экран в виде заключенных в круглые скобки номеров вершин, разделенных
пробелами.
В качестве примера возьмем граф, представленный на рис. 4.
20
Рисунок 4 – Граф G
В начале выполнения, при вводе данных, заполняется матрица
смежности для вершин графа.
Если число вершин графа простое, то разбиение не выполняется, так как
для такого графа возможно лишь тривиальное разбиение на равные части.
Проверка на простоту выполняется функцией Prime.
Далее функцией IntentionPopulation заполняется вектор, состоящий из
пар, содержащих множество вершин, задающее возможную часть разбиения
и значение целевой функции для него. В качестве целевой взята функция:
где ei,j – число ребер между вершинами (xi, xi+1, … , xj-1, xj),
образовавшими пару;
– число ребер, соединяющих
выбранную пару со всеми остальными вершинами графа.
Количество вершин для одной части разбиения вычисляется в функции
PartSize.
Во время выполнения, функция IntentionPopulation использует функцию
SortingPopulation, которая в связке с функцией PartSet рекурсивно составляет
множество всех возможных частей разбиения заданного размера; а также
функцию IntentValue, которая вычисляет значение целевой функции для
каждого составленного множества. Для графа G, представленного на рис. 4
размер части разбиения будет равен двум. На таблице 5 приведены все пары
21
(xi,xj), которые можно получить, объединяя вершины данного графа и
значения целевой функции
для них.
Таблица 5 – Пары вершин графа G
(xi,xj)
(1,2) (1,3)
6
(xi,xj)
0
(3,4) (3,5)
7
7
(1,4)
(1,5)
9
9
(3,6)
(4,5)
9
1
(1,6) (2,3) (2,4)
8
10
7
(2,5) (2,6)
10
0
(4,6) (5,6)
9
6
Далее производится сортировка вектора пар, содержащего все пары и
значения целевой функции для них, по возрастанию в соответствии со
значением целевой функции.
После этого по отсортированному вектору составляется решение задачи
разбиения графа:
1. В выходное множество записывается пара вершин из первого элемента
вектора.
2. Из вектора, с помощью процедуры ExcessCut удаляются все пары
вершин, в которых используются вершины уже вынесенной в решение
пары.
3. Если в векторе еще остались элементы, переход к шагу 1, если нет –
вывод на экран результата разбиения.
Результат разбиения для графа G, представленного на рис. 4 приводится
на рис.5.
22
Рисунок 5 – Результат разбиения графа G
23
ЗАКЛЮЧЕНИЕ
В данной работе рассмотрена структура генетических алгоритмов и их
возможности, в частности для решения задачи разбиения графа.
Преимущества генетических алгоритмов заключаются в том, что они не
требуют никакой информации о поведении функции; пригодны для решения
широкого
класса
задач,
в
том
числе
крупномасштабных
проблем
оптимизации; относительно стойки к попаданию в локальные оптимумы;
относительно просты в реализации.
Однако остается ряд еще не до конца разрешенных проблем: с помощью
генетических
алгоритмов
проблематично
найти
точный
глобальный
оптимум; непросто смоделировать алгоритм для нахождения всех решений
задачи; неэффективно применять в случае оптимизации функции, требующей
большого времени на вычисление.
Реализована программа на языке C++, осуществляющая решение задачи
разбиения графов, которая эффективно работает с графами не очень большой
размерности. Планируется реализация генетических операторов, которые
позволят работать с графами гораздо большей размерности.
24
СПИСОК ИСПОЛЬЗОВАННЫХ ИСТОЧНИКОВ
Гладков Л. А., Курейчик В. В., Курейчик В. М. Генетические алгоритмы.
Физматлит, 2006.
2. Курейчик В. М., Лебедев Б.К., Лебедев О.Б. Поисковая адаптация:
теория и практика. Физматлит, 2006.
3. Панченко Т.В. Генетические алгоритмы. Издательский дом
«Астраханский университет», 2007.
4. Кочетов Ю.А., Плясунов А.В. Генетический локальный поиск для
задачи о разбиении графа на доли ограниченной мощности. 2012.
5. Макконнелл Дж. Основы современных алгоритмов 2-е дополненное
издание. Техносфера, 2004.
6. Батищев Д.А. Генетические алгоритмы решения экстремальных задач.
ВГТУ, 1995.
7. Емельянов В. В., Курейчик В. В., Курейчик В. М. Генетические
алгоритмы. Физматлит, 2003.
8. Курейчик В. В. Эволюционные методы решения оптимизационных
задач. Монография. ТРТУ, 1999.
9. Лебедев Б. К., Лебедев В. Б. Разбиение на основе роевого интеллекта и
генетической эволюции.
10. Батищев Д. И., Старостин Н. В. Задачи декомпозиции графов. 2001.
11. [http://algolist.manual.ru/ai/ga/ga1.php] Популярно о генетических
алгоритмах. 07.04.2014.
1.
25
Приложение А
#include
#include
#include
#include
<iostream>;
<set>;
<vector>;
<algorithm>;
using namespace std;
vector<pair<set <int>, int>> TempPopulation;
bool SortFunction(pair<set <int>, int> elem1, pair < set <int>, int> elem2)
{
return elem1.second < elem2.second;
}
void ExcessCut(vector<pair<set <int>, int>> & Population, set <int> & ExcludedVertex)
{
for (set<int>::iterator it = Population[0].first.begin(); it !=
Population[0].first.end(); it++)
ExcludedVertex.insert(*it);
vector<pair<set <int>, int>>::iterator IteratorPopulation = Population.begin();
while (IteratorPopulation != Population.end())
{
bool temp = false;
for (set<int>::iterator it = IteratorPopulation[0].first.begin(); it !=
IteratorPopulation[0].first.end(); it++)
{
if (ExcludedVertex.count(*it)){
IteratorPopulation = Population.erase(IteratorPopulation);
temp = true;
break;
}
}
if (!temp)
if ((IteratorPopulation++ != Population.end()) && (IteratorPopulation !=
Population.end()))
IteratorPopulation++;
}
}
bool Prime(unsigned long NumberVertex) // Простое ли число;
{
unsigned long i, j, bound;
if (NumberVertex == 0 || NumberVertex == 1)
return false;
if (NumberVertex == 2 || NumberVertex == 3 || NumberVertex == 5)
return true;
if (NumberVertex % 2 == 0 || NumberVertex % 3 == 0 || NumberVertex % 5 == 0)
return false;
bound = sqrt((double)NumberVertex);
i = 7; j = 11;
while (j <= bound && NumberVertex%i && NumberVertex%j)
{
i += 6; j += 6;
}
if (j <= bound || i <= bound && NumberVertex%i == 0)
return false;
return true;
}
26
int PartSize(int NumberVertex) // вычисление максимально малой части разбиения
{
if (NumberVertex % 2 == 0)
return 2;
if (NumberVertex % 3 == 0)
return 3;
if (NumberVertex % 5 == 0)
return 5;
for (int divisor = 7; divisor <= NumberVertex; divisor += 2)
{
if (NumberVertex % divisor == 0)
{
return divisor;
break;
}
}
}
int IntentValue(set <int> ElementPopulation, vector <vector<int>> AdjancencyMatrix)
вычисление значения целевой функции
{
int external = 0, internal = 0;
for (set<int>::iterator it = ElementPopulation.begin(); it !=
ElementPopulation.end(); it++)
{
for (int i = 1; i <= AdjancencyMatrix.size(); i++)
{
if (AdjancencyMatrix[*it - 1][i - 1] != 0)
if (ElementPopulation.count(i))
{
if (*it < i)
internal += AdjancencyMatrix[*it - 1][i - 1];
}
else
external += AdjancencyMatrix[*it - 1][i - 1];
}
}
int Value = external - internal;
return Value;
}
//
set <int> PartSet(int* PopulationMatrix, int NumberVertex)
{
set <int> ElemPopulation;
for (int i = 0; i < PartSize(NumberVertex); i++)
ElemPopulation.insert(PopulationMatrix[i]);
return ElemPopulation;
}
vector<pair<set <int>, int>> SortingPopulation(int temp, int* PopulationMatrix, int
NumberVertex, vector <vector<int>> AdjancencyMatrix)
{
if (temp >= 0)
{
if (PopulationMatrix[temp] < NumberVertex + temp - PartSize(NumberVertex) +
1)
{
++PopulationMatrix[temp];
for (int i = temp + 1; i < PartSize(NumberVertex); i++)
PopulationMatrix[i] = PopulationMatrix[i - 1] + 1;
set <int> Part = PartSet(PopulationMatrix, NumberVertex);
TempPopulation.push_back(make_pair(Part, IntentValue(Part,
AdjancencyMatrix)));
27
SortingPopulation(PartSize(NumberVertex) - 1, PopulationMatrix,
NumberVertex, AdjancencyMatrix);
}
else
SortingPopulation(temp - 1, PopulationMatrix, NumberVertex,
AdjancencyMatrix);
}
else
return TempPopulation;
return TempPopulation;
}
vector<pair<set <int>, int>> IntentionPopulation(int NumberVertex, vector<vector <int>>
AdjacencyMatrix)
{
int* PopulationMatrix = new int[PartSize(NumberVertex)];
for (int i = 0; i < PartSize(NumberVertex); i++)
PopulationMatrix[i] = i + 1;
vector<pair<set <int>, int>> ResultPopulation;
ResultPopulation.push_back(make_pair(PartSet(PopulationMatrix, NumberVertex),
IntentValue(PartSet(PopulationMatrix, NumberVertex), AdjacencyMatrix)));
vector<pair<set <int>, int>> temp = SortingPopulation(PartSize(NumberVertex) - 1,
PopulationMatrix, NumberVertex, AdjacencyMatrix);
ResultPopulation.insert(ResultPopulation.end(), temp.begin(), temp.end());
return ResultPopulation;
}
void main(){
setlocale(LC_ALL, "Rus");
int StartArc = 1, EndArc = 0, Num = 0;
int NumberVertex;
cout << "Введите кол-во вершин:";
cin >> NumberVertex;
cout << endl;
if (Prime(NumberVertex)){
cout << "Возможно лишь тривиальное разбиение на равные части!" << endl;
system("pause");
exit(0);
}
vector<vector <int>> AdjacencyMatrix(NumberVertex, vector<int>(NumberVertex, 0));
while (StartArc != 0) // ввод дуг и заполнение матрицы смежности
{
cout << "Начало дуги: "; cin >> StartArc;
if (StartArc == 0)
break;
cout << "Конец дуги: "; cin >> EndArc;
cout << "Кол-во ребер: "; cin >> Num;
AdjacencyMatrix[StartArc-1][EndArc-1] = Num;
AdjacencyMatrix[EndArc-1][StartArc-1] = Num;
}
std::vector<pair<set <int>, int>> Population;
Population = IntentionPopulation(NumberVertex, AdjacencyMatrix);
std::sort(Population.begin(), Population.end(), SortFunction); // сортировка по
значению целевой функции
set <set <int>> GraphPart;
set <int> ExcludedVertex;
int i = 0;
cout << endl;
while (i != Population.size())
{
28
GraphPart.insert(Population[i].first);
cout << "( ";
for (set<int>::iterator it = Population[i].first.begin(); it !=
Population[i].first.end(); it++)
cout << *it << " ";
cout << ")" << endl;
ExcessCut(Population, ExcludedVertex);
}
system("pause");
}
29
Download