Генетическое программирование
6.2. Структуры для представления программ
Терминалы и функции должны быть объединены по определенным правилам в некоторые структуры, которые могут представлять программы и использоваться при их выполнении. Выбор структуры существенно влияет на порядок выполнения программы, распределение и использование локальной или глобальной памяти.
В настоящее время наиболее распространенными структурами для представления особей (потенциальных решений проблемы) являются:
- древовидное представление;
- линейная структура;
- графоподобная структура.
6.2.1. Древовидное представление
Значительная часть работ в области ГП, в которых были получены положительные результаты, выполнялась на языке программирования задач искусственного интеллекта LISP, где программу удобно представлять в виде дерева. Поэтому в ГП была предложена древовидная форма генома. Мы в качестве примера для удобства будем использовать арифметические формулы, которые также удобно представлять деревом. Рассмотрим арифметическую формулу (в обычном представлении). Этой формуле соответствует -выражение , которое по сути является префиксной (польской) записью формулы, где знак операции стоит перед двумя аргументами. Следует отметить, что скобки здесь при желании можно убрать. Часто используется также и постфиксная запись формулы, где знак операции стоит после аргументов – . Легко заметить, что дерево (генотип) рис.6.1 также представляет эту формулу (фенотип) .
При этом листья дерева соответствуют терминалам, а внутренние узлы – функциям. Заметим, что префиксная и постфиксная запись может быть получена из дерева путем различного обхода дерева. Например, постфиксная запись строится из дерева по Кнуту [4] с помощью следующего обхода:
- обход левого дерева снизу;
- обход правого дерева снизу;
- посещение корня.
Для нашего примера вершины дерева посещаются в следующем порядке:
Древовидная форма представления генотипа оказывается для данного класса задач более эффективной, и позволяет работать с программами или выражениями различной длины. Важным аспектом является также использование памяти при выполнении программы. Древовидная структура позволяет использовать только локальную память в процессе выполнения. Локальность памяти встроена в саму древовидную структуру. Значения переменных доступны для функции только в дереве, корню которого соответствует функция. Например, значения переменных , являются локальными относительно узла"/".
6.2.2. Линейные структуры
Древовидное представление особей первоначально было ориентировано на программы, написанные на LISP, и менее подходит, например, для программ, написанных на Си. Далее будет рассмотрен один из возможных вариантов линейной структуры ГП, ориентированного на подмножество Си [3,5]. При этом каждая особь (программа) представлена последовательностью переменной длины операторов Си. На рис.6.2 представлен пример такой программы [6].
Здесь функциональное множество ("instruction set" или "functional set") состоит из арифметических операций, условных операторов if и вызовов функций. Общая нотация для операторов каждого типа представляется в таблице 6.1.
Из таблице 6.1 видно, что за исключением условных операторов, все операторы имеют явную операцию присваивания переменной . Это позволяет использовать программы с "кратными выходами", которые в процессе выполнения изменяют и передают на выход значение нескольких переменных в отличие от программ, построенных на древовидных структурах, где на выход передается, как правило, значение только одной переменной, связанной с корнем дерева. Все операторы выполняются либо над двумя переменными, либо над переменной и константой. До начала работы программы переменным должны быть присвоены соответствующие значения.
Переменные и константы вместе образуют множество терминальных символов. При этом подходе любой оператор кодируется 4-х мерным вектором, компонентами которого является тип оператора (одна компонента) и адреса (указатели) переменных и констант.
Например, оператор представляется вектором . Каждая компонента использует 1 байт памяти, следовательно, число переменных (и констант) ограниченно сверху 256.
Такое представление позволяет эффективно выполнять рекомбинацию программы и их интерпретацию. Для частично определенных операторов и их функций, в случае неопределенных значений на выходе возвращается "-1". Последовательности условных операторов интерпретируются как вложенные условные операторы (как это трактуется в Си). В случае ложного значения условия оператора пропускается один следующий по порядку оператор. Такая интерпретация условных операторов дает, с одной стороны, достаточно выразительную мощность, и, с другой стороны, упрощает в дальнейшем работу по обнаружению и устранению интронов (ненужных участков кода, описанных в разделе 6.7), что само по себе представляет в ГП значительную проблему. Следует отметить, что в линейном представлении, в отличие от древовидного, для функции нет очевидного способа определения значений аргументов (в древовидном представлении они однозначно определяются узлами, находящимися ниже соответствующего функционального узла). Существенным отличием является также глобальное использование памяти при выполнении программы в отличие от локального в древовидных структурах. Здесь значения переменных доступны для всех функций.
Следует отметить, что данный подход является более выразительным (позволяющим генерировать более сложные программы), чем "регистровая машина" с линейной последовательностью команд, которая часто используется для представления линейных структур [5].
В эволюционном алгоритме при этом подходе обычно используется метод турнирного отбора родителей в промежуточную популяцию, описанный в разделе 4. Мощность популяции обычно поддерживается постоянной.