Федеральное государственное автономное образовательное учреждение высшего профессионального образования

реклама
Федеральное государственное автономное образовательное учреждение
высшего профессионального образования
НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ
ВЫСШАЯ ШКОЛА ЭКОНОМИКИ
МОСКОВСКИЙ ИНСТИТУТ ЭЛЕКТРОНИКИ И МАТЕМАТИКИ
НАЦИОНАЛЬНОГО ИССЛЕДОВАТЕЛЬСКОГО УНИВЕРСИТЕТА
«ВЫСШАЯ ШКОЛА ЭКОНОМИКИ»
Кафедра "Вычислительные системы и сети" (ВСиС)
ПОЯСНИТЕЛЬНАЯ ЗАПИСКА
к дипломному проекту
На тему:
«Реализация метода интеллектуального планирования
семейства STRIPS на языке С++»
Студентка
Березина Кристина Анатольевна
Руководитель проекта
В.Э. Карпов
Допущена к защите
2013 г.
КОНСУЛЬТАНТЫ ПРОЕКТА:
Специальная часть
В.Э. Карпов
Рецензент
С.А. Зинченко
Зав. кафедрой
А.В. Вишнеков
МОСКВА
АННОТАЦИЯ
Данный проект посвящен созданию программного обеспечения системы интеллектуального планирования семейства STRIPS на языке высокого
уровня С++ с использованием объектно-ориентированного стиля программирования. В качестве интегрированной среды разработки была взята среда Microsoft Visual Studio 2010 Professional.
Пояснительная записка состоит из следующих глав:
1) «Введение» - здесь дается определения понятиям «планирование» и
«интеллектуальное планирование», а также обосновывается выбор разработки программы, описывается цель работы и постановка задачи.
2) «История искусственного интеллекта» - здесь описана краткая история искусственного интеллекта, связанная с системой интеллектуального
планирования.
3) «Автоматическое планирование и диспетчеризация» - здесь более
подробно рассказывается о планировании, а также о языках и алгоритмах
планирования.
4) «Анализ методов интеллектуального планирования» - здесь идет обзор известных систем искусственного интеллекта: GPS, QA3 и STRIPS.
5) «Система построения планов» - здесь описывается теория, на основе
которой построена система STRIPS.
6) «Система STRIPS» - здесь приводится общий вид алгоритма STRIPS
и его недостатки при реализации.
7) «Обзор реализации планировщика на языке логического программирования» - здесь кратко рассматривается реализация системы STRIPS на языке Пролог.
8) «Разработка методов и алгоритмов» - здесь описываются используемые преимущества языка С++ и объектно-ориентированного стиля програм-
10
мирования, также разработана общая блок-схема работы программы и описывается облачная структура, которая использована в программе.
9) «Разработка программы и методик испытаний» - здесь описаны типы
данных, применяемых в программе, показан формат входных и выходных
данных, описана реализация работы программы с текстовыми файлами, а
также проведены тестовые примеры работы программы.
10) «Охрана труда» - здесь описывается как робот с интеллектуальной
системой управления, использующий планирование, может помочь человеку
в работе с задачами высокого уровня сложности, в том числе работа в местах
радиации.
11) «Расчет надежности» - здесь вычисляется завершенность, устойчивость, восстанавливаемость, доступность или готовность.
12) «Расчет сметной стоимости разработки» - здесь представлена экономическая часть проекта.
13) «Заключение» - здесь описываются полученные результаты в соответствие с постановкой задачи и с целью.
14) «Список литературы» - здесь приведен весь список литературы, которая была использована при работе над дипломным проектом.
15) «Приложение 1. История искусственного интеллекта» - здесь описана более полная история искусственного интеллекта, связанная с системой
интеллектуального планирования.
16) «Приложение 2. Текст программы» - здесь представлен весь текст
программы по реализации системы планирования.
17) «Приложение 3. Графические материалы»
Дипломный проект включает в себя:
 169 страниц;
 1757 строк программного кода;
 7 графических материалов.
11
ОГЛАВЛЕНИЕ
ВВЕДЕНИЕ ............................................................................................................ 14
1. ИСТОРИЯ ИСКУССТВЕННОГО ИНТЕЛЛЕКТА........................................ 17
2. АВТОМАТИЧЕСКОЕ ПЛАНИРОВАНИЕ И ДИСПЕТЧЕРИЗАЦИЯ ....... 19
2.1. Обзор ..................................................................................................... 19
2.2. Языки планирования ............................................................................ 22
2.3. Алгоритмы планирования ................................................................... 23
3. АНАЛИЗ МЕТОДОВ ИНТЕЛЛЕКТУАЛЬНОГО ПЛАНИРОВАНИЯ ....... 25
3.1. Обзор системы GPS ............................................................................. 25
3.2. Обзор системы QA3 ............................................................................. 29
3.3. Обзор системы STRIPS........................................................................ 33
3.4. Вывод по обзору систем ...................................................................... 36
4. СИСТЕМА ПОСТРОЕНИЯ ПЛАНОВ ........................................................... 37
4.1. Решение задач роботом ....................................................................... 37
4.1.1. Описания состояний и цели ................................................... 37
4.1.2. Моделирование действий робота .......................................... 38
4.1.3. Проблема фрейма.................................................................... 40
4.2. Прямой поиск в пространстве состояний .......................................... 41
4.3. Обратный поиск в пространстве состояний ...................................... 44
5. СИСТЕМА STRIPS ........................................................................................... 46
5.1 Общий вид алгоритма STRIPS ............................................................ 50
5.2. Неполнота алгоритма STRIPS ............................................................ 52
5.3. Применение системы STRIPS для реального объекта ..................... 56
6. ОБЗОР РЕАЛИЗАЦИИ ПЛАНИРОВЩИКА НА ЯЗЫКЕ ЛОГИЧЕСКОГО
ПРОГРАММИРОВАНИЯ ................................................................................ 58
7. РАЗРАБОТКА МЕТОДОВ И АЛГОРИТМОВ .............................................. 65
7.1. Использование преимуществ языка С++ ........................................... 65
7.2. Использование преимуществ объектно-ориентированного
программирования .............................................................................. 67
12
7.3. Общая схема алгоритма программы .................................................. 68
7.4. Организация облачной структуры ..................................................... 71
7.5. Эвристическая оценка алгоритма ....................................................... 73
8. РАЗРАБОТКА ПРОГРАММЫ ........................................................................ 74
8.1. Описание типов данных ...................................................................... 74
8.1.1. Описание объекта ................................................................... 74
8.1.2. Описание состояний кубика/робота ..................................... 75
8.1.3. Описание отношений состояния и объекта.......................... 77
8.1.4. Описание правил для объекта ............................................... 79
8.1.5. Описание облаков ................................................................... 82
8.2. Входные и выходные данные ............................................................. 85
8.3. Работа с файлами ................................................................................. 90
8.4. Методы испытаний .............................................................................. 90
8.5. Описание тестовых примеров и результатов их выполнения ......... 91
8.5.1. Тест 1. Диагностика ошибок.................................................. 91
8.5.2. Тест 2. Классическая задача .................................................. 92
8.5.3. Тест 3. Работа с позициями .................................................... 93
8.5.4. Выводы..................................................................................... 97
9. ОХРАНА ТРУДА .............................................................................................. 98
10. РАСЧЕТ НАДЕЖНОСТИ ............................................................................ 103
11. РАСЧЕТ СМЕТНОЙ СТОИМОСТИ РАЗРАБОТКИ ................................ 110
ЗАКЛЮЧЕНИЕ ................................................................................................... 115
СПИСОК ЛИТЕРАТУРЫ................................................................................... 116
ПРИЛОЖЕНИЕ 1 ................................................................................................ 118
ПРИЛОЖЕНИЕ 2 ................................................................................................ 130
ПРИЛОЖЕНИЕ 3 ................................................................................................ 162
13
ВВЕДЕНИЕ
Под термином планирование понимается оптимальное распределение
ресурсов, необходимых для достижения поставленных целей, или некая деятельность, состоящая из совокупности процессов, которая связана с постановкой целей (задач) и действий в будущем.
При рассмотрении большинства теоретических, практических или даже
бытовых задач мы видим, что эти задачи не могут быть решены за несколько
тривиальных шагов. Основными причинами этого являются использование
нескольких параметров, установление большое количество всевозможных
условий и ограничений, недостаточно полная информация о предметной области. Поиск решения человеком занимает большое количество времени, за
счет анализа большого количества информации. И скорее сего человек не
найдет оптимального решения.
Наиболее распространенным способом решения таких задач как раз и
является построение плана, представляющего собой упорядоченный набор
несложных действий, при выполнении которых достигается поставленная
цель. Намного удобнее данную задачу дать вычислительной машине, по
крайней мере, время поиска будет значительно меньшим. В завершении работы машины человеку может быть предоставлен оптимальный план, либо
несколько вариантов исполнения, из которых человек может выбрать.
Вопросы, связанные с теорией планирования с точки зрения теории
решения задач, занимают одно из основных мест в области искусственного
интеллекта. Автоматический (с помощью вычислительной машины) поиск
плана называют интеллектуальным планированием. Поскольку при помощи планирования повышается самостоятельность и гибкость интеллектуальной системы за счет составления последовательности действий для достижения заданных целей. Эта область искусственного интеллекта исследуется около четырех десятилетий.
14
Методы интеллектуального планирования нашли применение в различных задачах, включая робототехнику. Для роботов была поставлена задача, научиться реагировать на внешний мир. Предполагается, что робот умеет
выполнять некие элементарные действия. Он пытается спланировать свои
действия таким образом, чтобы выполнить поставленную перед ним цель.
Но разработка программы, которая бы составляла план действий для
робота, достаточно сложна. Поскольку путей достижения заданной цели, как
правило, много, и необходимо найти по возможности самый оптимальный
план. Поиск решения занимает долгое время, так как подразумевается полный перебор. Но очевидно, что это совсем не рационально, поэтому разрабатываются сложные эвристики, которые позволяют сократить область поиска
до разумных размеров.
На основе исследований в области поиска в пространстве состояний и
автоматического доказательства теорем была создана первая система интеллектуального планирования STRIPS (Stanford Research Institute Problem Solver).
Но обычно система STRIPS пишется на языках сверхвысокого уровня,
где описывается лишь принцип «что нужно сделать». Например, на декларативном языке программирования Пролог, который описывает саму задачу в
совершенно абстрактных логических терминах, то есть создает модель анализируемой проблемы и пытаться получить положительные или отрицательные результаты этого анализа. А в США, традиционно, более распространен
другой язык искусственного интеллекта — язык функционального программирования Lisp. Но программы, написанные на этих языках, работают гораздо медленнее, в отличие от программ императивного языка высокого уровня
С++, и пригодны для работы лишь на бортовых системах управления.
15
Целью работы является создание комплекса методов и программных
средств интеллектуального планирования семейства STRIPS, используя
стиль объектно-ориентированного программирования языка высокого уровня
С++.
В дипломной работе решаются следующие задачи:
 обзор систем интеллектуального планирования;
 исследование методов интеллектуального планирования на основе
алгоритмов семейства STRIPS;
 обзор реализации планировщика на языке логического программирования;
 разработка алгоритмического и программного обеспечения системы
интеллектуального планирования на объектно-ориентированном языке программирования;
 экспериментальные исследования на примере решения ряда характерных задач.
16
1. ИСТОРИЯ ИСКУССТВЕННОГО ИНТЕЛЛЕКТА
История искусственного интеллекта началась в древности с мифов.
Классические ученые также говорили про искусственный интеллект, они пытались описать процесс человеческого мышления как механическую манипуляцию символами. Первой работой, можно считать, программируемый цифровой компьютер, изобретенный в 1940 году.
Многие ранние программы искусственного интеллекта использовали
основной алгоритм поиска. Для достижения некоторых целей (например, победа в игре или доказательство теоремы), они выполняли шаг за шагом (делая ходы вперед или назад), словно в поиске по лабиринту мы отступаем, когда заходим в тупик. Эта парадигма была названа «анализ средств и целей».
Основная трудность заключалась в том, что для многих задач, число
возможных путей хождения по лабиринту было просто колоссальным (эта
ситуация известна как «комбинаторный взрыв»). Исследователи сокращали
пространство поиска при помощи эвристики или грубого эмпирического метода, что позволило устранить те пути, которые вряд ли приведут к решению.
Алан Ньюэл и Герберт Саймон создали систему для решения задач под
названием «Универсальный решатель задач» (General Problem Solver —
GPS). Другим поисковым программам удалось достичь впечатляющих результатов, таких как решение проблем в геометрии и алгебре. Это были разработки Герберта Гелернтера «Геометрия доказательства теорем» (1958) и
SAINT, написанная студентом Джеймсом Слагленом под покровительством
американского ученого Марвином Ли Мински (1961). Другие программы искали решение через цели и подцели с построением плана действий. Например, программа STRIPS, разработанная в Стэнфордском Университете, чтобы контролировать поведение робота Шейки.
В конце 60-х годов Марвин Ли Минский и Сеймур Пейперт из лаборатории Искусственного Интеллекта Массачусетского технологического ин17
ститута (англ. Massachusetts Institute of Technology, MIT) предложили провести исследования, направленные на создание простых ситуаций - микромиров. Они отметили, что в успешных науках таких, как физика, основные
принципы чаще всего лучше понимать с использованием упрощенных моделей, например, шарнир без трения и абсолютно твердые тела. Большая часть
исследований фокусировалась на «мире блоков», который состоял из цветных блоков различной формы и размеров, выстроенных на плоской поверхности (Рис.1.1).
Рис.1.1 – «Мир блоков»
В начале семидесятых годов, возможности программы искусственного
интеллекта были ограничены. Даже самые впечатляющие могли обрабатывать только тривиальные проблемы, которые они, как предполагалось, решали, все программы были, в некотором смысле, "игрушки". Исследователи искусственного интеллекта начали работать с рядом фундаментальных ограничений, которые не могли быть преодолены в 1970-х годах. Хотя некоторые из
этих ограничений были решены в последующие десятилетия, но другие загоняют в тупик и по сей день.
Прогресс в области искусственного интеллекта продолжается. Проблемы, которые казались невозможными, были решены в 1970 году и в настоящее время активно используются в успешных коммерческих продуктах. [Википедия, История искусственного интеллекта]
Более подробно в Приложении 1.
18
2. АВТОМАТИЧЕСКОЕ ПЛАНИРОВАНИЕ И
ДИСПЕТЧЕРИЗАЦИЯ
2.1. Обзор
Автоматическое планирование и диспетчеризация (англ. Automated
planning and scheduling, APS) – отраслью искусственного интеллекта, которая
касается реализации стратегии или последовательности действий, как правило, для интеллектуальных агентов, автономных роботов и беспилотных аппаратов. В отличие от классических проблем контроля и классификации, решения этой задачи являются сложными и должны быть обнаружены и оптимизированы в многомерном пространстве. Планирование также связано с теорией решений.
С известными параметрами среды и с доступными моделями, планирование может быть сделано в автономном режиме, еще на этапе проектирования. Решения могут быть найдены и оценены до выполнения. При неизвестных параметрах среды, стратегии часто нуждается в пересмотре на этапе реализации плана. Модели и шаблоны поведения должны быть адаптированы.
Решения обычно находятся из процесса проб и ошибок, которые часто применяются в искусственном интеллекте, так как он является врожденным методом мышления человека. Они включают в себя динамическое программирование, обучение с подкреплением и комбинаторная оптимизация. Языки,
используемые для описания систем планирования и диспетчеризации, часто
называют языками действий.
С учетом описания возможных начальных состояний мира, описание
желаемых целей, и описание множества возможных действий, задача планирования состоит в том, чтобы найти план, который гарантирует (для любого
начального состояния) для составления последовательности действий, приводящих к одному из целевых состояний.
19
Трудность планирования происходит от использования упрощающих
предположений. Проблемы в некоторых классах планирования возникают изза зависимости от свойств, которые имеют проблемы в других средах.
 Действия являются детерминированными или недетерминированными? Для недетерминированных действий, являются ли доступными связные
вероятности?
 Переменные состояния являются дискретными или непрерывными?
Если они являются дискретными, то имеют ли они конечное число возможных значений?
 Может текущее состояние наблюдается однозначно? Ведь могут быть
полные или частичные наблюдаемости.
 Сколько начальных состояний может быть, одно или множество?
 Есть ли продолжительность у действий?
 Могут ли несколько действий выполняться одновременно, или только
одно действие может выполняться за один раз?
 Целью плана является достижение поставленного целевого состояния
или максимизация функции вознаграждения?
 Есть только один агент или несколько агентов? Агентами являются
групповыми или одиночными? Агенты строят свои собственные планы по
отдельности, или планы построены централизованно для всех агентов?
Простейшие задачи планирования, известные как классические задачи планирования, определяются:
 уникальным начальным состоянием,
 малыми по продолжительности действиями,
 детерминированными действиями, которые могут быть выполнены
только по одному разу,
 и одним агентом.
Так как начальное состояние задается однозначно, и все действия детерминированы, состояние мира после любой последовательности действий
20
можно точно предсказать, и поэтому наблюдаемость не имеет отношения к
классическому планированию.
Кроме того, план может быть определен как последовательность действий, потому что он всегда заранее знает, какие действия будут ему необходимы.
При недетерминированных действиях или других событиях вне управления агента, формируется дерево, и план должен определить соответствующие действия для каждого узла дерева.
Марковские процессы принятия решений с дискретным временем
определяются:
 малыми по продолжительности действиями,
 недетерминированными вероятностными действиями,
 полной наблюдаемостью,
 максимизацией функции вознаграждения,
 и одним агентом.
Когда полной наблюдаемость заменяется частичной наблюдаемостью,
планирование основывается на частично наблюдаемых Марковских процессах принятия решения (Partially observable Markov decision process, POMDP).
Если имеется больше одного агента, то планирование мультиагентной
системы планирования становится связанным с теорией игр. [Википедия, Автоматическое планирование и диспетчеризация]
21
2.2. Языки планирования
Наиболее часто используемые языки для решения задач планирования, такие как STRIPS и PDDL, для классического планирования, основаны
на переменных состояния. Каждому возможному состоянию мира присваивается значение переменной состояния и действия, которые определяют, как
изменится значения переменных состояния, когда действие будет выполнено.
Так как множество переменных состояния вызывает в свою очередь множество состояний, то происходит увеличение количества состояний экспоненциальной форме. Поэтому планирование, как и многие другие вычислительные задачи, страдает от проклятия размерности и комбинаторного взрыва.
Альтернативным задачи планирования языком является планирование
иерархической сети задач, в которой дан набор задач, и каждая задача может быть реализована примитивным действием или разложена на множество
других задач. Здесь не обязательно включаются переменные состояния, хотя
в более реалистичных приложениях переменные состояния упрощают описание сети задач.
Целью планирования, основанного на отношениях предпочтения,
является не только составление плана, но и удовлетворение указанных пользователем предпочтений. В отличие от планирования, основанного на вознаграждении, например, Марковский процесс принятия решений, планирование с предпочтениями не обязательно должно иметь точное численное значение. [Википедия, Автоматическое планирование и диспетчеризация]
22
2.3. Алгоритмы планирования
Классическое планирование.
 поиск в пространстве состояний прямой цепочки рассуждений, возможно применение эвристики для сокращения поиска,
 поиск обратного построения цепочки, оптимизация за счет применения граничных состояний (например, STRIPS, Graphplan),
 планирование с частичным упорядочиванием действий,
 мир является полностью наблюдаемым, то есть в модели есть все необходимые знания о мире,
 мир является статичным, то есть он изменяется только при применении плана,
 действия являются дискретными, то есть они не имеют длительности
и происходят мгновенно,
 в планировании не допускается параллельное выполнение действий.
Сведение к другим задачам.
 сведение к задачам выполнимости булевых формул (Satplan),
 сведение до проверки модели. Задача пересечения пространства состояний и классическая задача планирования соответствуют подклассу задач
проверки модели.
Временное планирование.
Временное планирование может быть решено с помощью методов,
аналогичных классическому планированию. Основное отличие в том, что
действия могут выполняться одновременно, и поэтому состояние должно
включать в себя еще информацию о текущем времени и времени выполнения
каждого действия. Кроме того, в планировании в режиме реального времени,
пространство состояний может быть бесконечным, в отличие от классическо23
го планирования. Временное планирование может быть рассмотрено с точки
зрения временных автоматов.
Вероятностное планирование.
Вероятностное планирование может быть решено с помощью итерационных методов, таких как итерация значений и итерация по стратегиям, когда пространство состояний достаточно мало. При частичной наблюдаемости, вероятностное планирование так же решается итерационными методами,
но с использованием значений функций, определяемых для пространства
представлений вместо состояний. [Википедия, Автоматическое планирование и диспетчеризация]
Примером вероятностного планирования может быть Марковский процесс принятий решений (англ. Markov decision process (MDP)) (Рис.2.1).
Рис.2.1 – Пример MDP с 3 состояниями и 2 действиями
24
3. АНАЛИЗ МЕТОДОВ ИНТЕЛЛЕКТУАЛЬНОГО
ПЛАНИРОВАНИЯ
Зарождение интеллектуального планирования произошло на основе исследований автоматического доказательства теорем и области поиска в пространстве состояний. Именно система STRIPS была первой, кто основывался
на этих двух методологиях. Как было сказано ранее, STRIPS была создана
для робота Шеки. В данной системе поиск решения базируется на методологии поиска системы GPS, а система QA3 использовалась для проверки истинности предусловий истинности в текущем состоянии.
Поэтому перед тем как перейти к системе SRIPS, рассмотрим эти две
систем, ознакомимся с их методологиями, которые лежат в ее основе.
3.1. Обзор системы GPS
Универсальный решатель задач (англ. General Problem Solver, GPS)
компьютерная программа, созданная в 1959 году Гербертом Саймоном, Дж.
Шоу и Алленом Ньюэллом. Это универсальная машина, которая решает задачи. Любую символически формализованную задачу можно решить с помощью GPS. Например: доказательство теоремы, геометрические задачи и
игра в шахматы. Она была основана в качестве теоретической работы Саймона и Ньюэлла по логике машин. GPS была первая компьютерная программа, которая разделила свои знания о задачах (правила которой были представлены в виде входных данных) от своей стратегии решения задач (общий
решающий поисковый двигатель). Это был реализовано на низкоуровневом
языке программирования IPL.
В то время как GPS решала простые задачи, такие как Башни Ханоя,
которые могли быть достаточно формализованы (не знала информацию о
точном виде задачи), она не могла решить задачи реального мира, потому что
25
в результате поиска мог произойти комбинаторный взрыв из-за промежуточных состояний.
Поиск в системе GPS осуществляется следующим способом: система
сама подбирает путь, по которому необходимо осуществлять поиск. То есть
она пыталась найти такое решение, которое было расположено в наиболее
перспективных ветвях поиска. Этот метод был названо «анализом средств и
целей». Ее сущность заключалась в том, что изначально искалась разница
между текущим объектом и тем объектом, который мы хотим получить. Эта
разница была сопоставима некоему классу различий. Для каждого класса
приходился собственный набор действий, которые уменьшали различие
между целевыми и текущими объектами.
GPS на каждом из шагов ищет различие между объектами и выбирает
тот оператор, который релевантен, а затем применяет его к текущему объекту, таким образом, поиск осуществлялся в глубину до тех пор, пока операторы были применимы и ветвь поиска была перспективна. Иначе выполнялся
откат. Основным моментом является то, что всегда выбирается релевантный
оператор, который уменьшает различие, несмотря на то, что он даже может
быть и не применим. Получается, что система преобразовывала объект от неприменимого к пригодному для применения. Из-за этого образовывались рекурсии.
Итак, в качестве объекта была модель мира, оператором было действие,
но планирование, как задача, перед GPS не ставилась. А вот система STRIPS
как раз ставила перед собой задачу планирования. В системе GPS использовались методы, примененные позднее в системе STRIPS по аналогии.
В GPS по определенной методике выделяются некоторые П-правила
исходя из заданных описаний состояния S и цели G. Сначала делается попытка вычислить различие между S и G. Вычисление различий проводится
посредством функции, которая специально пишется для каждой области
применения.
26
Различия использовались для отбора «относящихся к делу» П-правил
путем обращения к некоторой «таблице различий», в которой эти П-правила
связаны с различиями. С данным различием ассоциировались те правила, которые «имеют отношение к уменьшению этого различия». П-правила, связанные с каждым различием, упорядочивались между собой в соответствии
со степенью уместности правила. Для каждой области применения должна
быть построена таблица различий. Когда принималось решение, что некоторое П-правило может быть выбрано как относящееся к делу, то система GPS
начинала рекурсивно работать с предусловиями этого правила. Когда они все
оказывались удовлетворенными, то П-правило применялось к текущему описанию состояния, и процесс продолжался.
Таким образом, мы видим, что рекурсивная процедура GPS очень похожа (и носит, может быть, несколько более общий характер) на рекурсивную процедуру STRIPS. Ведь исторически система STRIPS создавалась под
влиянием GPS. Программа для рекурсивной процедуры GPS могла бы выглядеть примерно так.
Сначала устанавливаем исходное описание состояния в качестве значения глобальной переменной S. Программа вызывается с аргументом G, т.е.
той целью, которую GPS пытается достичь.
Recursive Procedure GPS(G)
while S соответствует G do //основной цикл процедуры
//GPS является итеративным
{
d = различие между S и G
//точка возврата
f = П-прав.,относ. к уменьш.d //др. точка возврата
p = формула предусловия соответ. частного случая f
GPS(p)
//рекурсив. обращение
S = рез-т применения к S соответ. частного случая f
}
27
Процесс идентификации различий и выбора П-правил для их уменьшения называется анализом «цели-средства» (Рис.3.1). Рекурсивная процедура
STRIPS может рассматриваться как частный случай GPS, в котором различиями между S и G являются компоненты С, не соответствующие S, и в котором все П-правила, в списках дополнений которых содержится литерал L,
рассматриваются как имеющие отношение к уменьшению различия L.
Процесс постановки целей
Анализ
«цель-средство»
Анализ целей
Чего я хочу?
Желаемые цели
Ситуационный анализ
Формулировка целей
(планирование целей)
Что я могу?
Сильные и слабые
стороны
Практические цели
(цели действия)
развиваться
избавляться
Рис.3.1 – Пример анализа «цель-средство»
Хотя первоначально GPS работала рекурсивно, как только что было
описано, можно было бы легко себе представить систему GPS в режиме
управления поиском на графе, подобному режиму, описанному для системы
STRIPS. [Википедия, Универсальный решатель задач]
28
3.2. Обзор системы QA3
Вопросно-ответная система (QA-система; от англ. QA — англ. Question-answering system, QA3 – version 3) – информационная система, которая
может автоматически отвечать на вопросы, задаваемые людьми на естественном языке. Интерфейс системы – естественно-языковой.
Система дает ответы, запрашивая структурированную базу данных,
знаний или информации, как правило, базу знаний. Но обычно система получает ответы из неструктурированных документов естественного языка
(Рис.3.2).
Рис.3.2 – Пример структуры вопросно-ответного представления задачи
Примеры того, что включают в себя документы естественного языка, используемые для системы:
29
 набор справочных текстов,
 внутренние организационные документы и веб-страницы,
 скомпилированные новостные ленты,
 набор страниц Википедии,
 подмножество страниц Всемирной Сети (WWW - World Wide Web).
Исследования QA работают с широким диапазоном типов вопросов, в
том числе: факты, списки, определения, как, почему, гипотетические, семантически ограниченные и кросс-языковые вопросы.
Вопросно-ответные системы можно разделить на:
 Узкоспециализированные QA-системы, отвечают на вопросы, рассматриваемые в конкретных областях (например, медицинское или автомобильное обслуживания) или рассматриваемые как более легкие задачи, потому что NLP-системы (от англ. Natural Language Processing - Обработка естественного языка) могут использовать предметно-ориентированные знания
часто формализованные в онтологии. Кроме того, к узкоспециализированным
областям могут относиться к ситуации, где принимаются только ограниченные типы вопросов, например, вопросы для описательной, а не процедурной
информации.
 Общие QA-системы, которые анализируют почти все вопросы, и могут работать с общими онтологиями и знаниями о мире. С другой стороны, у
системы, намного больше данных, из которых можно извлечь ответ, так как
можно искать в смежных областях.
Производительность QA-системы зависит от хорошего поискового механизма, так как без документов, содержащих ответы на вопросы, системы
мало чего сможет найти. Таким образом, чем база больше, тем точнее система работает, если там есть необходимая информация. Понятие избыточности
данных в хранилищах, таких как Интернет, информация, может быть сформулирована по-разному в разных контекстах и документах. Здесь есть два
преимущества:
30
 при наличии нужной информации в разных формах, нагрузка на систему обеспечения качества для выполнения сложных техник NLP для понимания текста уменьшается, поэтому система быстрее находит верный ответ;
 правильные ответы могут быть отфильтрованы от ложных, поскольку
правильный ответ встречается чаще в документах, чем ошибочный.
Ответы на вопросы в значительной степени опираются на рассуждения,
и есть много вопросно-ответных систем, разработанных на Прологе.
Хоть и система QA3 была вопросно-ответной, но и еще она обладала
механизмами для решения задач. А именно, данная система имела возможность давать ответы на вопросы следующего вида: «Есть ли такая последовательность действий, при выполнении которой объект obj1 может оказаться в
комнате room3, если изначально он присутствует в комнате room1?». Вопрос,
сформулированный таким образом, является постановкой задачи планирования. Но все-таки целью системы QA3 являлась выдача ответа на вопрос, а не
решение поставленной задачи, причем было большое количество ограничений для выполнения механизма решения задач, поэтому данная система е
была первым планировщиком.
Система отвечает на вопросы, которые получились на основе неких
факторов о мире, выражающихся в форме предложений логики исчисления
предикатов 1-го порядка. Вопросы формулируются в виде утверждений, которые нужно доказать. Предполагается, что в каждый момент времени мир
пребывает в некоем состоянии. Обозначим состояния буквой s с индексом.
Эти состояния описываются с помощью предикатов. К примеру, пусть s1 –
состояние, при котором объект obj1 располагается в комнате room3, тогда
предикат AT(obj1,room3,s1) – истинен. Состояние определяются множеством
предикатов. Пусть описанный выше предикат будет аксиомой A1.
A1 : AT (obj1 , room3 , s1 )
Действия и их последовательности изменяют состояния. Также действие можно представить как функцию, при выполнении которой мир пере31
ходит в новое состояние. Аксиомы, которые описывают эффект действия,
имеют такую форму:
(s) P(s)  Q  f (s) 
где s – переменная состояния;
Р – предикат состояния до применения действия;
Q – предикат состояния после применения действия;
f – функция действия, отражающая некое состояние в новое состояний.
Например, рассмотрим аксиому, которая описывает возможность перемещения предмета из комнаты room3 в комнату room1.
А2 :  AT (obj1 , room3 , s)  AT  obj1 , room1 , transfer (obj1 , room3 , room1 , s )  
где transfer (obj1 , room3 , room1 , s) - функция перемещения предмета obj1 из
room3 в room1.
И рассмотрим вопрос: «Есть ли такая последовательность действий,
при выполнении которой объект obj1 может оказаться в комнате room3, если
изначально он присутствует в комнате room1?». Этот же вопрос можно выразить иначе: «Есть ли такое состояние, которое может быть получено при
применении действий-функции, что obj1 может оказаться в комнате room1?».
Или можно записать в виде утверждения логики 1-го порядка:
( s) AT (obj1 , room1 , s )
И доказать его можно на основе аксиом А1 и А2 с помощью метода резолюций. И в виде ответа будет подстановка:
s  transfer (obj1 , room3 , room1 , s1 )
Последовательность действий можно выразить в виде композиции
функций. При этом естественный порядок вычисления функций, начиная от
вложенных к внешним, определяет последовательность, а сами функции
определяют действия. [Википедия, Вопросно-ответная система]
32
3.3. Обзор системы STRIPS
В искусственном интеллекте, STRIPS (англ. STanford Research Institute
Problem Solver) является автоматическим планировщиком, который был разработан Ричардом Файксом и Нильсом Нильсоном в 1971 году в SRI International. Это же название позже использовалось для обозначения формального
языка, который описывается входные данные этого планировщика. Этот язык
- основа для большинства языков, которые описывают задачи автоматического планирования, такие языки, широко известны как языки действия. [Википедия, STRIPS]
Файкс и Нильсон определили задачу планирования, и такой же она является до сих пор. Изначально она была написана на ЛИСПе для ЭВМ PDP10 и была использована в работе над проектом робота Стэнфордского исследовательского института (SRI). Также популярным языком символьной обработки, является Пролог, на котором также была написана система планирования. [Братко, 1990]
Система STRIPS также относится к классу решателей задач. Она представляет собой программу, которая осуществляет поиск в пространстве «моделей среды» для отыскания таковой модели, в которой достигается установленная цель. Предполагается, что для любой модели существует большое количество операторов, любой из которых преобразует модель среды в некую
другую модель.
Задачей планировщика является, найти такую последовательность действий, которая преобразует начальное состояние в такое состояние, в котором будет достигнуто заранее заданное целевое условие. Состояние выглядит
как множество формул логики 1-го порядка. Цель также описывается формулой. А действия описываются в виде специальных конструкций.
STRIPS использует методологию доказательства теорем исключительно в рамках одного заданного состояния для того, чтобы получить ответ на
33
вопросы "какие действия можно применить" и "достигнуты ли целевые условия" (в отличие от вопросно-ответной система QA3, в которой методы доказательства теорем применялись и для перехода из состояния в состояние).
Поиск в пространстве состояний в STRIPS исполняется по аналогии с системой GPS, а точнее, используется стратегия "анализ средств и целей". [Трофимов, 2005]
В STRIPS постановка задачи состоит из 3 составляющих:
 начального состояния,
 множества действий,
 целевой формулы.
Работа STRIPS начинается с попытки доказать, что цель получается из
множества формул, которые описывают начальное состояние. Задача считается решенной, если цель следует из начального состояния. Иначе незавершенное доказательство теоремы (то, что не удалось доказать пока) рассматривается как различие между целью и состоянием. Потом выполняется поиск
схемы действия, результат которой добавляет (либо удаляет) такую формулу,
что доказательство будет продолжено. Тут может понадобиться частичная
или полная конкретизация формулы результата. Схем действий, которые
подходят, может быть несколько, потому будут формироваться ветви поиска,
в каждой из которых прорабатывается один способ. После выбора схемы
действия, ее предусловие (которое может быть частично конкретизировано)
рассматривается в качестве новой подцели. Чтобы доказать данное предусловие выполняется конкретизация предусловия. То есть процесс доказательства
ищет такие подстановки для параметров формулы, которые сделают данную
формулу истинной в текущем состоянии. В случае если такая подстановка
находится, то схема действия конкретизируется и действие применяется,
иначе в ходе доказательства вновь формируется различие и процесс повторяется.
34
Иерархия целей, подцелей и состояний, которая была получена в ходе
поиска, называется деревом поиска (search tree). Как писалось выше, ветвление обусловлено тем, что различие между состоянием и целью можно
уменьшить несколькими способами. Каждый узел дерева поиска представляет собой пару (список целей, состояние). Практически в узле описывается
стек целей (подцелей) и текущее состояние, которые необходимо достичь.
Каждый раз, когда STRIPS порождает еще один узел, он проводит проверку
того, достигнута ли первая цель списка целей нового узла в состоянии, означенном в новом узле. Если да, и цель – это предусловие некоторого действия,
то это действие применяется (предусловие — это одна формула), после этого
создается новый узел с новым состоянием и тем же самым списком целей, в
котором уже нет первой цели (поскольку она была достигнута). Но если же
первая цель не достигнута в состоянии, означенном в узле, то на основании
различия идет поиск подходящего действия и формируется новый узел путем
добавления предусловия данного действия. STRIPS использует эвристику для
выбора того, какой узел выбирать следующим. Эвристическая функция производит оценку таких факторов, как количество оставшихся целей в списке
целей и количество и виды предикатов в целевых формулах.
Так как описание модели мира может быть довольно большим в задаче
с роботом, то не совсем выигрышно хранить копии модели мира в каждом
узле по следующим причинам. Во-первых, одинаковая модель мира может
быть использована в описании нескольких узлов. Во-вторых, в описании модели мира большинство утверждений остаются неизменными. Только небольшое их число изменяется в ответ на воздействие робота. Вследствие этого было предложено следующее решение по организации памяти. Хранить
все формулы всех моделей в единой структуре памяти. А в узлах же хранить
только флаги, указывающие на то, какие формулы стали истинны, а какие
ложны по сравнению с начальным состоянием. [Файкс, Нильсон , 1973] [Люгер , 2003]
35
После этого STRIPS подвергся обширной критике и за подход к описанию мира и действий, и за алгоритм поиска. Но многие идеи и решения, которые были предложены Файксом и Нильсоном, и по сей день остаются основополагающими в планировании.
3.4. Вывод по обзору систем
Итак, исследования в области планирования были начаты на основе доказательства теорем (GPS и QA3), однако они имели ряд недостатков.
Наиболее важными из них являлись:
1) проблема фрейма (QA3),
2) крайне низкая производительность.
Учитывая данные недостатки, был создан подход, который был основан на поиске в пространстве состояний. И как раз система STRIPS решает
проблемы фрейма за счет отделения процесса доказательства теорем от процесса поиска в пространстве моделей среды. Такое разделение позволило использовать в системе разные стратегии и тем самым значительно улучшить
показатели производительности системы.
STRIPS использует при поиске в пространстве моделей сочетание стратегий «целей и средств» с формальным методом доказательства теорем, что
позволяет рассматривать объекты (модели среды), гораздо более сложные и
общие, чем те, которые доступны в GPS, и дает возможность применять более эффективные эвристики поиска по сравнению с теми, что используются
при доказательстве теорем.
Итак, планирование – это основа моделирования целенаправленного
поведения. Поэтому поиск новых идей, позволивших бы увеличить скорость
синтеза планов, сохранит высокую степень актуальности в ближайшем будущем.
36
4. СИСТЕМА ПОСТРОЕНИЯ ПЛАНОВ
4.1. Решение задач роботом
Написание программы для робота связано с объединением многих
функций, включая восприятие окружающего мира, формулирование планов
действий и контроль за их выполнением. Здесь будет сосредоточено основное внимание на проблеме синтеза последовательности действий робота, ведущей (при ее реализации) к достижению некоторой заранее поставленной
задачи исходя из данной начальной ситуации.
4.1.1. Описания состояний и цели
Описание состояний и целей, стоящих перед роботом, могут конструироваться из правильно построенных формул исчисления предикатов. В качестве примера можно рассмотреть механическую руку робота и совокупность
кубиков, изображенную на рис. 4.1. [Нильсон, 1985]
Рука робота
CLEAR (B)
CLEAR (C)
В
А
С
ON (C, A)
ONTABLE (A)
ONTABLE (B)
HANDEMPTY
Рис 4.1 - Расположение кубиков
Для описания состояния мира кубиков используются предикаты:
- CLEAR — "не занят",
- ON - "находится на кубике",
- ONTABLE - "находится на столе".
37
Состояние руки робота описывается предикатами:
- HANDEMPTY - "рука пуста",
- HOLDING - "рука держит кубик".
Наконец, возможны следующие действия:
- pickup - "взять со стола",
- putdown - "поставить на стол",
- stack - "поставить на другой кубик",
- unstack - "снять с другого кубика".
4.1.2. Моделирование действий робота
Действия робота изменяют одно состояние (или конфигурацию) мира
на другое. Можно смоделировать эти действия посредством П-правил, которые изменяют одно описание состояния на другое.
Принятая в системе STRIPS форма П-правил содержит три компоненты.
Первой является формула предварительного условия, которое должно логически следовать из фактов в описании состояния для того, чтобы данное П-правило было приложимо к этому состоянию. Предполагаем, что такие
предварительные условия для П-правил составляют конъюнкцию литералов.
Второй компонентой П-правила является список литералов (содержащих, возможно, свободные переменные), называемый списком вычеркиваний. Когда некоторое П-правило применяется к описанию состояния, подстановка соответствия применяется к литералам списка вычеркиваний. Полученные при этом основные частные случаи вычеркиваются из описания
прежнего состояния как первый шаг на пути конструирования нового состояния.
Третьей компонентой является формула добавлений. Она состоит из
конъюнкции литералов (возможно, содержащих свободные переменные). Когда П-правило применяется к описанию состояния, то подстановка соответ38
ствия применяется к этой формуле добавлений и результирующий частный
случай для этого соответствия добавляется к прежнему описанию состояния
(после того, как вычеркнуты литералы списка вычеркиваний) в качестве заключительного шага построения нового описания состояния. [Нильсон, 1985]
Запишем П-правила в форме, отвечающей системе STRIPS, которые
соответствуют множеству действий робота, показанного на рис. 4.1.
R1: pickup (x) – поднять кубик со стола
C (R1): ontable (x) & clear (x) handempty
A (R1): holding (x)
D (R1): ontable (x), clear (x), handempty
R2: putdown (x) – опустить кубик на стол
C (R2): holding (x)
A (R2): ontable (x), clear (x), handempty
D (R2): holding (x)
R3: stack (x,y) – положить кубик на другой кубик
C(R3): holding (x) & clear (y)
A(R3): handempty, on (x,y), clear (x)
D(R3): holding (x), clear (y)
R4: unstack (x,y) – снять кубик с другого кубика
C(R4): handempty & on(x,y) & clear (x)
A(R4): holding (x), clear (y)
D(R4): handempty, on(x,y), clear (x)
В каждом из этих правил оказалось, что формула предусловия (выраженная в виде списка литералов) и список вычеркиваний совпадают.
Например, видно, что pickup (x) может быть применено к ситуации рис.
4.1, только если вместо x подставлено В. Новое описание состояния в этом
случае будет:
CLEAR (С), ON (С, А), ONTABLE (A), HOLDING (В).
39
4.1.3. Проблема фрейма
П-правило системы STRIPS (с короткими списками вычеркиваний и
добавлений) обращается с большинством правильно построенных формул в
описании состояния, как с «неизменным фоном».
Проблема определения того, какие ппф (допустимые выражения в исчислении предикатов называются правильно построенными формулами или
кратко ппф) описания состояния следует изменять, а какие нет, в области искусственного интеллекта обычно называется проблемой фрейма.
Обычно компоненты состояния мира достаточно независимы, что позволяет предполагать, что результаты действий носят относительно локальный характер. Когда такое предположение является оправданным, П-правила
системы STRIPS будут эффективными и адекватными моделями для многих
типов действий. Оно называется STRIPS-допущением. [Нильсон, 1985]
40
4.2. Прямой поиск в пространстве состояний
Планирование с использованием прямого поиска в пространстве состояний иногда называют прогрессивным планированием, так как оно предусматривает продвижение в прямом направлении. Изначально берется начальное состояние задачи и рассматривается последовательность действий до тех
пор, пока не найдется последовательность, которая позволяет достигнуть целевого состояния.
Задачи планирования выглядят следующим образом:
 Начальное состояние поиска - это начальное состояние, которое
взято из задачи планирования. Так, каждое состояние должно быть представлено множеством положительных базовых литералов; а литералы, которые не присутствуют в определении задачи, считаются ложными.
 Для любого действия, применимого в некоем состоянии, обязательно
выполняются предусловия. Полученное состояние, которое является результатом выполнения действия, создается при помощи добавления положительных литералов результата и удаления отрицательных литералов результата. Если использовался литерал 1-го порядка к литералам результата
необходимо применить унификатор из предусловий.
 При проверке цели осуществляется проверка того, чтобы данное состояние цели удовлетворяло условиям задачи планирования.
 Стоимость этапа для каждого действия обычно равна 1. В планировщиках STRIPS не используются различные стоимости для разных действий. [Рассел, Норвинг, 2006]
Простейшим типом системы, предназначенной для решения задач роботом, является система продукций, в которой описание состояния используется в качестве глобальной базы данных, а правила, моделирующие действия робота, — в качестве П-правила.
41
В такой системе П-правила из числа применимых используются до тех
пор, пока не будет построено описание состояния, которое сопоставимо с целевым выражением. Здесь предлагается рассмотреть следующий пример того, как такая система могла бы действовать.
Рассмотрим на примере. Пусть, целью является состояние, показанное
на рис. 4.2. Цель можно описать следующими предикатами:
ON (B, C)  ON (A, B)
Ведя рассуждение в прямом направлении от описания начального состояния, изображенного на рис. 4.1, видно, что единственными применимыми П-правилами являются pickup (В) и unstack (С, А).
А
В
С
ON (B,C)
ON (A,B)
Рис. 4.2 - Цель задачи для робота
На рис. 4.3 изображено полное пространство состояний для этой задачи, а путь решения показан жирными стрелками.
Описание начального состояния помечено символом SO, а состояние,
сопоставимое с целью, — символом G. (Отступив от стандартов с тем, чтобы
просто подчеркнуть присутствие симметрии в рассматриваемой задаче, вершина SO не обозначена как верхняя на рис. 4.3.) Можно заметить, что в этом
примере каждое П-правило имеет обратное правило.
42
putdown (B)
pickup (B)
CLEAR (A)
CLEAR (B)
CLEAR (C)
ONTABLE (A)
ONTABLE (B)
ONTABLE (C)
HANDEMPTY
pickup (C)
CLEAR (A)
CLEAR (C)
HOLDING (B)
ONTABLE (A)
ONTABLE (C)
stack (B,C)
stack (B,A)
unstack (B,C)
CLEAR (A)
ON (B,C)
CLEAR (B)
ONTABLE (A)
ONTABLE (C)
HANDEMPTY
pickup (A)
ON (B,C)
CLEAR (B)
HOLDING (A)
ONTABLE (C)
stack (A,B)
CLEAR (A)
ON (A,B)
ON (B,C)
ONTABLE (C)
HANDEMPTY
unstack (B,A)
stack (C,B)
putdown (C)
unstack (C,A)
stack (A,C)
stack (A,B)
unstack (A,C)
CLEAR (B)
ON (A,C)
CLEAR (A)
ONTABLE (B)
ONTABLE (C)
HANDEMPTY
putdown (B)
pickup (B)
ON (C,A)
CLEAR (C)
HOLDING (B)
ONTABLE (A)
stack (B,C)
unstack (A,C)
CLEAR (A)
ON (A,C)
ON (C,B)
ONTABLE (B)
HANDEMPTY
CLEAR (B)
CLEAR (C)
HOLDING (A)
ONTABLE (B)
ONTABLE (C)
CLEAR (B)
ON (C,A)
CLEAR (C)
ONTABLE (A)
ONTABLE (B)
HANDEMPTY
pickup (B)
putdown (A)
ON (C,B)
CLEAR (C)
HOLDING (A)
ONTABLE (B)
unstack (C,B)
stack (A,C)
CLEAR (C)
ON (C,B)
ON (B,A)
ONTABLE (A)
HANDEMPTY
stack (C,A)
unstack (C,B)
CLEAR (A)
ON (C,B)
CLEAR (C)
ONTABLE (A)
ONTABLE (B)
HANDEMPTY
putdown (C)
pickup (A)
ON (B,A)
CLEAR (B)
HOLDING (C)
ONTABLE (A)
stack (C,B)
unstack (A,B)
putdown (A)
CLEAR (A)
CLEAR (B)
HOLDING (C)
ONTABLE (A)
ONTABLE (B)
CLEAR (C)
ON (B,A)
CLEAR (B)
ONTABLE (A)
ONTABLE (C)
HANDEMPTY
pickup (C)
putdown (A)
pickup (A)
CLEAR (C)
ON (A,B)
CLEAR (A)
ONTABLE (B)
ONTABLE (C)
HANDEMPTY
pickup (C)
putdown (B)
ON (A,C)
CLEAR (A)
HOLDING (B)
ONTABLE (C)
CLEAR (B)
ON (B,A)
ON (A,C)
ONTABLE (C)
HANDEMPTY
putdown (C)
ON (A,B)
CLEAR (A)
HOLDING (C)
ONTABLE (B)
unstack (B,C)
stack (C,A)
stack (B,A) unstack (B,A)
CLEAR (B)
ON (B,C)
ON (C,A)
ONTABLE (A)
HANDEMPTY
unstack (A,B)
unstack (C,A)
CLEAR (C)
ON (C,A)
ON (A,B)
ONTABLE (B)
HANDEMPTY
Рис. 4.3 - Пространство состояний задачи робота
В этом очень простом примере (всего лишь 22 состояния во всем пространстве состояний) прямая система продукций, снабженная достаточно
простой системой управления, может быстро найти путь, ведущий к целевому состоянию.
В случае более сложных задач, однако, можно ожидать, что прямой поиск, ведущий к цели, породит довольно большой граф и такой поиск будет
реальным только при использовании оценочной функции, содержащей достаточную информацию. [Нильсон, 1985] [Белкин, Гахов, 2010]
43
4.3. Обратный поиск в пространстве состояний
Достоинством обратного поиска является то, что при помощи него
можно рассматривать лишь релевантные действия. Действие является релевантным конъюнктивной цели, если оно достигает одного из конъюнктов
данной цели. Кроме того существует много нерелевантных действий, которые способны привести к целевому состоянию.
Обратный поиск, допускающий нерелевантные действия, все еще является полным, но менее эффективным. Если решение есть, то оно может быть
найдено с помощью обратного поиска, который допускает только релевантные действия. Из-за этого ограничения, допускающего только релевантные
действия, процедура обратного поиска обладает более низким коэффициентом ветвления в сравнении с прямым поиском.
Поиск в обратном направлении иногда называют регрессивным планированием. Главным вопросом регрессивного планирования является: существуют ли такие состояния, из которых применение некоторого действия
приводит к цели? Вычисление описаний этих состояний называется регрессией цели через действие.
Состояния на основе обратного поиска получаются следующим образом:
 любые положительные результаты действия, появляющиеся в цели G,
удаляются;
 добавляется каждый литерал предусловия действия, при условии, что
он не присутствует еще в определении действия.
Для реализации обратного поиска могут быть использованы любые из
стандартных алгоритмов поиска. Работа завершается, когда вырабатывается
такое описания полученного состояния, которое идет в соответствие с
начальным состоянием задачи планирования. Если использовать логику 1-го
порядка, чтобы обеспечить соответствие с начальным состоянием, то может
44
понадобиться подстановка переменных в описание полученного состояния.
[Рассел, Норвинг, 2006]
Итак, система начинает с целевого описания (опять некоторой конъюнкции литералов) в качестве глобальной базы данных и применяет к этой
базе данных О-правила, создавая подцелевые описания. Она успешно заканчивает свою работу тогда, когда создает некоторое подцелевое описание, которому соответствуют факты в начальном описании состояния.
Первый шаг на пути разработки обратной системы продукций — определение множества О-правил, которые преобразуют целевые выражения в
подцелевые. Одним из подходов является использование О-правил, базирующихся на только что рассмотренных П-правилах; О-правило, которое преобразует цель G в подцель G', логически основывается на П-правиле, которое, будучи примененным к описанию состояния, соответствующего G', создает некоторое описание состояния, соответствующее G.
Применение П-правила к любому описанию состояния описание состояния, соответствующее литералам из его списка добавлений. Следовательно,
если целевое выражение содержит литерал L , который унифицируется с одним из литералов в списке добавлений для некоторого П-правила, ясно, что
если будет порождено описание состояния, находящегося в соответствии с
некоторыми частными случаями предусловий П-правила, то это правило может быть применено для создания описания состояния, соответствующего L.
Таким образом, подцелевое выражение, создаваемое обратным применением
П-правила, должно, безусловно, содержать частные случаи предусловий этого правила. Но если целевое выражение содержит другие литералы (кроме L),
то подцелевое выражение также должно содержать другие литералы, которые после применения нашего П-правила становятся этими другими литералами (т.е. отличными от L) в выражении цели. [Нильсон, 1985]
45
5. СИСТЕМА STRIPS
STRIPS была одной из первых систем решения задач роботом. В ней
поддерживается некоторый стек целей, и усилия по решению задач концентрируются на верхней цели стека. Вначале стек целей содержит только главную цель. Всякий раз, когда верхняя цель в стеке целей оказывается в соответствии с текущим описанием состояния, она вычеркивается из стека и подстановка соответствия применяется к выражениям, расположенным в стеке
под целью.
В противном случае если верхняя цель в стеке целей является составной, то система STRIPS добавляет в стек в некотором порядке каждый из литералов компонентной цели над составной целью. Смысл состоит в том, что
система работает с каждой составляющей целью в том порядке, в котором
они размещены в стеке.
Когда решены все составляющие цели, то система пересматривает составную цель, заново перечисляя компоненты, находящиеся на верху стека,
если составная цель не приводится в соответствие с текущим описанием состояния. Это повторное обращение к составной цели является (довольно
примитивным) средством, с помощью которого в системе предусматривается
случай возникновения взаимодействия целей. Если решение одной составляющей цели отменяет уже решенную компоненту, то эта "отмененная" цель
пересматривается и при необходимости решается заново.
Когда верхняя (нерешенная) цель в стеке является однолитеральной,
система ищет П-правило, которое содержит в списке добавлений литерал,
сопоставимый с нею. Затем эту однолитеральную цель на вершине стека заменяют на тот частный случай упомянутого П-правила, который возникает в
результате приведения в соответствие. Затем выше этого П-правила заносится частный случай формулы предусловия Р, отвечающий этому соответствию.
46
Если Ρ носит составной характер и не сопоставляется с текущим описанием состояния, то ее компоненты в некотором порядке заносятся на вершину стека. Когда верхний элемент стека представляет собой П-правило, то
это значит, что формула предусловия этого правила пришла в соответствие с
текущим описанием состояния и была удалена из стека. Таким образом, это
П-правило применимо. Оно применяется к текущему описанию состояния и
удаляется из верхней части стека.
Теперь вместо первоначального используется новое описание состояния, а система запоминает то П-правило, которое было применено с тем, чтобы в дальнейшем включить его в решающую последовательность.
Систему STRIPS можно рассматривать как систему продукций, в которой глобальная база данных представляет собой комбинацию текущего описания состояния и стека целей.
Операции, совершаемые над этой базой данных, изменяют либо описание состояния, либо стек целей, причем процесс продолжается до тех пор,
пока стек целей не опустеет. "Правилами" такой системы продукций являются правила, которые преобразуют одну глобальную базу данных в другую.
Их не следует путать с правилами системы STRIPS, которые соответствуют
моделям действий робота.
Высокоуровневые правила изменяют глобальную базу данных, состоящую как из описания состояния, так и из стека целей. Правила же системы
STRIPS указаны в глобальном стеке и используются для изменения описания
состояния.
Работа системы STRIPS при каком-то режиме управления поиском на
графе порождает некоторый граф глобальных баз данных, причем решение
соответствует пути в этом графе, ведущему от начала к терминальной вершине. (Терминальной является вершина, помеченная базой данных, имеющей пустой стек целей). [Нильсон, 1985]
47
ОПИСАНИЕ СОСТОЯНИЙ
CLEAR (B)
CLEAR (C)
ON (C,A)
ONTABLE (A)
C
ONTABLE (B)
А B
HANDEMPTY
СТЕК ЦЕЛЕЙ
ON (C,B) & ON (A,C)
ОПИСАНИЕ СОСТОЯНИЙ
CLEAR (B)
CLEAR (C)
ON (C,A)
ONTABLE (A)
C
ONTABLE (B)
А B
HANDEMPTY
СТЕК ЦЕЛЕЙ
ON (A,C)
ON (C,B)
ON (C,B) & ON (A,C)
ОПИСАНИЕ СОСТОЯНИЙ
CLEAR (B)
CLEAR (C)
ON (C,A)
ONTABLE (A)
C
ONTABLE (B)
А B
HANDEMPTY
СТЕК ЦЕЛЕЙ
ON (C,B)
ON (A,C)
ON (C,B) & ON (A,C)
ОПИСАНИЕ СОСТОЯНИЙ
CLEAR (B)
CLEAR (C)
ON (C,A)
ONTABLE (A)
C
ONTABLE (B)
А B
HANDEMPTY
СТЕК ЦЕЛЕЙ
CLEAR (C) & HOLDING (A)
stack (A,C)
ON (C,B)
ON (C,B) & ON (A,C)
ОПИСАНИЕ СОСТОЯНИЙ
CLEAR (B)
CLEAR (C)
ON (C,A)
ONTABLE (A)
C
ONTABLE (B)
А B
HANDEMPTY
СТЕК ЦЕЛЕЙ
CLEAR (B) & HOLDING (C)
stack (C,B)
ON (A,C)
ON (C,B) & ON (A,C)
ОПИСАНИЕ СОСТОЯНИЙ
CLEAR (B)
CLEAR (C)
ON (C,A)
ONTABLE (A)
C
ONTABLE (B)
А B
HANDEMPTY
СТЕК ЦЕЛЕЙ
HOLDING (C)
CLEAR (B)
CLEAR (B) & HOLDING (C)
stack (C,B)
ON (A,C)
ON (C,B) & ON (A,C)
ОПИСАНИЕ СОСТОЯНИЙ
CLEAR (B)
CLEAR (C)
ON (C,A)
ONTABLE (A)
ONTABLE (B)
HANDEMPTY
C
А B
СТЕК ЦЕЛЕЙ
HANDEMPTY & CLEAR (C)
& ON(C,y)
unstack (C,y)
CLEAR (B)
CLEAR (B) & HOLDING (C)
stack (C,B)
ON (A,C)
ON (C,B) & ON (A,C)
ОПИСАНИЕ СОСТОЯНИЙ
CLEAR (C)
CLEAR (A)
ON (C,B)
HANDEMPTY
В
ONTABLE (A)
А С
ONTABLE (B)
СТЕК ЦЕЛЕЙ
Бесперспективный путь решения
Бесперспективный путь решения
При подстановке {A/y} верхняя подцель сопоставляется с текущим описанием
состояния. Мы можем затем применить unstack (С,А). Теперь следующие две
цели также приводятся в соответствие, так что мы можем применить stack (C,B)
ОПИСАНИЕ СОСТОЯНИЙ
CLEAR (A)
CLEAR (C)
ON (C,B)
ONTABLE (A)
В
ONTABLE (B)
А С
HANDEMPTY
СТЕК ЦЕЛЕЙ
CLEAR (C) & HOLDING (A)
stack (A,C)
ON (C,B) & ON (A,C)
ОПИСАНИЕ СОСТОЯНИЙ
CLEAR (A)
CLEAR (C)
ON (C,B)
ONTABLE (A)
В
ONTABLE (B)
А С
HANDEMPTY
СТЕК ЦЕЛЕЙ
ONTABLE (A) & CLEAR (A)
& HANDEMPTY
pickup (A)
CLEAR (C) & HOLDING (A)
stack (C,B)
ON (C,B) & ON (A,C)
Теперь мы можем применить pickup (A), и затем следующая цель будет
приведена в соответствие, так что мы можем применить stack (A,C). Теперь
последняя оставшаяся цель в стеке оказывается приведенной в соответствие
ОПИСАНИЕ СОСТОЯНИЙ
ON (A,C)
ON (C,B)
А
HANDEMPTY
С
CLEAR (A)
В
ONTABLE (B)
СТЕК ЦЕЛЕЙ
NIL
Рис. 4.4 - Граф поиска, созданный системой STRIPS
48
Рассмотрим, как система STRIPS могла бы решать довольно простую
задачу складывания кубиков в пирамиду. Пусть целью является:
ON (С, В)
ON (A, С),
А начальное состояние, изображенное на рис. 4.1:
CLEAR (B)
CLEAR (C)
ON (C, A)
ONTABLE (A)
ONTABLE (B)
HANDEMPTY
Следует заметить, что цель может быть просто достигнута помещением
С на В и затем А на С.
На рис. 4.4 показана часть графа, который может быть построен в ходе
решения этой иллюстративной задачи.
Так как проблема является очень простой, то система довольно легко
находит решающую последовательность:
unstack (С, А)
stack (С, В)
pickup (A)
stack (А, С).
49
5.1 Общий вид алгоритма STRIPS
Итак, сведем информацию по работе системы STRIPS, разобранную
ранее, в некий алгоритм.
STRIPS
вход: R, S0, G
выход: plan
1 S = S0
2 пока G невыполнимо в s делать
2.1. выбрать компоненту g из G
2.2. выбрать правило RR такое, что gA(R)
2.3. STRIPS (R, s, C(R))
2.4. применить R к s
2.5. добавить R в plan
3 вернуть plan
Входными данными для системы STRIPS являются:
 R - множество правил, предполагается, что в множестве R все правила полностью конкретизированы.
 S0 - начальное состояние,
 G целевое состояние.
Выходными данными является:
 plan – перечень действий, при выполнении которых начальное состояние превратится в целевое.
Записываем начальное состояние в стек текущих состояний (п.1).
Помещаем главную цель G в стек целей (п.2).
Если цель является составной, то есть она состоит из конъюнкции литералов, то в стек целей записываются в некоем порядке каждый из литералов составной цели (подцели) (п.2.1).
50
Далее система ищет действие, содержащее в списке добавлений литерал, который сопоставим с этой целью (п.2.2).
Заходим рекурсивно в нашу функцию (п.2.3).
Если выбранное действие еще нельзя применить к текущему состоянию, то его предусловие помещается в стек целей, иначе действие применяется к текущему состоянию (п.2.4).
Выполненное действие помещается в план (plan) (п.2.5).
Если верхняя цель в стеке соответствует текущему состоянию, то она
удаляется из стека.
Алгоритм STRIPS завершается, когда стек целей пуст.
51
5.2. Неполнота алгоритма STRIPS
Есть такие задачи, для которых STRIPS или не может построить план,
или строит не минимальный план. Это происходит из-за того, что STRIPS достижение каждой компоненты составной цели происходит по отдельности, не
учитывая их взаимосвязи. Эта особенность предметной области, где цели являются взаимосвязанными (которые взаимодействуют) получила название
взаимосвязи целей.
Примечание. Впервые эта некорректность STRIPS'а в этом случае была найдена Аленом Брауном из Массачусетского Технологического института в 1973 году. Он попытался решить задачу, которая рассматривалась в этом
разделе на планировщике HACKER, который был создан Джеральдом Суссманом на основе планировщика STRIPS, именно поэтому эта задача была
названа аномалией Суссмана (Sussman Anomaly). [Википедия, STRIPS]
Рассмотрим аномалию Суссмана. Дано:
 Объекты, 3 кубика:
A,
B,
C.
 Начальное состояние S0 (рис.5.1):
CLEAR (B)
CLEAR (C)
ON (C, A)
ONTABLE (A)
ONTABLE (B)
HANDEMPTY
 Цель G (рис.5.2):
ON (A,B)
ON (B,C)}.
52
С
А
В
Рис.5.1 – Начальное состояние
А
В
С
Рис.5.2 – Целевое состояние
Так как цель G является составной, то STRIPS разделяет её на отдельные подцели – ON (A,B) и ON (B,C), которые помещаются в стек целей и достижение каждой происходит по очереди.
Предположим, что ON (A,B) находится на вершине стека, тогда планировщик сформирует следующую последовательность правил, которые надо
выполнить, чтобы достичь эту подцель:
unstack (C,A)
putdown (C)
pickup (A)
stack (A,B)
После применения этой последовательности действий к начальному
состоянию S0. Получается состояние, изображённое на рис.5.3, где ON (A,B)
- выполнима. Поэтому подцель ON (A,B) удаляется из стека целей.
53
А
С
В
Рис.5.3 - Удовлетворение первой цели
Далее, из состояния, изображенного на рис.5.3, начинается удовлетворяться следующая подцель в стеке – ON (B,C).
Получаем следующую последовательность действий:
unstack (C,A)
putdown (C)
pickup (A)
stack (A,B)
unstack (A,B)
putdown (A)
pickup (B)
stack (B,C)
После выполнения последних 4-х правил получаем состояние, изображенное на рис.5.4. Цель ON (B,C) удовлетворена и удаляется из стека. Однако цель ON (A,B), удовлетворённая на предыдущем этапе, перестаёт быть
выполнимой.
В
С
А
Рис.5.4 - Удовлетворение второй цели
54
Поэтому, дальше планировщик начинает выходить из состояния на
рис.5.4 и вновь начинает удовлетворять подцель ON (A,B). Что приводит к
добавлению ещё двух правил к плану действий.
Итак, получаем следующий план:
unstack (C,A)
putdown (C)
pickup (A)
stack (A,B)
unstack (A,B)
putdown (A)
pickup (B)
stack (B,C)
pickup (A)
stack (A,B)
После выполнения последних двух действий цель ON (A,B) & ON (B,C)
достигается. План построен.
Очевидно, что построенный план не оптимален, поскольку в ходе выполнения полученной последовательности действий объекты 2 раза находились в одинаковом состоянии. Это свидетельствует о том, что в нашем плане
есть лишние действия, которые надо удалить.
Итак, оптимизированный план будет выглядеть следующим образом:
unstack (C,A)
putdown (C)
pickup (B)
stack (B,C)
pickup (A)
stack (A,B)
55
5.3. Применение системы STRIPS для реального объекта
Программу, реализованную на основе системы семейства STRIPS можно использовать в качестве модуля планирования, который входит в состав
интеллектуальной системы управления роботом-манипулятором (рис.5.5).
Рис.5.5 – Комплекс управления роботом-манипулятором
Робот распознает кубики в пространстве, предоставляя программе
входные данные в соответствие с требованиями, и программа начинает выполняться.
Модуль планирования отвечает за создание плана действий, необходимых для достижения целевого состояния. На его вход поступает информация о местоположении всех объектов, находящихся в рабочей зоне из модуля
видео-распознавания. Также в модуль поступает информация от оператора
(или же от главной управляющей программы) – целевое положение (цель).
56
Получив всю необходимую информацию, модуль планирования начинает работу по поиску пути из начального состояния в целевое. Основу работы модуля планирования составляет анализ целей-средств с защитой и регрессией
целей для нахождения списка действий. В итоге планировщик составляет
стек действий и посылает требование выполнения очередного действия из
стека в модуль кинематики.
Робот материализует поставленную цель. [Киселев, 2011]
57
6. ОБЗОР РЕАЛИЗАЦИИ ПЛАНИРОВЩИКА НА ЯЗЫКЕ
ЛОГИЧЕСКОГО ПРОГРАММИРОВАНИЯ
В разделе 5.1 был описан общий алгоритм планирования на основе. А
теперь рассмотрим реализацию планировщика на языке Пролог.
Пролог (от англ. Programming in Logic - Программирование в логике) –
это язык логического программирования, связанный с искусственным интеллектом. Он основан на языке предикатов математической логики дизъюнктов
Хорна и представляет собой подмножество логики предикатов первого порядка.
Интерпретатор Пролога, выполняя программы, всегда реализует вывод
на основе логических спецификаций. Главное преимущество применения
этого языка для компьютерных наук вообще и искусственного интеллекта в
частности - использование возможностей представления теории предикатов
1-го порядка.
За счет применения теории предикатов 1-го порядка в языке программирования обеспечивается элегантный, прозрачный синтаксис и хорошо
определенная семантика.
Язык Пролог развивался благодаря исследованиям, связанным с доказательством теорем, а конкретнее, с разработкой алгоритмов опровержения
резолюции. В данной работе была разработана процедура доказательства, которая получила название резолюция, ставшая основным методом вычислений на языке Пролог.
Из-за этих свойств Пролог стал считаться полезным средством исследования, касающихся экспериментальных вопросов программирования.
Например, автоматическая генерация кода, разработка высокоуровневых
языков программирования и верификация программ.
Как и другие языки, основанные на логике, Пролог поддерживает декларативный стиль программирования, то есть конструирование про58
граммы в терминах высокоуровневого описания ограничений предметной
области задачи. В отличие от этого стиля программирования, процедурный
стиль предполагает написание программы с помощью последовательности
инструкций по выполнению алгоритма. А в логическом программировании
компьютеру показывается, "что есть истина", а не "как это сделать". Поэтому
программисты сосредоточиваются на решении задачи и создании спецификаций для предметной области, а не на деталях, которые описывают низкоуровневые алгоритмические инструкций вида "что делать далее".
В начале 1970-х годов во Франции была написана первая программа на
языке Пролог в рамках проекта по пониманию естественного языка. В 19751979 годах был основной этап развития языка Пролог, в это время на кафедре
искусственного интеллекта университета Эдинбурга Дэвид Уоррен и Фернандо Перейра отвечали за реализацию этого языка. Ими был создан первый
интерпретатор Пролога, он достаточно робастный для предъявления его всей
компьютерной общественности. Интерпретатор располагался на системе
DEC 10 и был способен работать как в режиме интерпретатора, так и в режиме компилятора.
Но Пролог, несмотря на неоднократные пессимистические прогнозы,
продолжает развиваться в разных странах и вбирает в себя новые технологии
и концепции. [Люггер, 2003] [Википедия, Пролог (язык программирования)]
Вернемся к нашему планировщику, представим состояния мира, включая начальное и целевое, в виде списков предикатов. Опишем начальное и
целевое состояния следующим образом (рис.6.1):
START = [handempty, ontable(b), ontable(c), on(a, b), clear(c), clear(a)]
GOAL = [handempty, ontable(a), ontable(b), on(c, b), clear(a), clear(c)]
59
А
В
С
С
А
В
Рис.6.1 - Начальное и целевое состояния для задачи из мира блоков
Переходы в этом "мире блоков" описываются при помощи списков добавления и вычеркивания. Предикаты MOVE зависят от трех аргументов:
1. имя предиката MOVE с его параметрами;
2. список предусловий, то есть список предикатов, которые должны
быть истинны в описании состояния мира, для того чтобы данное правило
перехода стало применимым к этому состоянию;
3. список добавления и вычеркивания или список предикатов, которые
добавляются к состоянию мира или удаляются из него, для того чтобы создать новое состояния в результате применения правила перехода.
Отметим, что здесь лучше использовать абстрактные типы данных и
операции над множествами, включая объединение, пересечение, разность и
т.д. Так как их удобно применять не только для управления предусловиями,
но и для добавления и удаления списков.
Четыре перехода в этом мире на Прологе можно описать так:
move (pickup(X) , [handempty, clear(X), on(X, Y) ] ,
[del(handempty), del(clear(X)), del(on(X,Y)),
add(clear(Y)), add(holding(X))])
move (pickup(X), [handempty, clear(X), ontable(X)],
[del(handempty), del(clear(X)), del(ontable(X)),
add(holding(X))])
60
move (putdown(X), [holding(X)],
[del(holding(X)),
add(ontable(X)), add(clear(X)), add(handempty)])
move (stack(X/ Y), [holding(X), clear(Y)],
[del(holding(X)), del(clear(Y)),
add(handempty), add(on(X, Y)), add(clear(X))])
И теперь построим рекурсивный контроллер для генерации плана. Первый предикат планирования содержит условия останова при успешной реализации плана, то есть при достижении цели. Последний предикат говорит о
завершении полного перебора, после которого дальнейшее планирование невозможно.
Рекурсивный генератор плана работает следующим образом:
1. Находится отношение для предиката move.
2. При помощи операции получения подмножества идет проверка того,
выполняются ли предусловия Preconditions для данного состояния.
3. При помощи предиката change_state генерируется новое состояние
Child_state с использованием списка добавления и удаления.
4. При помощи предиката member_stack проверяется, не содержится ли
новое состояние в списке уже пройденных.
5. При помощи оператора stack добавляется новое состояние Child_state
в стек New_move_stack.
6. При помощи оператор stack добавляет исходное состояние name в
стек New_been_stack.
7. При помощи рекурсивного вызова предиката plan на основе дочернего состояния Child_state и обновленных стеков New_move_stack и Been_stack
находится новое состояние.
61
Очевидно, что поиск реализуется на основе стеков с помощью алгоритма поиска в глубину с возвратами и завершается после нахождения первого пути к цели. [Википедия, Пролог (язык программирования)]
plan(State, Goal, _, Move_stack) :equal_set(State, Goal),
write(xпереходы'), nl,
reverse_print_stack(Move_stack).
plan(State, Goal, Been_stack, Move_stack) :move(Name, Preconditions, Actions),
conditions_met(Preconditions, State),
change_state(State, Actions, Child_state),
not(member_stack(Child_state, Been_stack)),
stack(Name, Been_stack, New_been_stack),
stack(Child_state, Move_stack, New_move_stack),
plan(Child_state,Goal,New_been_stack,New_move_stack), !.
plan(_, _, _) :write(‘При таких переходах план невозможен!').
conditions_met(P, S)
subset(P, S),
change_state(S, [ ], S).
change_state(S, [add(P)|T], S_new) :change_state(S, T, S2),
add_if_not_in_set(P, S2, S_new), !.
change_state(S, [del(P)|T], S_new) :change_state(S, T, S2),
delete_if_in_set(P, S2, S_new), !.
reverse_print_stack(S) :empty_stack(S).
reverse_print_stack(S) :stack(E, Rest, S),
62
reverse_print_stack(Rest),
write(E), nl.
Предикат go для инициализации аргументов предиката plan и предикат
test для демонстрации простого метода запуска системы планирования будут
такие:
go(Start, Goal) :empty_stack(Move_stack),
empty_stack(Been_stack),
stack(Start/ Been_stack/ New_been_stack),
plan(Start/ Goal, New_been_stack/ Move_stack).
test :go([handempty, ontable(b), ontable(c),
on(a, b), clear(c), clear(a)],
[handempty, ontable(a), ontable(b),
on(c, b) , clear(a), clear(c)]).
Изобразим графически фрагмент поиска пространства на рис.6.2:
63
В
А
С
А
В
С
А
В
С
А
В
С
А
С
В
С
А
В
Рис.6.2 – Начальные уровни пространства состояний для мира блоков
64
7. РАЗРАБОТКА МЕТОДОВ И АЛГОРИТМОВ
7.1. Использование преимуществ языка С++
Основной задачей дипломного проекта является разработка планировщика системы STRIPS на языке С++. C++ - это компилируемый статически
типизированный язык программирования общего назначения. Он поддерживает такие парадигмы программирования, как процедурное программирование, объектно-ориентированное программирование (ООП), обобщенное программирование, обеспечивает модульность, раздельную компиляцию, обработку исключений, абстракцию данных, объявление типов (классов) объектов, виртуальные функции.
Язык C++ - чрезвычайно мощный язык, который содержит средства для
создания эффективных программ почти пригодного для любого назначения,
начиная низкоуровневыми утилитами и драйверами, заканчивая сложными
программными комплексами самого различного назначения. [C++ “От и до”,
2013]
Основные достоинства языка С++:
 поддержка объектно-ориентированного программирования;
 возможность создания для разных типов данных обобщённых алгоритмов, специализация и вычисление типов на этапе компиляции;
 кроссплатформенность, компиляторы доступны для большого количества платформ, на языке C++ разрабатывают программы для практически
любых платформ и систем;
 предсказуемость выполнения при построении системы реального
времени. Весь код, который генерируется компилятором для реализации
языковых возможностей, определён в стандарте. Кроме того строго определены места программы, где дынный этот код выполняется. Благодаря этому
можно замерять или рассчитывать время реакции программы на внешнее событие;
65
 эффективность, программист имеет максимальный контроль над всеми аспектами структуры и порядка исполнения программы. При этом ни одна из языковых возможностей, которая приводит к дополнительным накладным расходам, не является обязательной для использования, поэтому, если
необходимо, язык может обеспечить максимальную эффективность программы;
 вызов деструкторов объектов при их уничтожении происходит автоматически, причём он обратен вызову конструкторов. Это позволяет упростить (необходимо только объявить переменную) и сделать более надёжным
освобождение ресурсов (память, файлы, семафоры и т. п.), а также позволяет
гарантировать выполнение переходов состояний программы, которые не обязательно связанны с освобождением ресурсов (например, запись в журнал);
 наличие пользовательских функций-операторов, которые позволяют
записывать выражения над пользовательскими типами в естественной алгебраической форме;
 поддержка физической (const) и логической (mutable) константности.
Благодаря им увеличивается надежность программы, потому что компилятору позволяется, например, вести диагностику ошибочных попыток изменения
значения переменной. [Мессенбок, 1998]
66
7.2. Использование преимуществ объектно-ориентированного
программирования
Объектно-ориентированный подход к программированию заключается
в моделировании объектов реального мира, которые интерпретируются как
совокупность свойств и поведения. Основополагающей идеей объектноориентированного подхода является объединение данных и функций, оперирующих этими данными, в одно целое, которое и называется объектом.
Поскольку
программа
написана
с
использованием
объектно-
ориентированного метода, поэтому также рассмотрим его достоинства:
 возможность использования классов:
o классы дают возможность абстрагироваться от деталей реализации, за счет конструирования из полезных компонент, которые обладают простыми инструментами;
o данные и операции в совокупности образуют некую сущность,
и они не «размазываются» по всей программе, в отличие от процедурного программирования;
o код и данные локализованы, благодаря чему улучшается
наглядность и удобство сопровождения программного обеспечения;
o защита наиболее критичных данных от несанкционированного
доступа за счет инкапсуляции информации;
 возможность создания расширяемых систем. То есть можно работать
с новыми компонентами, не используя внесения в нее каких-либо изменений.
Добавление компонент может быть на этапе выполнения;
 наследование и полиморфизм позволяют не писать новый код, а изменять существующий, при помощи добавления и переопределения атрибутов. Это приводит к сокращению объема исходного кода. [Все обо всем,
2013]
67
7.3. Общая схема алгоритма программы
Начало
Чтение входных данных
Цель и текущее состояние не совпадают
Заполнение стека главной целью
Стек целей не пуст
Верхняя цель соответствует
текущему состоянию
Нет
Да
Цикл-до
Удалить верхнюю подцель стека
Верхняя подцель не пуста и соответствует текущему состоянию
Б
А
68
А
Стек правил не пройден до конца
Нет
Голова стека целей найдена в
списке добавлений правила
Да
Запомнить имя подходящего правила
Заполнить аргументы правила
Заполнить списки добавления и вычеркивания правила
Цикл-пока
Создать облако
Последнее облако совпадает с другим (равны
состояния и миницель)
Нет
Да
Удалить облака до повторного
Заменить текущее состояние на состояние, взятое из облака
Выбрать следующее подходящее правило
Заменить стек миницелей на стек, взятый из облака
Заменить стек плана действий на стек, взятый из облака
В
69
Б
А
В
Да
Нет
Правило НЕ применимо
Удалить из текущего состояния предусловие
Предусловие правила помещается в
стек целей
Добавить список добавления в
текущее состояние
Добавить правило в план
Цикл-пока
Цикл-пока
Оптимизировать план
Конец
70
7.4. Организация облачной структуры
В описанном выше алгоритме упоминалось понятие «облака» (рис
7.1). Поясним, что имелось в виду. Они представляют собой стеководревовидную структуру, то есть каждый узел дерева в программе представлен как элемент стековой структуры (рис.7.2). Облака хранят информацию о
состояниях объектов (кубиков и руки робота) на каждой итерации. Это необходимо для:
 обеспечения поиска с возвратом, иначе данная программа на одном
из шагов зашла бы в тупик (оказалась бы на листе дерева). Но с помощью дерева мы поднимаемся на узел выше и переходим по другой ветке.
 предотвращения зацикливания программы. В случае если текущее состояние и одно из состояний, хранящееся в других облаках совпадают, то программа переходит в найденный узел и поиск продолжается по другой ветке
этого узла.
 удаления бессмысленного повтора при оптимизации плана, которая
удаляет облака с одинаковыми состояниями.
А
В
1
С
2
3
Рис 7.1 – Визуализация облака состояния
71
1
2
3
4
5
7
6
8
9
9
8
7
6
5
4
3
2
1
Рис.7.2 – Древовидная (для наглядности) и стековая (для программы)
структура облаков
Поиск по дереву осуществляется в глубину. Это можно увидеть на
рис.7.2. Если посмотреть на стековую организацию облаков, изображенную
на рис.7.2 справа, то можно увидеть, что узлы в стеке повторяются. Это происходит из-за того, что стек целей корректируется (удаляется вершина, если
она совпадает с текущим состоянием).
72
7.5. Эвристическая оценка алгоритма
Использование методов решения задач, которые организованы по
принципу поиска по дереву, обычно приводит к возникновению проблемы
комбинаторной сложности из-за быстрого увеличения количества вариантов.
Вследствие чего возникает «комбинаторный взрыв». А также бывают случаи,
когда программа не может достичь цели. Например, при неверно заданной
пользователем цели или при внешних воздействиях (кубик А забрали в процессе решения задачи). Тогда при выполнении программа «уходит в задумчивость», то есть зацикливается.
Необходимо вычислить верхний предел количества итераций, когда
становится очевидно, что программа зациклилась, и она нуждается в корректном завершении. Этот предел можно получить при помощи эвристического метода.
Эвристические методы основаны на результатах опыта, интуиции,
экспертных оценок и применяются как для количественного измерения текущих событий, так и для прогнозирования их дальнейшего развития.
Количество итераций зависит от:
 количества кубиков;
 количества заданных состояний объектов.
Максимальное количество итераций (верхний предел) будем вычислять по формуле:
MAX
N итерац
 e Nсост i
где
MAX
N итерац
- максимальное количество итераций,
N сост - количество заданных состояний объектов,
i – количество кубиков.
73
8. РАЗРАБОТКА ПРОГРАММЫ
8.1. Описание типов данных
Рассмотрев все достоинства языка С++ и объектно-ориентированного
программирования, решено описать все объекты программы, их состояния и
списки при помощи классов.
8.1.1. Описание объекта
Объект – это один из множества кубиков, каждый из которых имеет
свое имя. В свою очередь эти объекты должны храниться в некоем динамическом списке, поэтому в его свойствах должен быть указатель на следующий элемент списка.
Пример. Объект А.
//Класс существующих объектов
class TObj
{
public:
char *name; //имя объекта (кубика)
TObj *next; //указатель на следующий элемент списка
TObj();
//конструктор
};
Класс списков должен иметь указатель на голову списка. Кроме того
автоматически считается количество объектов в списке.
Также класс имеет следующие методы:
 TOList() – конструктор, необходимый для инициализации списка;
 void Push(char *c_name) - функция добавления нового элемента в
начало списка (на вершину стека);
 void Print() - Функция печати списка объектов.
74
//Класс списков существующих объектов
class TOList
{
public:
TObj *head; //первый элемент списка
int count;
//количество элементов списка
TOList();
//конструктор
void Push(char *c_name);
void Print();
};
8.1.2. Описание состояний кубика/робота
Любой объект, будь то кубик или рука робота, имеет имя и автоматически вычисляемый идентификатор.
Пример. Кубик В имеет идентификатор 2.
//Класс объектов - кубики/робот
class TObject
{
friend class TRelation;
friend class TRule;
public:
char *name_obj;
//имя объекта (кубика)
int id;
//номер аргумента (0 - нет арг.)
};
В случае если кубик лежит на столе, он имеет свою позицию – это целое число. Флаг flag_pos показывает, имеет ли заданное состояние кубика
свою позицию. Очевидно, что для состояния HOLDING(A), позиция на столе
не нужна.
75
Пример. На рис.8.1 кубик В лежит на столе и занимает позицию 1.
Значит состояние ONTABLE(B) имеет истинное значение флага, а состояние
HOLDING(A) – ложное.
А
В
1
С
2
3
Рис.8.1 – Пример состояния мира
//Класс позиций кубика
class TPosition
{
friend class TRelation;
public:
int numb_pos;
//номер позиции
bool flag_pos;
//наличие позиции
};
В любой момент времени мир кубиков пребывает в некотором состоянии и следующий класс содержит информацию об имени состояния.
Пример. Состояние HANDEMPTY – рука робота свободна.
//Класс состояний кубика/робота
class TState
{
friend class TRelation;
public:
char *name_state;
//имя состояния
};
76
8.1.3. Описание отношений состояния и объекта
Очевидно, что любое состояние мира описывается при помощи имени
состояния и его аргумента или аргументов (и позицией, если необходимо). А
также это отношение должно иметь указатель на следующее отношение в
списке. И для инициализации отношения необходим конструктор.
Пример. Состояние, изображенное на рис.8.2 описывается следующим
образом: ONTABLE (C,3).
С
1
2
3
Рис.8.3 – Пример состояния кубика
//Класс отношений состояния и объекта
class TRelation
{
friend class TRule;
friend class TList;
public:
TState state;
TObject obj1;
TObject obj2;
TPosition pos;
TRelation *next; //указатель на следующий элемент списка
TRelation();
//конструктор
};
С помощью отношений описываются текущее состояние, предусловия/вычеркивания и добавления правила, а также целевое состояние. Очевидно, что для них необходим список, а для некоторых и стек. Поэтому создан класс списков отношений, который имеет указатель на вершину стека
(голову списка). Также автоматически вычисляется количество отношений в
списке.
77
Кроме того класс отношений имеет следующие методы:
 TList() – конструктор, для инициализации класса;
 void Push(char *c_state, char *c_obj1=NULL, char *c_obj2=NULL, int
c_pos=0) - функция добавления нового элемента в начало списка (на вершину
стека);
 void PushRule(TRule *, char *c_state, char *c_obj1=NULL, char
*c_obj2=NULL, int c_pos=0) - функция добавления нового элемента правила в
начало списка (предусловия, добавления);
 void Pop() - функция удаления первого элемента списка (вершины
стека);
 bool Find(TRelation *) - функция проверяет вхождение элемента в
данный список: 1 - элемент найден в списке, 0 - не найден;
 TRelation *FindList(TRelation *) - функция проверяет вхождение элемента в данный список, если элемент найден в списке - возвращает указатель
на предшествующий элемент списка, если элемент является головой - возвращает NULL, если элемент не найден - возвращает последний элемент
списка;
 TRelation *FindState(TRelation *) - функция проверяет вхождение
элемента (без учета аргументов) в данный список, если элемент найден в
списке - возвращает указатель на предшествующий элемент списка, если
элемент является головой - возвращает NULL, если элемент не найден - возвращает последний элемент списка;
 bool TList::MatchRel(TList) - функция поиска соответствия двух списков отношений: 0 - второй список не включает в себя первый, 1 – включает;
 bool FindCon(TRelation *) - функция поиска совпадения конца списка
и элемента: 1 - элементы совпадают, 0 - не совпадают;
 TRelation *ViewTail() - функция нахождения конца стека;
 void Remove(TRelation *, TList) - функция удаления заданного элемента из списка;
78
 void Print() - функция печати списка отношений;
 void Clean() - Функция очищения списка.
//Класс списков отношений
class TList
{
public:
TRelation *head; //первый элемент списка
int count;
//количество элементов списка
TList();
//конструктор
void Push(char *c_state, char *c_obj1=NULL,
char *c_obj2=NULL, int c_pos=0);
void PushRule(TRule *, char *c_state, char *c_obj1=NULL,
char *c_obj2=NULL, int c_pos=0);
void Pop();
bool Find(TRelation *);
TRelation *FindList(TRelation *);
TRelation *FindState(TRelation *);
bool TList::MatchRel(TList);
bool FindCon(TRelation *);
TRelation *ViewTail();
void Remove(TRelation *, TList);
void Print();
void Clean();
};
8.1.4. Описание правил для объекта
Кроме отношений, есть еще и правила. Они имеют не только схожие
свойства, но и совершенно другие. Каждое правило имеет свое имя и, автоматически назначенный, идентификатор. Также правило имеет один или более объектов, на которые направлено его действие, иногда имеет позицию
(например, для выполнения действия PICKUP (A,2)). Класс автоматически
высчитывает количество аргументов правила (это необходимо для внутренней работы программы). Каждое правило имеет свои собственные список
79
предусловий/вычеркивания и список добавления, которые имеют тип списка
класса отношений, описанного ранее. И конечно же класс имеет указатель на
следующее правило.
Класс имеет 2 метода:
 TRule() – конструктор для инициализации нового объекта класса;
 void fill(char *c_rule, char *c_obj1=NULL, char *c_obj2=NULL) - вспомогательная функция для заполнения подходящих правил вручную.
Пример. Правило pickup (x) поднимает кубик со стола. Оно имеет список предусловия/вычеркивания: (ontable (x), clear (x), handempty) и список
добавления: holding (x).
//Класс правил для объекта
class TRule
{
public:
int id_rule;
//номер правила
char *name_rule; //имя правила
TObject r_obj1;
TObject r_obj2;
TPosition r_pos;
int sum_arg;
//количество аргументов (0 - без арг.)
TList prec;
//список предусловий
TList add;
//список добавлений
TRule *r_next;
//указатель на следующий элемент списка
TRule();
//конструктор
void fill(char *c_rule, char *c_obj1=NULL,
char *c_obj2=NULL);
};
С помощью класса правил описываются правила мира, которые осуществляют действия, и полученный в результате выполнения программы
план действия для достижения целевого состояния из начального. Очевидно,
что для хранения требуется списки правил, а для плана – стек.
80
Поэтому создан класс списков правил, который имеет указатель на
начало списка (вершину стека). Класс автоматически вычисляет количество
аргументов в списке.
Класс списков правил имеет следующие методы:
 TRList() – конструктор, необходимый для инициализации списка правил;
 void Push(char *c_rule, char *c_obj1=NULL, char *c_obj2=NULL, int
c_pos=0) - функция добавления нового элемента в начало списка (на вершину
стека);
 void Pop() - функция удаления первого элемента списка (вершины
стека);
 void PopTail() - функция удаления последнего элемента списка (хвоста стека);
 bool Find(TRule *) - функция проверки вхождения элемента в данный
список: 1 - элемент найден в списке, 0 - не найден;
 bool CompareHead(TRule *) - функция поиска совпадения головы
списка и элемента: 1 - элементы совпадают, 0 - не совпадают;
 void Print() - функция печати списка правил;
 void PrintState() - функция печати списка правил со списками предусловий и добавлений;
 void Clean() - функция очищения списка;
 void FillPA(TRule *) - функция заполнения списков предусловия и
добавления правила;
 void FindPos(TRelation *) - функция поиска свободной позиции (без
лежащего объекта) для правила вершины списка, нуждающегося в задании
позиции.
81
//Класс списков правил
class TRList
{
public:
TRule *r_head; //первый элемент списка
int r_count;
//количество элементов списка
TRList();
//конструктор
void Push(char *c_rule, char *c_obj1=NULL,
char *c_obj2=NULL, int c_pos=0);
void Pop();
void PopTail();
bool Find(TRule *);
bool CompareHead(TRule *);
void Print();
void PrintState();
void Clean();
void FillPA(TRule *);
void FindPos(TRelation *);
};
8.1.5. Описание облаков
Так как программа написана с использованием понятия «облако», полностью описывающего состояние внешнего мира, поэтому для него необходимо создать собственный класс. Каждое облако имеет свой, автоматически
вычисляемый номер. Итак, облако содержит следующие списки, которые
имеют типы данных, описанные ранее: список состояний, список подходящих для этого состояния правил, список правил, вошедших в еще не завершенный план, стек миницелей в данном состоянии, стек плана действий робота. А также содержит указатель на подошедшее правило и указатель на
следующее облако. И конструктор, необходимый для инициализации облака.
82
Пример. Изображен на рис.8.4.
А
В
1
С
2
3
Рис.8.4 – Облако, описывающее состояние внешнего мира
//Класс состояний внешнего мира (облако)
class TCloud
{
public:
int id_cloud;
//номер облака
TList status;
//список состояний (описание облака)
TRList rule;
//список подходящих правил
TRule *suit_rule; //подошедшее правило
TRList plan_rule; //правило, вошедшее в план
TList goals;
//стек миницелей
TRList plan;
//стек плана действий робота
TCloud *c_next;
//указатель на следующий эл-т списка
TCloud();
//конструктор
};
Но для работы программы нам необходим стек облаков. Для этого создан класс списка облаков. Он содержит указатель на вершину стека. Программа автоматически высчитывает количество облаков в стеке.
83
Кроме того, необходима реализация следующих методов класса:
 TCList() – конструктор, который необходим для инициализации стека
облаков;
 void Push(TRelation *, TRule *, TRelation *, TRule *) - функция добавления нового облака в начало списка (на вершину стека);
 void Pop() - функция удаления головы списка;
 bool MatchCloud() - функция поиска одинаковых облаков: 0 - голова
списка облаков не совпадает ни с одним облаком, N - совпадает с облаком
под номером N;
 TRList UpgradePlan() - функция оптимизации плана (удаление ненужных действий из плана).
//Класс списков облаков
class TCList
{
public:
TCloud *c_head;
//первый элемент списка
int c_count;
//количество элементов списка
TCList();
//конструктор
void Push(TRelation *, TRule *, TRelation *, TRule *);
void Pop();
bool MatchCloud();
TRList UpgradePlan();
};
84
8.2. Входные и выходные данные
Входными данными для программы является текстовый файл, который содержит следующую информацию:
 имена используемых объектов, перечисленных через запятую;
 описание текущего состояние мира;
 описание правил, которые необходимо применить для достижения
цели. Правила должны содержать список предусловия/вычеркивания и список добавления;
 описание цели с помощью списка состояний.
Пример формата входного файла Input.txt, описывающем начальное и
целевое состояния, изображенном на рис. 8.5.
С
С
А
1
В
А
В
3
1
2
2
3
Рис.8.5. Начальное и целевое состояния
OBJECTS
A,B,C
STATE
clear(B)
on(C,A)
clear(C)
ontable(A,1)
ontable(B,3)
handempty
RULE pickup(x)
PREC ontable(x,5)
85
clear(x)
handempty
ADD holding (x)
RULE putdown(x,5)
PREC holding(x)
ADD ontable(x,5)
clear(x)
handempty
RULE stack(x,y)
PREC holding(x)
clear(y)
ADD handempty
on(x,y)
clear(x)
RULE unstack(x,y)
PREC handempty
on(x,y)
clear(x)
ADD holding(x)
clear(y)
GOAL
clear(B)
on(C,A)
clear(C)
ontable(A,1)
ontable(B,2)
handempty
Выходные данные могут быть двух видов.
1) Текстовый файл, содержащий только список действий оптимизированного плана.
Пример формата выходного файла Output1.txt.
План:
pickup(B)
putdown(B,2)
86
2) Текстовый файл, содержащий промежуточную информацию выполнения программы. Он включает в себя описание стека целей, текущего состояния, действий плана, подходящего правила, полученных на каждом шаге
программы. Вывод файла в таком виде получается при указании специального ключа «debug» при вызове функции.
Пример формата выходного файла Output2.txt, полученного с использованием ключа «debug».
Вывод промежуточных шагов системы планирования
1---------------------------------------Стек целей:
clear(B)
on(C,A)
clear(C)
ontable(A,1)
ontable(B,2)
handempty
Текущее состояние:
handempty
ontable(B,3)
ontable(A,1)
clear(C)
on(C,A)
clear(B)
План:
---------------------------------------1---------------------------------------Стек целей:
ontable(B,2)
handempty
Текущее состояние:
handempty
ontable(B,3)
ontable(A,1)
clear(C)
on(C,A)
87
clear(B)
План:
---------------------------------------Подходящее правило:
putdown(B,2)
2---------------------------------------Стек целей:
holding(B)
ontable(B,2)
handempty
Текущее состояние:
handempty
ontable(B,3)
ontable(A,1)
clear(C)
on(C,A)
clear(B)
План:
---------------------------------------Подходящее правило:
pickup(B)
3---------------------------------------Стек целей:
holding(B)
ontable(B,2)
handempty
Текущее состояние:
holding(B)
ontable(A,1)
clear(C)
on(C,A)
План:
pickup(B)
---------------------------------------3---------------------------------------Стек целей:
88
ontable(B,2)
handempty
Текущее состояние:
holding(B)
ontable(A,1)
clear(C)
on(C,A)
План:
pickup(B)
---------------------------------------Подходящее правило:
putdown(B,2)
4---------------------------------------Стек целей:
ontable(B,2)
handempty
Текущее состояние:
handempty
clear(B)
ontable(B,2)
ontable(A,1)
clear(C)
on(C,A)
План:
putdown(B,2)
pickup(B)
---------------------------------------Оптимизированный план:
pickup(B)
putdown(B,2)
89
8.3. Работа с файлами
Входными и выходными данными являются текстовые файлы. Поэтому
для работы с ними были созданы специальные функции:
 void ReadFile(char *,TOList &,TList &,TRList &,TList &) - функция
чтения из файла;
 void PrintTList(char *,TList) - функция печати списка отношений в
файл;
 void PrintTRList(char *,TRList) - функция печати списка правил в
файл;
 void WritePlanFile(char *,char *,TRList) - функция записи в файл;
8.4. Методы испытаний
Поиск и отладка синтаксических ошибок программы проводились с
помощью встроенного в систему разработки программ отладчика Microsoft
Visual Studio 2010 Professional.
Правильность и корректность отображения выходной информации в
текстовый файл проверка проводилась на тестовых примерах, в которых выявлялись ошибки работы программы и неточности при выводе в текстовый
файл.
Проверка программы на устойчивость к сбоям в работе или функциональная устойчивость проводилась путем многократного прогона программы
с различными входными данными.
90
8.5. Описание тестовых примеров и результатов их выполнения
Запуск программы осуществляется через командную строку в формате
вида:
<Имя_пр-мы_запуска> <Имя_вх._файла> <Имя_вых._файла> <Ключ>
где:
<Имя_пр-мы_запуска> - Strips.exe;
<Имя_вх._файла> - имя текстового файла с входными данными;
<Имя_вых._файла> - имя текстового файла с выходными данными;
<Ключ> = debug – для вывода в текстовый файл промежуточных шагов
выполнения программы.
8.5.1. Тест 1. Диагностика ошибок
Продиагностируем программу на ошибки.
1. Введем в командную строку неверное имя входного файла программы. Получим сообщение об ошибке:
Файл ‘Input.txt’ не найден
Для закрытия окошка необходимо нажать клавишу “Enter”.
2. Запустим программу с ошибочными входными данными. Укажем в
описании начального состояния состояние с одинаковыми аргументами:
on(A,A)
Программа выдаст сообщение об ошибке:
Аргументы не могут быть одинаковыми
Для закрытия окошка необходимо нажать клавишу “Enter”.
91
3. Запустим программу с указанием Объектов А, В и С. Но в цели укажем несуществующий объект, например ONTABLE (B,T).
Тогда программа выдаст сообщение об ошибке:
Аргументы не совпадают
Для закрытия окошка необходимо нажать клавишу “Enter”.
8.5.2. Тест 2. Классическая задача
Рассмотрим классический тест Нильсона. Запустим программу без
ключа с входными данными, описывающими состояния, изображенные на
рис.8.6,
А
С
А
В
В
С
Рис.8.6 – Начальное и целевое состояния
В результате работы программы будет сформирован следующий текстовый файл:
План:
unstack(C,A)
putdown(C)
pickup(B)
stack(B,C)
pickup(A)
stack(A,B)
92
Видно, что выданный план в данной ситуации является самым оптимальным для выполнения.
8.5.3. Тест 3. Работа с позициями
Немного изменим тест 2, добавив еще один аргумент к описанию состояний – номер позиции на столе. Запустим программу с теми же входными
данными, что описаны в тесте 2 раздела 8.5.2, но с использованием дополнительного аргумента (рис.8.7) и ключа “debug”, результат работы планировщика будет тем же, но выходной файл уже будет содержать промежуточную
информацию. Мы увидим, что план был найден за 1068 итераций. А размер
выходного текстового файла резко увеличится с 7 строк до 58039. Потому
покажем лишь малую часть выходного файла.
А
В
С
А
1
В
2
3
С
1
2
3
Рис.8.7 – Начальное и целевое состояния
В результате работы программы будет сформирован следующий текстовый файл:
Вывод промежуточных шагов системы планирования.
1---------------------------------------Стек целей:
on(A,B)
on(B,C)
clear(A)
ontable(C,2)
93
handempty
Текущее состояние:
handempty
ontable(B,3)
ontable(A,1)
clear(C)
on(C,A)
clear(B)
План:
---------------------------------------Подходящее правило:
stack(A,B)
...
555---------------------------------------Стек целей:
handempty
on(C,A)
clear(C)
holding(C)
clear(C)
clear(B)
ontable(B)
holding(B)
clear(C)
clear(B)
holding(A)
on(A,B)
on(B,C)
clear(A)
ontable(C,2)
handempty
Текущее состояние:
handempty
on(A,C)
clear(A)
on(C,B)
ontable(B,3)
План:
stack(A,C)
pickup(A)
stack(C,B)
unstack(C,A)
---------------------------------------...
1068---------------------------------------Стек целей:
on(A,B)
on(B,C)
clear(A)
ontable(C,2)
94
handempty
Текущее состояние:
clear(A)
on(A,B)
handempty
on(B,C)
ontable(C,2)
План:
stack(A,B)
pickup(A)
stack(B,C)
pickup(B)
putdown(A,1)
unstack(A,B)
stack(A,B)
pickup(A)
putdown(C,2)
unstack(C,A)
---------------------------------------Оптимизированный план:
unstack(C,A)
putdown(C,2)
pickup(B)
stack(B,C)
pickup(A)
stack(A,B)
Получается, что запрограммированный робот может работать не только
в одной линии плоскости, но и на всей плоскости, посылая программе данные, будто объекты расположены в одну линию (см. рис.8.8).
95
1
2
A
4
5
6
CB
8
9
С
А
1
2
3
В
4
5
6
7
8
9
Рис.8.8 – Преобразование задачи на плоскости в задачу, решаемую программой.
96
8.5.4. Выводы
Программа успешно прошла тестирование на синтаксические и семантические ошибки, завершая программу корректно с указанием ошибки.
Также программа решила классическую задачу, предоставив в качестве
результата оптимальный план.
Кроме того, классическая задача была несколько модифицирована с
помощью добавления аргумента позиций, что позволит увеличить границы
применимости программы. В результате программа выдала оптимальный
план с учетом модификаций.
Время работы программы зависит от сложности задачи (работа с
тремя кубиками):
 простая решается за 0,046 сек,
 сложная решается за 0,421 сек.
Тогда как программа, написанная на Прологе, в среднем решает задачу
за 2 сек. Очевиден значимый выигрыш во времени.
Вычислим максимальное количество итераций для проведенных тестов
из трех кубиков.
MAX
N итерац
 e Nсост i  e53  2980
97
9. ОХРАНА ТРУДА
В настоящее в наше время трудно представить человечество без таких
помощников как роботы. Они присутствуют почти везде, помогая человеку
выполнять какую-либо работу. Помогают на заводах, где человек не может
выполнять повседневную работу без чувства усталости. Еще, например, в тех
местах, где человек как биологическое существо, не сможет находиться и
минуты, а то и вообще.
Их присутствие на производстве естественно выгодно для руководителей предприятия. Многие заводы мира перешли полностью на автоматизацию своего производства, но все же робот еще пока не может полностью обходиться без человека. Лучшие умы всех стран пытаются разработать такого
робота, который мог бы полностью выполнять действия как человек. Мог бы
принимать решения на ходу и быть коммуникабельными.
Разработчики видят возрастающий интерес у потребителей электроники к «искусственному интеллекту». Этот термин есть в описании почти
всех премиум-продуктов. Но без исключения во всех случаях это всего лишь
имитатор человеческой логики, который основан на анализе и сопоставлении
вводимых данных.
Думать роботы, можно сказать, научились с помощью встроенных
компьютеров. Робот может выполнять те действия, которые записаны у него
в программе. Однако собственные решения они принимать еще не могут, для
этого и необходим искусственный интеллект. На данный момент времени
роботы помогают на работе в офисе, в кафе, на заводах, под водой на большой глубине, где человек не может находиться, на атомных электростанциях
с большой радиацией, а также в космосе и на других планетах, где не ступала
нога человека. [Российское атомное сообщество, 2011]
98
Роботы радиационной разведки
Рассмотрим применение роботов радиационной разведки. Комплексное использование дистанционных радиометрических методов и современной робототехники разрешает обеспечить полную безопасность персонала во
время выполнения радиационных обследований, которые находятся в условиях среднего и высокого уровня радиоактивности.
Навык проведения радиационной разведки и обследования разных объектов говорит о том, что источники излучения порой не имеют точечную
структуру, а их активность часто имеет распределенный в пространстве характер. В данных условиях использование обычных радиометров для радиационного обследования - малоэффективно.
Если имеется большое количество источников излучения или они имеют распределенную структуру, то наиболее эффективным является метод дистанционной коллимированной радиометрии. Этот метод позволяет при существенно меньшей дозовой нагрузке (иногда в сотни раз) на персонал получать наиболее объективную картину, показывающую распределение радиоактивного загрязнения, в отличие от методов обычной дозиметрии.
Но даже использование дистанционных методов не отменяет облучение операторов в точке расположения коллимированного детектора. Данный
недостаток можно решить, поместив детектор на робототехническом средстве, что позволит удалить оператора с места проведения опасных работ.
Состав робототехнического комплекса
Сотрудники РНЦ «Курчатовский институт» создали робототехнический комплекс, который основан на коллимированном детекторе и шведского
робота Brokk 90. Основной целью разработки было осуществление проведения эффективных и безопасных для человека работ по радиационному обследованию объектов. Этот комплекс был назван «Брокк-Пионер» (от англ.
99
pioneer – первопроходец, разведчик). Был выбран робот Brokk из-за нескольких обстоятельств.
Техника компании Brokk разработана с учетом потенциальных экстремальных нагрузок, для работы в очень тяжелых условиях (при высоких и
низких температурах, возможности загазованности, обвалов, запыленности,
электромагнитных и радиационных полях). Техника выпускается серийно, на
протяжении многих лет, и отличается надежностью.
Дистанционное управление машиной можно осуществлять как по кабелю, так и по радио на расстоянии до 100 м.
В состав робота входят различные сменные инструменты (гидромолот,
грейферный ковш, гидроножницы), поэтому он способен разрушать железобетонные конструкции. Благодаря этому он особенно удобен для использования в качестве носителя аппаратуры для обследования радиационноопасных объектов.
Помимо всего этого, в РНЦ «Курчатовский институт» приобретен
большой опыт применения роботов данного типа при разборке хранилищ радиоактивных отходов и прочих работах.
Измерительный блок системы разработан и сделан в виде навесного
сменного инструмента (рис.9.1) и находится на манипуляторе робота. Следовательно, при управлении машиной можно помещать аппаратуру в необходимое место.
Рис.9.1 - Измерительный блок
100
В состав измерительного блока входят:
 открытый детектор, который измеряет мощность дозы в точке размещения измерительного блока (диапазон измерения МЭД составляет 0,4-8,5
Зв/ч);
 коллимированый детектор (парциальный дозиметр), который измеряет поток излучения, идущего из установленного угла коллимации и позволяющий оценить вклад излучения из конкретного направления; угол коллимации прибора – 12°, толщина свинцовой защиты – 30 мм.
 видеокамера с большим (26-кратным) оптическим увеличением, которая направлена по одной оси с коллимированным детектором, позволяя
при этом подробно разглядеть объект, излучение которого в этот момент
фиксируется этим прибором;
 блок электроники, который обрабатывает информацию, с детектором,
замешивающим индикацию результатов измерения на видеосигнал с видеокамеры.
Информация от измерительного блока передается на пульт управления
(рис.9.2)по радиоканалу, или, кода невозможно его использовать, по проводному каналу на расстояние, которое необходимо для защиты оператора от
облучения.
Рис.9.2 - Изображение с пульта управления комплексом
101
Опыт и перспективы использования
В настоящее время в РНЦ «Курчатовский институт» проводятся работы по подготовке реактора МР для вывода из эксплуатации и разборки. Робототехнический комплекс «Брокк-Пионер» используется в работах, обследующих центральный зал исследовательского реактора МР в условиях радиоактивного загрязнения (рис.9.3).
Рис. 9.3 - Радиационного обследование хранилища СУЗ
Внедрение данного комплекса при обследовании хранилища стержней
системы управления и защиты реактора в условиях высокого уровня излучения могло бы полностью обеспечить дистанционное проведение работ. Был
совершен подробный визуальный осмотр содержимого хранилища. Использование видеосистемы, имеющей большое оптическое увеличение, разрешило бы детально рассматривать мельчайшие детали состояния конструкций, а
при использовании коллимированного детектора можно было бы определить
распределение активности на исследуемых конструкциях.
Итак, робототехнический комплекс «Брокк-Пионер» может быть эффективно использован для обследования и разведки объектов в условиях высокого уровня загрязнения. Особенно он необходим во время проведения
аварийных нештатных работ на хранилищах высокоактивных РАО и ОЯТ,
при выводе из эксплуатации ядерных установок, при поиске утерянных источников (например, РИТЭГов), при ликвидации аварийных ситуаций. [20]
102
10. РАСЧЕТ НАДЕЖНОСТИ
Для разрабатываемой программы необходимо определить следующие
свойства:
 завершенность,
 устойчивость;
 восстанавливаемость;
 доступность или готовность.
Завершенность – это свойство программного обеспечения (ПО) не попадать в состояния отказов из-за ошибок и дефектов в программе и данных.
Это свойство можно определить при помощи показателей: наработка на
ошибку (отказ) и степень покрытия ПО тестами функций и структуры программ.
1. Рассчитаем среднюю наработку на отказ при помощи формулы:
TO 
1
ПО
где  ПО - интенсивность ошибок программного обеспечения.
Последний параметр рассчитывается по формулам:
ПО  0  et
0 
где
N
N
0 K
 0 K
K
K
N  t ТП N  t ТПi ЯЗi ПЛi
t – фактическое время отладки;
α – коэффициент крутизны линии, который характеризует скорость ро-
ста надежности;
N0 – число ошибок обнаруженных за время отладки t;
N – общее число строк программы;
103
КТП – коэффициент, который учитывает влияние технологии программирования на надежность ПО;
КТПi – коэффициент, который учитывает использование i–ой технологии программирования;
КЯЗi – коэффициент, который учитывает использование i–ого языка
программирования;
КПЛi– коэффициент, который учитывает использование i–ой платформы
программирования.
При разработке данного программного обеспечения я использовала
объектно-ориентированную технологию программирования ( K
ТПi
 0,1 ) в
Microsoft Visual Studio 2010 Professional. Программа написана на языке высокого уровня С++, поэтому K
K
ПЛi
ЯЗi
 3 . И на 32-разрядной платформе, значит
 2 ).
Общее число строк всех программных файлов составляет 1757.
Отладка программного обеспечения производилась при помощи тестирования продолжительностью 8 часов. В таблице 10.1 представлены результаты.
Время отладки t, Интенсивность ошибок,
часы
λ0, 1/ час
39
8
0,001665
19
8
0,000811
9
8
0,000384
4
8
0,000171
3
8
0,000128
2
8
0,000085
1
8
0,000043
0
8
0,000000
Таблица 10.1 – Результаты тестирования программного обеспечения
Число ошибок
104
На основании вычисленных данных можно построить кривую зависимости интенсивности ошибок от времени отладки (рис.10.1)
Рис.10.1 - График зависимости интенсивности ошибок от времени отладки
На графике легко заметить экспоненциальную зависимость интенсивности ошибок ПО от времени отладки. Также зависит от коэффициента крутизны линии, которая характеризует скорость роста надежности α, и от фактического времени отладки ПО. В результате тестирования ПО был вычислен коэффициент крутизны линии α = 0,0135.
Итак, интенсивность ошибок разрабатываемого ПО равна:
0 
N
77
 1 
0 K


0,6

0,000329


N  t ТП 1757  8
 час 
 1 
  0,000295
ПО  0  e t  0,000329  e0,01358


 час 
Получилось, что для разрабатываемого программного обеспечения
средняя наработка на ошибку составит 3389 часа.
105
Вероятность безотказной работы – это вероятность того, что в пределах данной наработки отказ объекта не возникнет.
Вероятность безотказной работы определяется по формуле:
n
P(t )   Pi (t ) , где Pi (t )  e it
i 0
То есть:
P(t )  e ПО t , где ПО  0,000295 
1 

 час 
Рассчитаем вероятность безотказной работы за 10 000 часов (таблица
10.2).
Время работы
системы t, ч
0
500
1000
1500
2000
2500
3000
3500
4000
4500
5000
5500
6000
6500
7000
7500
8000
8500
9000
9500
10000
Вероятность безотказной
работы P(t)
1
0,862862438
0,744531587
0,642428341
0,554327285
0,478308193
0,412714173
0,356115558
0,307278739
0,265139282
0,228778727
0,19740457
0,170332989
0,146973938
0,126818291
0,109426739
0,094420223
0,081471664
0,070298839
0,060658227
0,052339706
Таблица 10.2 – Таблица расчета вероятности безотказной работы от
времени работы системы
106
Построим диаграмму в соответствие с таблицей (рис.10.2).
Рис.10.2 - График зависимости вероятности безотказной работы от
времени работы системы
2. Рассчитаем также степень покрытия программного тестами
функций и структуры программ.
Можно выделить следующие типы покрытия, но для каждого из них
необходимо разное число тестовых примеров:
 покрытие утверждений;
 покрытие ветвей;
 покрытие условий.
Покрытие утверждений. Необходимо следить, чтобы каждая строка кода выполнялась хотя бы один раз. Для достижения 100%-го покрытия утверждений, нужно будет выполнить тело оператора IF, то есть его условие
должно быть истинным.
Покрытие ветвей. Необходимо следить, чтобы каждая ветвь программы
была пройдена при всех возможных исходах. Для достижения100%-го покрытия необходимо выполнения программы не только из тела оператора IF,
но и из оператора FALSE. Также должны хотя бы раз быть выполнены тела
107
цикла WHILE. А для утверждений CASE или SWITCH необходимы такие тестовые примеры, при которых программа будет проходить по всем возможным веткам.
Покрытие условий. Необходимо следить, чтобы каждый операнд в
комплексных
логических
выражениях
значения
принимал
значения
FALSE/TRUE.
Каждый из представленных выше типов покрытия содержит в себе более низкие уровни. То есть при достижении 100%-го покрытия ветвей автоматически достигается 100%-ное покрытие утверждений. А при достижении
100%-го покрытия условий удовлетворяется 100%-ое покрытие ветвей.
В результате тестирования были получены следующие коэффициенты:
1. Коэффициент полноты:
К полн 
где
P
,
100%
Р – степень покрытия тестами в процентах.
Кполн 
98%
 0,98 .
100%
2. Коэффициент достоверности:
К дост 
где
N пр - N ош
N пр
,
Nпр – число прогонов;
Nош – число ошибок, обнаруженных во время данных прогонов.
К дост 
50 - 4
 0,92 .
50
Устойчивость к ошибкам и дефектам – это свойство программного
обеспечения автоматически поддерживать определенный уровень качества
функционирования при обнаружении ошибок и дефектов.
Появление ошибок в данной системе может возникнуть из-за сбоя технических средств, из-за введения пользователем входных данных, не соот108
ветствующим обозначенному формату данных. А введение данных, на основе которых нельзя получить корректные выходные данные, проверяется программой, то есть устойчивость программы обеспечивается за счет алгоритма.
Восстанавливаемость – свойство программного обеспечения в случае
отказа возобновлять требуемый уровень качества функционирования, а также
поврежденные программы и данные.
В случае возникновения отказа, для того чтобы разрабатываемая система соответствовала требуемому уровню качества функционирования,
необходимо данную систему запустить заново, что составляет 5 секунд. Но
при этом данные, обрабатываемые системой в момент возникновения отказа,
будут потеряны, поэтому работу с программой нужно будет начать сначала.
Это свойство ПО основано на временной избыточности.
Доступность или готовность – это свойство программного обеспечения быть в состоянии выполнять требуемую функцию в данный момент времени при заданных условиях использования.
Коэффициент готовности рассчитывается по формуле:
Kг 
где
Tо
,
Tо  Tв
То – средняя наработка на ошибку (3389 часов),
Тв – время восстановления программы (5 сек = 0,00139 часа).
Итак, коэффициент готовности разрабатываемой системы:
Kг 
3389
 0,9999996 .
3389  0,00139
109
11. РАСЧЕТ СМЕТНОЙ СТОИМОСТИ РАЗРАБОТКИ
Необходимо разработать УСНО для данного программного обеспечения. Создателя данной программы можно считать индивидуальным предпринимателем, поэтому для расчетов можно использовать упрощенную систему
налогообложения (УСНО).
При применении УСНО индивидуальный предприниматель освобождается от уплаты следующих налогов:
 налог на доходы физических лиц;
 налог на имущество физических лиц;
 НДС (кроме налога, уплачиваемого в соответствии с договором простого товарищества (договором о совместной деятельности)).
Индивидуальные предприниматели уплачивают взносы по системе
обязательного медицинского страхования и обязательного пенсионного страхования в федеральный фонд, который рассчитывается от минимального
размера оплаты труда, вне зависимости от дохода.
При расчете сметной стоимости (цены) необходимо:
 определить источники расходов, включая заработную плату и налоги;
 посчитать прибыль с учетом рентабельности продаж.
Цена разрабатываемой системы определяется по формуле:
Цена  Расходы  Прибыль
Времени разработки системы: T  3( месяца) .
Расходы состоят из:
 аренда помещения
 аренда оборудования
 расходные материалы
 плата за коммуникационные услуги
 налоги
110
Расходы на аренду помещения, исходя из того, что рабочее место под
одного человека – 4 м2. Аренда рабочего места в районе метро Академическая составляет 950 руб/м2 за месяц. Рассчитаем расходы за 3 месяца и 4 м2:
РасходыАП  950 * 4 * 3  11400( руб )
Расходы на аренду оборудования вычисляются как общая стоимость
компьютерных комплектующих, расписанная на 36 месяцев и умноженная на
время разработки системы. Для написания данного ПО достаточно ноутбука
HP Pavilion dv7-7150er B3Q50EA 17.3"(1600x900)/ i3-2370M(2.4Ghz)/ 4Gb/
500Gb/ GF GT630M 1024Mb/ Win7HP стоимостью 21 390 руб. Вычислим
расход на это оборудование:
РасходыАО 
21390
* 3  1782,5( руб )
36
Используемые расходные материалы представлены в таблице 11.1.
Наименование материала
Стоимость, руб.
Офисное приложение MICROSOFT Visual Studio 10 400 руб.
Professional 2010, 32/64, Rus, BOX, DVD [c5e-00595]
A4 500 листов "HP Office"
152 руб.
Флэш-память Transcend JetFlash 700
916 руб.
Итого: 11 468 руб.
Таблица 11.1 – Перечень расходных материалов
РасходыРМ  11468( руб )
Расходы на коммуникационные услуги составляют:
 Мобильная связь: 400 руб./мес. (МТС тариф «Smart»)
 Интернет: 450 руб./мес. (АкадемНет «AkNet-450-Стандарт 21»).
РасходыКУ  (400  450)  3  2550( руб.)
111
В качестве расходов на зарплату возьмем среднюю зарплату стажераразработчика C++ без опыта работы по Москве. Она составляет 45 000
руб/мес.
РасходыЗП  45000 * 3  135000( руб )
Итак, рассчитаем расходы без учета налогов:
Расходы  РасходыАП  РасходыАО  РасходыРМ  РасходыКУ  РасходыЗП 
 11400  1782,5  11468  2550  135000  162200,5
Рентабельность продаж (r) - коэффициент равный отношению прибыли от реализации продукции к сумме полученной выручки. Необходим при
расчете прибыли. Рассчитывается по следующей формуле:
r
Прибыль
100%
Цена
На российском рынке существует единицы аналогичных систем планирования, написанных на языке высокого уровня, поэтому можно считать, что
у данного программного обеспечения найдутся покупатели. Но так как проект можно разрабатывать дальше для более объемного применения, то коэффициент рентабельности продаж равен около 45%.
Следовательно, прибыль будет рассчитываться по формуле:
Прибыль
 0,45  Прибыль  0,45  Цена
Цена
Решим уравнение для нахождения общей стоимости разработки:
Цена  Расходы  Прибыль
Цена  162200,5  0,45  Цена
0,55  Цена  162200,5
Цена  294910( руб.)
Рассчитаем прибыль:
112
Прибыль  0,45  Цена  0,45  294910  132709,7( руб )
Расходы на налоги:
 Налог УСНО: 6% от дохода;
 Страховой взнос в Федеральный фонд медицинского страхования:
5,1% от минимального размера оплаты труда;
 Страховой взнос в Пенсионный фонд России: 22% от минимального
размера оплаты труда.
Расходыналог УСНО1  0,06  Цена  0,06  294910  17694,6( руб )
РасходыФФМС  796,5( руб )
РасходыПФР  6870,6( руб )
Рассчитаем налог УСНО:
Расходыналог УСНО  Расходыналог УСНО1  РасходыПФР  РасходыФФМС 
 17694,6  796,5  6870,6  10027,5( руб )
Общая цена с учетом налогов будет равна:
Ценаобщ  Расходы  Прибыль  Расходыналог УСНО  РасходыПФР  РасходыФФМС 
 162200,5  132709,5  10027,5  6870,6  796,5  312604,6( руб )
113
Сведем все составляющие общей стоимости системы, в том числе
прибыль и все виды расходов, в одну таблицу 11.2. И на основе нее построим
круговую диаграмму, отображающую процентные доли составляющих к общей стоимости разработки.
Наименование
Аренда помещений
Аренда оборудования
Расходные материалы
Коммуникационные услуги
Зарплата
Налог УСНО
Отчисления в ФФМС
Отчисления в ПФР
Прибыль
Значение
(руб.)
11400
1782,5
11468
2550
135000
10027,5
796,5
6870,6
132709,5
Таблица 11.2 – Составляющие общей стоимости системы
Рисунок 11.1 – Диаграмма составляющих общей стоимости системы
114
ЗАКЛЮЧЕНИЕ
В процессе разработки дипломного проекта были решены следующие
задачи:
 был проведен обзор систем интеллектуального планирования;
 было проведено исследование методов интеллектуального планирования на основе алгоритмов семейства STRIPS;
 был проведен обзор реализации планировщика на языке логического
программирования;
 были разработаны алгоритмическое и программное обеспечения системы интеллектуального планирования на объектно-ориентированном языке
программирования;
 были проведены экспериментальные исследования на примере решения ряда характерных задач.
Практическим путем было выявлено, что программная реализация системы STRIPS, написанная на языке Пролог, работает гораздо медленнее, в
отличие от реализации на языке С++.
Основная цель исследования - создание комплекса методов и программных средств интеллектуального планирования семейства STRIPS, используя стиль объектно-ориентированного программирования языка высокого уровня С++, была достигнута.
115
СПИСОК ЛИТЕРАТУРЫ
1. Википедия: свободная электронная энциклопедия: на русском языке [История искусственного интеллекта] //
http://ru.wikipedia.org/wiki/История_искусственного_интеллекта
2. Википедия: свободная электронная энциклопедия: на русском языке [Автоматическое планирование и диспетчеризация] //
http://ru.wikipedia.org/wiki/Автоматическое_планирование_и_диспетчериз
ация
3. Википедия: свободная электронная энциклопедия: на русском языке
[Универсальный решатель задач] //
http://ru.wikipedia.org/wiki/Универсальный_решатель_задач
4. Википедия: свободная электронная энциклопедия: на русском языке [Вопросно-ответная система] // http://ru.wikipedia.org/wiki/Вопросноответная_система
5. Википедия: свободная электронная энциклопедия: на русском языке
[STRIPS] // http://ru.wikipedia.org/wiki/STRIPS
6. Братко И. Программирование на языке Пролог для искусственного интеллекта / Пер. с англ. А.И.Лупенко, А.М.Степанова. Под ред.
А.М.Степанова. -М.: Мир, 1990, -560с.
7. Трофимов И.В. Исследовательский центр искусственного интеллекта //
История интеллектуального планирования – апрель 2005 - март 2006 г.
[Электронный ресурс]: URL: http://ai-center.botik.ru/planning/index.php?
ptl=materials/02history.htm.
8. Файкс Р., Нильсон Н. Система STRIPS - новый подход к применению методов доказательства теорем при решении задач / Пер. с англ. Под ред.
Поздняк Г.Е. -М.: Мир, 1973, -421 с.
116
9. Люгер Дж. Ф. Искусственный интеллект: стратегии и методы решения
сложных проблем, 4-е издание. / Пер. с англ. - М.: Издательский дом "Вильяме", 2003, - 864 с.
10. Нильсон Н. Принципы искусственного интеллекта / Пер. с англ.
Р.М.Абдусаматова, Ю.И.Крюкова. Под ред. В.Л. Стефанюка -М.: Радио и
связь, 1985, -376 с.
11. Введение в методы программных решений: учебное пособие / Е.В. Белкин, А.В. Гахов, А.М. Горбань, В.М. Куклин. В.М. Лазурик, А.С. Петренко, М.Ю.Силкин, В.В. Яновский. Под ред. проф. Куклина В.М. –Х.: ХНУ
им. В. Н. Каразина, 2010.
12. Рассел C., Норвинг П. Искусственный интеллект: современный подход, 2е издание. / Пер. с англ. К.А.Птицын. –М.: Издательский дом “Вильямс”,
2006, -1408 с.
13. Киселев А., Попов И., Заборовский Д. Интеллектуальная система управления роботом манипулятором, 2011.
14. Википедия: свободная электронная энциклопедия: на русском языке
[Пролог (язык программирования)] //
http://ru.wikipedia.org/wiki/Пролог_(язык_программирования)
15. Википедия: свободная электронная энциклопедия: на русском языке
[C++] // http://ru.wikipedia.org/wiki/C%2B%2B
16. C++ “От и до” – 2013 [Электронный ресурс] // http://cppp.ucoz.ru/
17. Мессенбок Х. Плюсы и минусы объектно-ориентированного программирования /-М.: ИнфоАрт, 1998.
18. Все обо всем. Роботы - будут ли служить в помощь человеку? – 2013
[Электронный ресурс] // http://upublic.ru/227-roboty-budut-li-sluzhit-vpomosch-cheloveku.html
19. Российское атомное сообщество. Сверхновая энергетика. Робот радиационной разведки – 2011 [Электронный ресурс] // http://www.atomicenergy.ru/technology/21887
117
ПРИЛОЖЕНИЕ 1
Федеральное государственное автономное образовательное учреждение
высшего профессионального образования
НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ
ВЫСШАЯ ШКОЛА ЭКОНОМИКИ
МОСКОВСКИЙ ИНСТИТУТ ЭЛЕКТРОНИКИ И МАТЕМАТИКИ
НАЦИОНАЛЬНОГО ИССЛЕДОВАТЕЛЬСКОГО УНИВЕРСИТЕТА
«ВЫСШАЯ ШКОЛА ЭКОНОМИКИ»
Кафедра "Вычислительные системы и сети" (ВСиС)
История искусственного интеллекта
«Реализация метода интеллектуального планирования
семейства STRIPS на языке С++»
МОСКВА
118
ИСТОРИЯ ИСКУССТВЕННОГО ИНТЕЛЛЕКТА
История искусственного интеллекта началась в древности с мифов,
рассказов и слухов о неживых существах, которые наделены интеллектом,
превосходящим человеческий.
Классические ученые также говорили про искусственный интеллект,
они пытались описать процесс человеческого мышления как механическую
манипуляцию символами. Результатом этой работы стало изобретение программируемого цифрового компьютера в 1940 году. Эта вычислительная машина была основана на абстрактной сущности математического рассуждения. Это устройство и идеи, лежащие в его основе, вдохновили группу ученых на то, чтобы начать всерьез обсуждать возможность создания электронного мозга.
Обзоры исследований в области искусственного интеллекта были изложены на конференции в кампусе колледжа Дартмут летом 1956 года. Те,
кто присутствовал там, были лидерами исследований в области искусственного интеллекта в течение десятилетия. Многие из них предсказывали, что
машина станет такой же умной, как человек и будет существовать дольше,
чем поколение человека. Ученые получили миллионы долларов, чтобы превратить эту мечту в реальность.
Несколько лет после конференции в Дартмуте были годами открытий.
Программы, которые были разработаны в это время для большинства людей,
были просто удивительны: компьютеры решали задачи по алгебре, доказывали теоремы в геометрии и научились говорить по-английски. Немногие в то
время верили, что такое «умное» поведение машины возможно. Исследователи были оптимистичны в своих предположениях, они предсказывали, что
полностью умная машина будет построена менее чем за 20 лет. Поэтому правительственные учреждения, такие как DARPA (англ. Defense Advanced
119
Research Projects Agency — агентство передовых оборонных исследовательских проектов), вкладывали деньги в новые области.
В конце 50-х и 1950-х годов было создано много успешных и появилось множество новых направлений. Среди них наиболее выдающимися были такие:
Поиск в пространстве состояний.
Многие ранние программы искусственного интеллекта использовали
основной алгоритм поиска. Для достижения некоторых целей (например, победа в игре или доказательство теоремы), они выполняли шаг за шагом (делая ходы вперед или назад), словно в поиске по лабиринту мы отступаем, когда заходим в тупик. Эта парадигма была названа «анализ средств и целей».
Основная трудность заключалась в том, что для многих задач, число
возможных путей хождения по лабиринту было просто колоссальным (эта
ситуация известна как «комбинаторный взрыв»). Исследователи сокращали
пространство поиска при помощи эвристики или грубого эмпирического метода, что позволило устранить те пути, которые вряд ли приведут к решению.
Алан Ньюэл и Герберт Саймон создали систему для решения задач под
названием «Универсальный решатель задач» (General Problem Solver —
GPS). Другим поисковым программам удалось достичь впечатляющих результатов, таких как решение проблем в геометрии и алгебре. Это были разработки Герберта Гелернтера «Геометрия доказательства теорем» (1958) и
SAINT, написанная студентом Джеймсом Слагленом под покровительством
американского ученого Марвином Ли Мински (1961). Другие программы искали решение через цели и подцели с построением плана действий. Например, программа STRIPS, разработанная в Стэнфордском Университете, чтобы контролировать поведение робота Шеки (Рис.1.1).
120
Рис.1.1 – Робот Шейки
Естественный язык.
Важной целью исследования в области искусственного интеллекта была, научить компьютеры общаться на естественном языке, за основу был взят
английский. Первым успехом была разработка Даниэлем Бобровым программы Студент, которая могла решать задачи высшей математики.
Семантическая сеть представляла собой понятия (например, «дом»,
«дверь») и отношения между понятиями (например, «имеет»), которые показывали связь между узлами (Рис.1.2). Первая программа искусственного интеллекта, которая использовала семантическую сеть, была написана Россом
Квиллиным, и самая успешная (и спорная) версия принадлежит Роджеру
Шенку, содержащая в себе теорию концептуальных зависимостей.
В 1966 году Джозеф Вейценбаум написал знаменитую компьютерную
программу Элиза (ELIZA), которая вступала в диалог с человеком при помощи техники активного слушания (Рис.1.3). Эти диалоги были настолько реалистичны, что пользователи иногда были введены в заблуждение, думая, что
они общались с человеком, а не с программой. Но, на самом деле Элиза не
имела понятия, о чем она говорит, она просто давала готовый ответ или по-
121
вторяла за человеком, немного перефразируя свой ответ с помощью нескольких правил грамматики. Элиза была первым виртуальным собеседником.
Рис.1.2 – Пример семантической сети
Рис.1.3 – Пример работы Элизы в Emacs
122
Микромиры.
В конце 60-х годов Марвин Ли Минский и Сеймур Пейперт из лаборатории Искусственного Интеллекта Массачусетского технологического института (англ. Massachusetts Institute of Technology, MIT) предложили провести исследования, направленные на создание простых ситуаций - микромиров. Они отметили, что в успешных науках таких, как физика, основные
принципы чаще всего лучше понимать с использованием упрощенных моделей, например, шарнир без трения и абсолютно твердые тела. Большая часть
исследований фокусировалась на «мире блоков», который состоял из цветных блоков различной формы и размеров, выстроенных на плоской поверхности (Рис.1.4).
Рис.1.4 – «Мир блоков»
Эта парадигма привела Джеральда Суссмана, возглавлявшего команду,
Адольфа Гусмана, Девида Вальца, Патрика Уинстона к инновации в области
Машинного зрения. В то же время Минский и Пейперт построили руки робота, которые могли укладывать блоки, в результате чего «мир блоков» был
применен к жизни. Завершающим достижением стала программа Терри Винограда SHRDLU, написанная на языках Micro Planner и Лисп на компьютере
DEC PDP-6 и использовавшая графический терминал DEC. Она могла общаться при помощи обычных выражений английского языка, могла по приказу составлять план для достижения цели и перемещать простые объекты в
упрощенном «мире блоков».
123
Оптимизм.
Первое поколение исследователей искусственного интеллекта сделали
следующие предсказания своей работе:
 1958 год, Герберт Саймон и Аллен Ньюэлл: «через 10 лет компьютер
победит человека – чемпиона мира по шахматам» и «через 10 лет компьютер
сможет доказать логико-математическую теорему».
 1965 год, Герберт Саймон: Через 20 лет машины смогут делать любую работу, которую делает человек».
 1967 год Марвин Ли Минский: «Через поколение … проблема создания искусственного интеллекта будет решена».
 1970 год, Марвин Ли Минский: «В периоде от трех до восьми лет будет создана машина с интеллектом среднестатистического человека».
Деньги.
В июне 1963 года Массачусетский технологический институт получил
2,2 млн. долларов от недавно созданного Агентства Передовых Исследовательских Проектов (позже известного как DARPA) (Рис.1.5). Эти деньги были потрачены на финансирование проекта MAC, который включал в себя
«Группу Искусственного интеллекта», основанную Минским и Маккарти пятью годами ранее. DARPA дала аналогичные гранты Ньюэллу и Саймону из
CMU (Университет Карнеги-Меллон, англ - Carnegie Mellon University) и
проекту Стэнфордской лаборатории искусственного интеллекта, основанной
Джоном Маккарти в 1963 году). Другая важная лаборатория была создана в
Эдинбургском университете Дональдом Мичи в 1965 году. Эти четыре учреждения будут оставаться основными центрами исследований в области искусственного интеллекта в течение многих лет.
124
Рис.1.5 – Логотип Агентство по перспективным оборонным научноисследовательским разработкам США (DARPA)
Деньги были даны с некоторыми обязательствами: Джозеф Карл Робнетт Ликлайдер, директор DARPA, считал, что его организация должна «финансировать людей, а не проекты», и позволил проводить исследования в
любых направлениях, которые бы могли их заинтересовать. Это создало
вольную атмосферу в Массачусетском технологическом институте, из-за чего появились первые хакеры.
Проблемы.
В 1970-х годах Искусственный интеллект подвергся критике и финансовым неудачам. Исследователи не смогли оценить сложность проблем, с которыми они столкнулись. Их огромный оптимизм вселил надежды в неподъемные задачи, и когда обещанные результаты не оправдались, финансирование прекратилось. В то же время поле коннекционизма (или нейронных сетей) было почти полностью закрыто в течение 10 лет Марвином Минским с
уничтожающей критикой перцептронов (математическая и компьютерная
модель восприятия информации мозгом - кибернетическая модель мозга)
(Рис.1.6). Несмотря на трудности с общественным восприятием искусственного интеллекта, в конце 70-х, были изучены новые идеи в логическом программировании, в рассуждениях на основе здравого смысла и во многих других областях.
В начале семидесятых годов, возможности программы искусственного
интеллекта были ограничены. Даже самые впечатляющие могли обрабаты125
вать только тривиальные проблемы, которые они, как предполагалось, решали, все программы были, в некотором смысле, "игрушки". Исследователи искусственного интеллекта начали работать с рядом фундаментальных ограничений, которые не могли быть преодолены в 1970-х годах. Хотя некоторые из
этих ограничений были решены в последующие десятилетия, но другие загоняют в тупик и по сей день.
Рис.1.6 - Логическая схема перцептрона с тремя выходами
 Ограниченная мощность компьютера: Была нехватка памяти или
скорости обработки, которая не давала достичь чего-то по-настоящему полезного. Например, успешная работа Росс Квилина на естественном языке
была продемонстрирована с лексикой только в двадцать слов, потому что
только такое малое количество могло поместиться в памяти. Ганс Моравец
утверждал, что в 1976 году компьютеры еще в миллионы раз слабы, чтобы
проявлять интеллект. Он предложил аналогию: искусственный интеллект
требует столько питания компьютера, сколько самолет требует лошадиных
сил. Ниже определенного порога это сделать невозможно, но, как только
126
мощность увеличится, то эту задачу можно будет с легкостью решить. Что
касается машинного зрения, Моравец оценил, что простая возможность обнаружения движения наравне с человеческой сетчаткой в режиме реального
времени потребует от компьютера способность совершать до 109 операций в
секунду (MIPS 1000). С 2011 года для практического применения компьютерного зрения требуется от 10000 до 1000000 MIPS. Для сравнения, самый
быстрый суперкомпьютер в 1976 году, Cray-1 (цена в розничной торговле от
5000000 до 8000000 долларов), обладал только около 80 до 130 MIPS
(Рис.1.7), и обычным настольным компьютером в то время было достигнуто
менее 1 MIPS.
 Комбинаторный взрыв. В 1972 году Ричард Карп на основе теоремы
Стивена Кука показал, что есть много проблем, которые, вероятно, могут
быть решены только в экспоненциальное время (в зависимости от количества
входов). Поиск оптимальных решений этих проблем занимает много времени
для компьютера, даже когда задача несложная. Это означает, что многие "игрушечные" решения, используемые искусственном интеллекте, вероятно, никогда не найдут использования в полезных системах.
Рис.1.7 - Cray-1 в Немецком музее в Мюнхене
127
 Повседневные знания. Много важных приложений искусственного
интеллекта, как зрение или естественный язык требует просто огромного количества информации о мире: программа должна иметь некоторое представление о том, что она видит или что она говорит. Требует, чтобы программа
хотя бы знала те вещи, которые знает ребенок. Исследователи вскоре обнаружили, что это было действительно огромное количество информации. Никто в 1970 году не мог создать базу данных настолько большую, и никто не
знал, как программа может узнать так много информации.
 Парадокс Моравека. Доказательство сложных теорем и решение
сложных задач геометрии относительно легко для компьютеров, но простая
задача, как опознавание лица или перемещение по комнате, не натыкаясь на
что-либо крайне сложно. Согласно Моравеку «относительно легко достичь
уровня взрослого человека в таких задачах как тест на интеллект или игре в
шашки, однако сложно или невозможно достичь навыков годовалого ребенка
в задачах восприятия или мобильности». Это помогло объяснить, почему исследования в машинном зрении и робототехнике достигли малого прогресса
к середине 1970-х годов.
 Проблема фреймов спецификаций. Исследователи искусственного
интеллекта, такие как Джон Маккарти, которые использовали логику, обнаружили, что они не могут представлять обычные выводы, которые участвуют
в планировании или рассуждениях по умолчанию без внесения изменений в
структуру самой логики. Они разработали новую логику (немонотонные логики и модальные логики), чтобы попытаться решить эти проблемы.
В 1973 году в ответ на критику Джеймса Лайтхилла и постоянного давления со съезда, США и Британское Правительство прекратили финансирование неориентированных исследований искусственного интеллекта. Семь
лет спустя, дальновидная инициатива Японского Правительства подтолкнуло
Правительство и промышленность к тому, чтобы обеспечить область искус128
ственного интеллекта миллиардом долларов, но к концу 80-х годов инвесторы разочаровались и снова прекратили финансирование. Этот цикл подъема
и спада заинтересованности в искусственном интеллекте продолжает до сих
пор преследовать область. Но все равно находятся ученые, которые, не боясь,
делают внеочередные прогнозы даже сейчас.
Прогресс в области искусственного интеллекта продолжается, несмотря на взлеты и падения своей репутации в глазах государственных чиновников и венчурных капиталистов. Проблемы, которые казались невозможными,
были решены в 1970 году и в настоящее время активно используются в
успешных коммерческих продуктах.
Тем не менее, ни одна машина не была построена на уровне, равном
человеческому интеллекту, вопреки оптимистическим прогнозам первого поколения исследователей искусственного интеллекта. [Википедия, История
искусственного интеллекта].
129
ПРИЛОЖЕНИЕ 2
Федеральное государственное автономное образовательное учреждение
высшего профессионального образования
НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ
ВЫСШАЯ ШКОЛА ЭКОНОМИКИ
МОСКОВСКИЙ ИНСТИТУТ ЭЛЕКТРОНИКИ И МАТЕМАТИКИ
НАЦИОНАЛЬНОГО ИССЛЕДОВАТЕЛЬСКОГО УНИВЕРСИТЕТА
«ВЫСШАЯ ШКОЛА ЭКОНОМИКИ»
Кафедра "Вычислительные системы и сети" (ВСиС)
Текст программы
«Реализация метода интеллектуального планирования
семейства STRIPS на языке С++»
МОСКВА
130
ТЕКСТ ПРОГРАММЫ
“Strips.cpp” - главный файл.
//Программа для дипломного проекта на тему:
//"Реализация метода интеллектуального планирования семейства STRIPS на языке С++"
//Автор: Березина К.А., гр. С-93
//Дата: 2013 год
#include "stdio.h"
#include "string.h"
#include "State.h"
#include "Files.h"
#include <iostream>
#include <fstream>
using namespace std;
int main(int argc, char *argv[])
//необходимо ввести имена входного и выходного файлов
//(и ключ)
{
setlocale( LC_ALL,"Russian" );
ofstream fl2;
//файл для записи
if (argc < 3)
//проверка количества введенных файлов
error("Необходимы 2 имени файла");
//argv[1] = "Input.txt";
//argv[2] = "Output.txt";
//argv[3] = "debug";
if (argv[3] == NULL)
argv[3] = "none";
if (strcmp(argv[3],"debug") == 0) //если введен ключ "debug"
{
fl2.open(argv[2], ios::out);
//открытие файла
fl2 << "Вывод промежуточных шагов системы планирования" << endl;
fl2.close();
//закрытие файла
}
TOList object;
//существующие объекты
TList state_now;
//текущее состояние
TRList rule;
//база правил
TList goal;
//список основных миницелей
ReadFile(argv[1],object,state_now,rule,goal);//чтение из файла
cout << "
Объекты:" << endl;
//вывод прочитанного из файла на экран
object.Print();
cout << "
Начальное состояние:" << endl;
state_now.Print();
cout << endl << "
Правила:";
rule.PrintState();
cout << endl << "
Главная цель:" << endl;
goal.Print();
int i=1;
TList stack_goal;
TRList plan;
TRelation *temp_goal;
TRule *temp_rule;
TRelation *temp_prec;
TRelation *temp_add;
TRelation *temp_trel;
TRule *temp_trul;
TCList cloud;
TObj *temp_obj;
TObj *temp_obj2;
//шаг итераций
//стек целей
//план действий робота
//указатель на элемент
//указатель на элемент
//указатель на элемент
//указатель на элемент
//указатель на элемент
//указатель на элемент
//стек облаков
//указатель на элемент
//указатель на элемент
цели
списка правил
списка предусловий
списка добавлений
отношений состояния и объекта
правила для объекта
списка существующих объектов
списка существующих объектов
131
while (!goal.MatchRel(state_now))
//пока цель и текущее состояние не совпадают
{
temp_goal = goal.head;
//заполнение стека главной целью
while (temp_goal != NULL)
{
stack_goal.Push(temp_goal->state.name_state,
temp_goal->obj1.name_obj, temp_goal->obj2.name_obj, temp_goal->pos.numb_pos);
stack_goal.head->pos.flag_pos = temp_goal->pos.flag_pos;
temp_goal = temp_goal->next;
}
while (stack_goal.head != NULL)
//пока стек целей не пуст
{
cout << endl << i << "----------------------------------------" << endl;
cout << "
Стек целей:" << endl;
stack_goal.Print();
cout << endl << "
Текущее состояние:" << endl;
state_now.Print();
cout << endl << "
План:" << endl;
plan.Print();
cout << "----------------------------------------" << endl;
if (strcmp(argv[3],"debug") == 0)
//если введен ключ "debug"
{
fl2.open(argv[2], ios::app);
//открытие файла
fl2 << endl << i << "----------------------------------------" << endl;
fl2 << "
Стек целей:" << endl;
fl2.close();
//закрытие файла
PrintTList(argv[2],stack_goal);
fl2.open(argv[2], ios::app);
//открытие файла
fl2 << endl << "
Текущее состояние:" << endl;
fl2.close();
//закрытие файла
PrintTList(argv[2],stack_goal);
fl2.open(argv[2], ios::app);
//открытие файла
fl2 << endl << "
План:" << endl;
fl2.close();
//закрытие файла
PrintTList(argv[2],stack_goal);
fl2.open(argv[2], ios::app);
//открытие файла
fl2 << "----------------------------------------" << endl;
fl2.close();
//закрытие файла
}
if (state_now.Find(stack_goal.head))
//если верхняя подцель соответствует
//текущему состоянию
{
do
{
stack_goal.Pop();
//удаляем верхнюю подцель
}
while ((stack_goal.head != NULL)
//пока верхняя подцель не пуста
&& state_now.Find(stack_goal.head));//и соответствует текущему состоянию
}
else
{
TRList ver_rule; //список вариантов подходящих правил
//ищем правила ver_rule, кот. содержат верхнюю подцель в списках добавлений
temp_rule = rule.r_head;
while (temp_rule != NULL)
{
temp_add = temp_rule->add.FindState(stack_goal.head);
if ((temp_add == NULL) || (temp_add->next != NULL))//голова стека целей
//найдена в списке добавлений
132
{
if (temp_add == NULL)
temp_add = temp_rule->add.head;
else
temp_add = temp_add->next;
ver_rule.Push(temp_rule->name_rule);
//добавляем только имя правила,
//без аргументов
ver_rule.r_head->sum_arg = temp_rule->sum_arg;
ver_rule.r_head->r_pos.flag_pos = temp_rule->r_pos.flag_pos;
//заполняем аргументы правила
if (temp_add->obj1.id == 1)
//1ый аргумент
ver_rule.r_head->r_obj1.name_obj = stack_goal.head->obj1.name_obj;
if (temp_add->obj2.id == 2)
//2ой аргумент
ver_rule.r_head->r_obj2.name_obj = stack_goal.head->obj2.name_obj;
if (temp_add->obj1.id == 2)
//2ой аргумент
ver_rule.r_head->r_obj2.name_obj = stack_goal.head->obj1.name_obj;
if (temp_add->obj2.id == 1)
//1ый аргумент
ver_rule.r_head->r_obj1.name_obj = stack_goal.head->obj2.name_obj;
//заполнение аргументов путем перебора и позиций
if ((ver_rule.r_head->sum_arg == 1) &&
(ver_rule.r_head->r_obj1.name_obj == NULL))
{
temp_obj = object.head;
while (temp_obj != NULL)
{
ver_rule.r_head->r_obj1.name_obj = temp_obj->name;
temp_obj = temp_obj->next;
if (temp_obj != NULL)
{
if (ver_rule.r_head->r_pos.flag_pos == true)
{
ver_rule.r_head->r_pos.numb_pos=stack_goal.head->pos.numb_pos;
if (ver_rule.r_head->r_pos.numb_pos == 0)
ver_rule.FindPos(state_now.head);
}
ver_rule.FillPA(temp_rule);
ver_rule.Push(temp_rule->name_rule);
ver_rule.r_head->r_pos.flag_pos = temp_rule->r_pos.flag_pos;
}
}
}
if ((ver_rule.r_head != NULL) && (ver_rule.r_head->sum_arg == 2)
&& (ver_rule.r_head->r_obj1.name_obj == NULL)
&& (ver_rule.r_head->r_obj2.name_obj == NULL))
{
/*ver_rule.Pop();*/
//если закомментировать нижнюю часть,
//то кол-во шагов уменьшится
temp_obj = object.head;
while (temp_obj != NULL)
{
ver_rule.r_head->r_obj1.name_obj = temp_obj->name;
temp_obj2 = object.head;
while (temp_obj2 != NULL)
{
if (strcmp(temp_obj2->name, ver_rule.r_head->r_obj1.name_obj)!=0)
ver_rule.r_head->r_obj2.name_obj = temp_obj2->name;
temp_obj2 = temp_obj2->next;
if ((temp_obj2!=NULL) && (ver_rule.r_head->r_obj2.name_obj!=NULL)
&&(strcmp(temp_obj2->name,ver_rule.r_head->r_obj1.name_obj)!=0))
{
if (ver_rule.r_head->r_pos.flag_pos == true)
133
{
ver_rule.r_head->r_pos.numb_pos =
stack_goal.head->pos.numb_pos;
if (ver_rule.r_head->r_pos.numb_pos == 0)
ver_rule.FindPos(state_now.head);
}
ver_rule.FillPA(temp_rule);
ver_rule.Push(temp_rule->name_rule,
ver_rule.r_head->r_obj1.name_obj);
ver_rule.r_head->sum_arg = ver_rule.r_head->r_next->sum_arg;
ver_rule.r_head->r_pos.flag_pos = temp_rule->r_pos.flag_pos;
}
}
temp_obj = temp_obj->next;
if (temp_obj != NULL)
{
if (ver_rule.r_head->r_pos.flag_pos == true)
{
ver_rule.r_head->r_pos.numb_pos=stack_goal.head->pos.numb_pos;
if (ver_rule.r_head->r_pos.numb_pos == 0)
ver_rule.FindPos(state_now.head);
}
ver_rule.FillPA(temp_rule);
ver_rule.Push(temp_rule->name_rule);
ver_rule.r_head->r_pos.flag_pos = temp_rule->r_pos.flag_pos;
}
}
}
if ((ver_rule.r_head != NULL) && (ver_rule.r_head->sum_arg == 2)
&& (ver_rule.r_head->r_obj1.name_obj == NULL))
{
temp_obj = object.head;
while (temp_obj != NULL)
{
if (strcmp(temp_obj->name, ver_rule.r_head->r_obj2.name_obj) != 0)
ver_rule.r_head->r_obj1.name_obj = temp_obj->name;
temp_obj = temp_obj->next;
if ((temp_obj != NULL) && (ver_rule.r_head->r_obj1.name_obj!=NULL)
&& (strcmp(temp_obj->name, ver_rule.r_head->r_obj2.name_obj)!=0))
{
if (ver_rule.r_head->r_pos.flag_pos == true)
{
ver_rule.r_head->r_pos.numb_pos=stack_goal.head->pos.numb_pos;
if (ver_rule.r_head->r_pos.numb_pos == 0)
ver_rule.FindPos(state_now.head);
}
ver_rule.FillPA(temp_rule);
ver_rule.Push(temp_rule->name_rule);
ver_rule.r_head->r_obj2.name_obj =
ver_rule.r_head->r_next->r_obj2.name_obj;
ver_rule.r_head->sum_arg = ver_rule.r_head->r_next->sum_arg;
ver_rule.r_head->r_pos.flag_pos = temp_rule->r_pos.flag_pos;
}
}
}
if ((ver_rule.r_head != NULL) && (ver_rule.r_head->sum_arg == 2)
&& (ver_rule.r_head->r_obj2.name_obj == NULL))
{
temp_obj = object.head;
while (temp_obj != NULL)
{
if (strcmp(temp_obj->name, ver_rule.r_head->r_obj1.name_obj) != 0)
134
ver_rule.r_head->r_obj2.name_obj = temp_obj->name;
temp_obj = temp_obj->next;
if ((temp_obj != NULL) && (ver_rule.r_head->r_obj2.name_obj!=NULL)
&& (strcmp(temp_obj->name, ver_rule.r_head->r_obj1.name_obj)!=0))
{
if (ver_rule.r_head->r_pos.flag_pos == true)
{
ver_rule.r_head->r_pos.numb_pos=stack_goal.head->pos.numb_pos;
if (ver_rule.r_head->r_pos.numb_pos == 0)
ver_rule.FindPos(state_now.head);
}
ver_rule.FillPA(temp_rule);
ver_rule.Push(temp_rule->name_rule,
ver_rule.r_head->r_obj1.name_obj);
ver_rule.r_head->sum_arg = ver_rule.r_head->r_next->sum_arg;
ver_rule.r_head->r_pos.flag_pos = temp_rule->r_pos.flag_pos;
}
}
}
if (ver_rule.r_head != NULL)
{
if (ver_rule.r_head->r_pos.flag_pos == true)
{
ver_rule.r_head->r_pos.numb_pos = stack_goal.head->pos.numb_pos;
if (ver_rule.r_head->r_pos.numb_pos == 0)
ver_rule.FindPos(state_now.head);
}
ver_rule.FillPA(temp_rule);
}
}
temp_rule = temp_rule->r_next;
}
//создаем облако
cloud.Push(state_now.head,ver_rule.r_head,stack_goal.head,plan.r_head);
cloud.c_head->suit_rule = ver_rule.r_head;
//проверяем, было ли такое облако раньше (совпадают состояния и миницель)
if (cloud.MatchCloud())
//если было
{
cloud.Pop();
//удаляем голову облака
while (cloud.c_head->rule.r_head->r_next == NULL)
cloud.Pop();
//удаляем облака до повторного
//очищаем и заменяем список текущего состояние на список, взятый из облака
state_now.Clean();
temp_trel = cloud.c_head->status.head;
while (temp_trel != NULL)
{
state_now.Push(temp_trel->state.name_state, temp_trel->obj1.name_obj,
temp_trel->obj2.name_obj, temp_trel->pos.numb_pos);
state_now.head->pos.flag_pos = temp_trel->pos.flag_pos;
temp_trel = temp_trel->next;
}
//выбираем следующее правило и заменяем подходящее правило
if (cloud.c_head->rule.CompareHead(cloud.c_head->suit_rule))
cloud.c_head->rule.Pop();
else
cloud.c_head->rule.PopTail();
ver_rule = cloud.c_head->rule;
cloud.c_head->suit_rule = ver_rule.r_head;
//очищаем и заменяем стек миницелей на стек, взятый из облака
stack_goal.Clean();
temp_trel = cloud.c_head->goals.head; //перевернутый стек
135
while (temp_trel != NULL)
{
stack_goal.Push(temp_trel->state.name_state, temp_trel->obj1.name_obj,
temp_trel->obj2.name_obj, temp_trel->pos.numb_pos);
stack_goal.head->pos.flag_pos = temp_trel->pos.flag_pos;
temp_trel = temp_trel->next;
}
//очищаем и заменяем стек плана действий на стек, взятый из облака
plan.Clean();
temp_trul = cloud.c_head->plan.r_head; //перевернутый стек
while (temp_trul != NULL)
{
plan.Push(temp_trul->name_rule, temp_trul->r_obj1.name_obj,
temp_trul->r_obj2.name_obj, temp_trul->r_pos.numb_pos);
if (temp_trul->r_pos.flag_pos == true)
plan.r_head->r_pos.flag_pos = temp_trul->r_pos.flag_pos;
temp_trul = temp_trul->r_next;
}
}
//Вывод подходящего правила
cout << "
Подходящее правило:" << endl;
cout << ver_rule.r_head->name_rule;
if (ver_rule.r_head->r_obj1.name_obj != NULL)
{
cout << "(" << ver_rule.r_head->r_obj1.name_obj;
if (ver_rule.r_head->r_obj2.name_obj != NULL)
cout << "," << ver_rule.r_head->r_obj2.name_obj;
if (ver_rule.r_head->r_pos.numb_pos != 0)
cout << "," << ver_rule.r_head->r_pos.numb_pos;
cout << ")";
}
cout << endl;
if (strcmp(argv[3],"debug") == 0)
//если введен ключ "debug"
{
fl2.open(argv[2], ios::app);
fl2 << "
Подходящее правило:" << endl;
fl2 << ver_rule.r_head->name_rule;
if (ver_rule.r_head->r_obj1.name_obj != NULL)
{
fl2 << "(" << ver_rule.r_head->r_obj1.name_obj;
if (ver_rule.r_head->r_obj2.name_obj != NULL)
fl2 << "," << ver_rule.r_head->r_obj2.name_obj;
if (ver_rule.r_head->r_pos.numb_pos != 0)
fl2 << "," << ver_rule.r_head->r_pos.numb_pos;
fl2 << ")";
}
fl2 << endl;
fl2.close();
//закрытие файла
}
if
{
(!ver_rule.r_head->prec.MatchRel(state_now))//если правило не применимо
//предусловие правила помещаем в стек целей
temp_prec = ver_rule.r_head->prec.head;
while (temp_prec != NULL)
{
stack_goal.Push(temp_prec->state.name_state,
temp_prec->obj1.name_obj, temp_prec->obj2.name_obj);
if (temp_prec->pos.flag_pos == true)
stack_goal.head->pos.flag_pos = temp_prec->pos.flag_pos;
temp_prec = temp_prec->next;
}
136
}
else
//если правило применимо
{
//выполняем действие:
temp_prec = ver_rule.r_head->prec.head; //удаляем из текущего состояния
//предусловие PREC
while (temp_prec != NULL)
{
state_now.Remove(temp_prec,state_now);
temp_prec = temp_prec->next;
}
temp_add = ver_rule.r_head->add.head;
//добавляем список добавления
//в текущее состояние
while (temp_add != NULL)
{
if ((temp_add->pos.flag_pos == true) &&
(ver_rule.r_head->r_pos.flag_pos == true))
{
state_now.Push(temp_add->state.name_state,temp_add->obj1.name_obj,
NULL, ver_rule.r_head->r_pos.numb_pos);
}
else
state_now.Push(temp_add->state.name_state,
temp_add->obj1.name_obj, temp_add->obj2.name_obj);
state_now.head->pos.flag_pos = temp_add->pos.flag_pos;
temp_add = temp_add->next;
}
//добавляем правило в план
plan.Push(ver_rule.r_head->name_rule, ver_rule.r_head->r_obj1.name_obj,
ver_rule.r_head->r_obj2.name_obj, ver_rule.r_head->r_pos.numb_pos);
plan.r_head->r_pos.flag_pos = ver_rule.r_head->r_pos.flag_pos;
cloud.c_head->plan_rule.Push(ver_rule.r_head->name_rule,
ver_rule.r_head->r_obj1.name_obj,ver_rule.r_head->r_obj2.name_obj,
ver_rule.r_head->r_pos.numb_pos);
cloud.c_head->plan_rule.r_head->r_pos.flag_pos =
ver_rule.r_head->r_pos.flag_pos;
}
i++;
}
}
}
if (cloud.c_head != NULL)
{
cout << endl << "
Оптимизированный план:" << endl;
cloud.UpgradePlan().Print();
WritePlanFile(argv[2],argv[3],cloud.UpgradePlan());
}
cin.get();
return 0;
//оптимизируем план
//вывод в файл
}
137
“State.h” – заголовочный файл состояний
#ifndef _STATE_H_
#define _STATE_H_
//Класс существующих объектов
class TObj
{
public:
char *name;
//имя объекта (кубика)
TObj *next;
//указатель на следующий элемент списка
TObj();
//конструктор
};
//Класс списков существующих объектов
class TOList
{
public:
TObj *head;
//первый элемент списка
int count;
//количество элементов списка
TOList();
//конструктор
void Push(char *c_name);
void Print();
};
//Класс состояний кубика/робота
class TState
{
friend class TRelation;
public:
char *name_state;
//имя состояния
}
//Класс объектов - кубики/робот
class TObject
{
friend class TRelation;
friend class TRule;
public:
char *name_obj;
//имя объекта (кубика)
int id;
//номер аргумента (0 - нет арг.)
};
//Класс позиций кубика
class TPosition
{
friend class TRelation;
public:
int numb_pos;
bool flag_pos;
};
//номер позиции
//наличие позиции
//Класс отношений состояния и объекта
class TRelation
{
friend class TRule;
friend class TList;
public:
TState state;
TObject obj1;
TObject obj2;
TPosition pos;
TRelation *next;
//указатель на следующий элемент списка
138
TRelation();
//конструктор
};
//Класс списков отношений
class TList
{
public:
TRelation *head;
//первый элемент списка
int count;
//количество элементов списка
TList();
//конструктор
//~TList();
//деструктор
void Push(char *c_state, char *c_obj1=NULL, char *c_obj2=NULL, int c_pos=0);
void PushRule(TRule *,char *c_state,char *c_obj1=NULL,char *c_obj2=NULL,int c_pos=0);
void Pop();
bool Find(TRelation *);
TRelation *FindList(TRelation *);
TRelation *FindState(TRelation *);
bool TList::MatchRel(TList);
bool FindCon(TRelation *);
TRelation *ViewTail();
void Remove(TRelation *, TList);
void Print();
void Clean();
};
//Класс правил для объекта
class TRule
{
public:
int id_rule;
//номер правила
char *name_rule;
//имя правила
TObject r_obj1;
TObject r_obj2;
TPosition r_pos;
int sum_arg;
//количество аргументов (0 - без арг.)
TList prec;
//список предусловий
TList add;
//список добавлений
TRule *r_next;
//указатель на следующий элемент списка
TRule();
//конструктор
void fill(char *c_rule, char *c_obj1=NULL, char *c_obj2=NULL);
};
//Класс списков правил
class TRList
{
public:
TRule *r_head;
//первый элемент списка
int r_count;
//количество элементов списка
TRList();
//конструктор
//~TRList();
//деструктор
void Push(char *c_rule, char *c_obj1=NULL, char *c_obj2=NULL, int c_pos=0);
void Pop();
void PopTail();
bool Find(TRule *);
bool CompareHead(TRule *);
void Print();
void PrintState();
void Clean();
void FillPA(TRule *);
void FindPos(TRelation *);
};
//Класс состояний внешнего мира (облако)
139
class TCloud
{
public:
int id_cloud;
TList status;
TRList rule;
TRule *suit_rule;
TRList plan_rule;
TList goals;
TRList plan;
TCloud *c_next;
TCloud();
};
//номер облака
//список состояний (описание облака)
//список подходящих правил
//подошедшее правило
//правило, вошедшее в план
//стек миницелей
//стек плана действий робота
//указатель на следующий элемент списка
//конструктор
//Класс списков облаков
class TCList
{
public:
TCloud *c_head;
//первый элемент списка
int c_count;
//количество элементов списка
TCList();
//конструктор
void Push(TRelation *, TRule *, TRelation *, TRule *);
void Pop();
bool MatchCloud();
TRList UpgradePlan();
};
#endif
140
“State.cpp” – файл состояний
#include
#include
#include
#include
<string.h>
<stdlib.h>
<iostream>
"State.h"
using namespace std;
char *newstr(char *s)
{
if (s==NULL)
return NULL;
char *p = new char[strlen(s)+1];
strcpy(p,s);
return p;
}
//Конструктор для класса существующих объектов
TObj::TObj ()
{
next = NULL;
}
//Конструктор для класса списка существующих объектов
TOList::TOList ()
{
count=0;
head = NULL;
}
//Функция добавления нового элемента в начало списка (на вершину стека)
void TOList::Push (char *c_name)
{
if (count == 0)
//список пуст
{
head = new TObj;
head->name = newstr(c_name);
}
else
{
TObj *new_node = new TObj;
new_node->name = newstr(c_name);
new_node->next = head;
head = new_node;
}
count++;
}
//Функция печати списка объектов
void TOList::Print()
{
TObj *temp = head;
while (temp != NULL)
{
cout << temp->name;
if (temp->next != NULL)
{
cout << ",";
}
temp = temp->next;
}
cout << endl << endl;
141
}
//Конструктор для класса отношений
TRelation::TRelation ()
{
next = NULL;
}
//Конструктор для класса списков отношений
TList::TList ()
{
count = 0;
head = NULL;
}
//Функция добавления нового элемента в начало списка (на вершину стека)
void TList::Push(char *c_state, char *c_obj1, char *c_obj2, int c_pos)
{
if (count == 0)
//список пуст
{
head = new TRelation;
head->state.name_state = newstr(c_state);
head->obj1.name_obj = newstr(c_obj1);
head->obj2.name_obj = newstr(c_obj2);
head->pos.numb_pos = c_pos;
}
else
{
TRelation *new_node = new TRelation;
new_node->state.name_state = newstr(c_state);
new_node->obj1.name_obj = newstr(c_obj1);
new_node->obj2.name_obj = newstr(c_obj2);
new_node->pos.numb_pos = c_pos;
new_node->next = head;
head = new_node;
}
count++;
}
//Функция добавления нового элемента правила в начало списка (предусловия, добавления)
void TList::PushRule(TRule *rul, char *c_state, char *c_obj1, char *c_obj2, int c_pos)
{
if (count == 0)
//список пуст
{
head = new TRelation;
head->state.name_state = newstr(c_state);
head->obj1.name_obj = newstr(c_obj1);
head->obj2.name_obj = newstr(c_obj2);
head->pos.numb_pos = c_pos;
}
else
{
TRelation *new_node = new TRelation;
new_node->state.name_state = newstr(c_state);
new_node->obj1.name_obj = newstr(c_obj1);
new_node->obj2.name_obj = newstr(c_obj2);
new_node->pos.numb_pos = c_pos;
new_node->next = head;
head = new_node;
}
if ((c_obj1 == NULL) && (c_obj2 == NULL))
//без аргументов
{
head->obj1.id = 0;
142
head->obj2.id = 0;
}
else
{
if ((c_obj1 != NULL) && (c_obj2 == NULL))
//один аргумент
{
if (strcmp(rul->r_obj1.name_obj, head->obj1.name_obj) == 0)
{
head->obj1.id = 1;
head->obj2.id = 0;
}
else if (strcmp(rul->r_obj2.name_obj, head->obj1.name_obj) == 0)
{
head->obj1.id = 2;
head->obj2.id = 0;
}
}
else if ((c_obj1 != NULL) && (c_obj2 != NULL)) //два аргумента
{
if ((strcmp(rul->r_obj1.name_obj, head->obj1.name_obj) == 0)
&& (strcmp(rul->r_obj2.name_obj, head->obj2.name_obj) == 0))
{
head->obj1.id = 1;
head->obj2.id = 2;
}
else if ((strcmp(rul->r_obj1.name_obj, head->obj2.name_obj) == 0)
&& (strcmp(rul->r_obj2.name_obj, head->obj1.name_obj) == 0))
{
head->obj1.id = 2;
head->obj2.id = 1;
}
}
}
count++;
}
//Функция удаления первого элемента списка (вершины стека)
void TList::Pop()
{
if (count != 0)
//список не пуст
{
TRelation *temp = head;
head = head->next;
delete temp;
count--;
}
}
//Функция проверяет вхождение элемента в данный список
//1 - элемент найден в списке, 0 - не найден
bool TList::Find(TRelation *rel)
{
TRelation *temp = head;
while (temp != NULL)
{
if (strcmp(temp->state.name_state, rel->state.name_state) == 0)
{
if ((temp->obj1.name_obj == NULL) && (rel->obj1.name_obj == NULL))//без арг-ов
{
return 1;
}
else
{
143
if ((temp->obj1.name_obj == NULL) || (rel->obj1.name_obj == NULL))
{
temp = temp->next;
}
else
{
if ((temp->obj2.name_obj == NULL) && (rel->obj2.name_obj == NULL))//1 арг-т
{
if (strcmp(temp->obj1.name_obj, rel->obj1.name_obj) == 0)
{
if ((rel->pos.flag_pos == true) && (rel->pos.numb_pos != 0)
&& (temp->pos.numb_pos != rel->pos.numb_pos))
{
temp = temp->next;
}
else
return 1;
}
else
{
temp = temp->next;
}
}
else
{
if ((temp->obj2.name_obj == NULL) || (rel->obj2.name_obj == NULL))
{
temp = temp->next;
}
else
{
if ((strcmp(temp->obj1.name_obj, rel->obj1.name_obj) == 0) &&//2 арг-та
(strcmp(temp->obj2.name_obj, rel->obj2.name_obj) == 0))
{
return 1;
}
else
{
temp = temp->next;
}
}
}
}
}
}
else
temp = temp->next;
}
return 0;
}
//Функция проверяет вхождение элемента в данный список,
//если элемент найден в списке - возвращает указатель на предшествующий эл-т списка,
//если элемент является головой - возвращает NULL,
//если элемент не найден - возвращает последний элемент списка
TRelation *TList::FindList(TRelation *rel)
{
TRelation *temp = head;
TRelation *prev;
prev = NULL;
while (temp != NULL)
{
144
if (strcmp(temp->state.name_state, rel->state.name_state) == 0)
{
if ((temp->obj1.name_obj == NULL) && (rel->obj1.name_obj == NULL))//без аргументов
{
return prev;
}
else
{
if ((temp->obj1.name_obj == NULL) || (rel->obj1.name_obj == NULL))
{
prev = temp;
temp = temp->next;
}
else
{
if ((temp->obj2.name_obj == NULL) && (rel->obj2.name_obj == NULL))
{
if (strcmp(temp->obj1.name_obj, rel->obj1.name_obj) == 0)
{
return prev;
}
else
{
prev = temp;
temp = temp->next;
}
}
else
{
if ((temp->obj2.name_obj == NULL) || (rel->obj2.name_obj == NULL))
{
prev = temp;
temp = temp->next;
}
else
{
if ((strcmp(temp->obj1.name_obj, rel->obj1.name_obj) == 0) &&
(strcmp(temp->obj2.name_obj, rel->obj2.name_obj) == 0))
{
return prev;
}
else
{
prev = temp;
temp = temp->next;
}
}
}
}
}
}
else
{
prev = temp;
temp = temp->next;
}
}
return prev;
}
//Функция проверяет вхождение элемента (без учета аргументов) в данный список,
145
//если элемент найден в списке - возвращает указатель на предшествующий эл-т списка,
//если элемент является головой - возвращает NULL,
//если элемент не найден - возвращает последний элемент списка
TRelation *TList::FindState(TRelation *rel)
{
TRelation *temp = head;
TRelation *prev;
prev = NULL;
while (temp != NULL)
{
if (strcmp(temp->state.name_state, rel->state.name_state) == 0)
{
return prev;
}
else
{
prev = temp;
temp = temp->next;
}
}
return prev;
}
//Функция поиска соответствия двух списков отношений
//0 - второй список не включает в себя первый, 1 - включает
bool TList::MatchRel(TList list)
{
TRelation *temp = head;
while (temp != NULL)
{
if (!list.Find(temp))
return 0;
temp = temp->next;
}
return 1;
}
//Функция поиска совпадения конца списка и элемента
//1 - элементы совпадают, 0 - не совпадают
bool TList::FindCon(TRelation *rel)
{
TRelation *tail = head;
while (tail->next != NULL)
tail = tail->next;
if ((tail != NULL) && (strcmp(tail->state.name_state, rel->state.name_state) == 0))
{
if ((tail->obj1.name_obj == NULL) && (rel->obj1.name_obj == NULL))//без аргументов
return 1;
else
{
if ((tail->obj2.name_obj == NULL) && (rel->obj2.name_obj == NULL) &&
(strcmp(tail->obj1.name_obj, rel->obj1.name_obj) == 0))
return 1;
else if ((strcmp(tail->obj1.name_obj, rel->obj1.name_obj) == 0) &&
(strcmp(tail->obj2.name_obj, rel->obj2.name_obj) == 0))
return 1;
}
}
return 0;
}
//Функция нахождения конца стека
146
TRelation *TList::ViewTail()
{
TRelation *temp = head;
while (temp->next != NULL)
{
temp = temp->next;
}
return temp;
}
//Функция удаления заданного элемента из списка
void TList::Remove(TRelation *del, TList list)
{
TRelation *temp;
temp = list.FindList(del);
if (temp == NULL)
Pop();
else
{
if (temp->next != NULL)
{
TRelation *buf = temp->next;
buf = buf->next;
delete temp->next;
temp->next = buf;
count--;
}
}
}
//Функция печати списка отношений
void TList::Print()
{
TRelation *temp = head;
while (temp != NULL)
{
cout << temp->state.name_state;
if (temp->obj1.name_obj != NULL)
{
cout << "(" << temp->obj1.name_obj;
if (temp->obj2.name_obj != NULL)
cout << "," << temp->obj2.name_obj;
if (temp->pos.numb_pos != 0)
cout << "," << temp->pos.numb_pos;
cout << ")";
}
cout << endl;
temp = temp->next;
}
}
//Функция очищения списка
void TList::Clean()
{
while (count != 0)
//список не пуст
{
Pop();
}
}
//Конструктор для класса правил
147
TRule::TRule ()
{
r_pos.flag_pos = false;
r_next = NULL;
}
//Конструктор для класса списков правил
TRList::TRList ()
{
r_count = 0;
r_head = NULL;
}
//Функция добавления нового элемента в начало списка (на вершину стека)
void TRList::Push(char *c_rule, char *c_obj1, char *c_obj2, int c_pos)
{
if (r_count == 0)
//список пуст
{
r_head = new TRule;
r_head->name_rule = newstr(c_rule);
r_head->r_obj1.name_obj = newstr(c_obj1);
r_head->r_obj2.name_obj = newstr(c_obj2);
r_head->r_pos.numb_pos = c_pos;
}
else
{
TRule *r_new_node = new TRule;
r_new_node->name_rule = newstr(c_rule);
r_new_node->r_obj1.name_obj = newstr(c_obj1);
r_new_node->r_obj2.name_obj = newstr(c_obj2);
r_new_node->r_pos.numb_pos = c_pos;
r_new_node->r_next = r_head;
r_head = r_new_node;
}
if ((c_obj1 == NULL) && (c_obj2 == NULL)) //без аргументов
r_head->sum_arg = 0;
else if ((c_obj1 == NULL) || (c_obj2 == NULL))
r_head->sum_arg = 1;
else
r_head->sum_arg = 2;
r_count++;
}
//Функция удаления первого элемента списка (вершины стека)
void TRList::Pop()
{
if (r_count != 0)
//список не пуст
{
TRule *r_temp = r_head;
r_head = r_head->r_next;
delete r_temp;
r_count--;
}
}
//Функция удаления последнего элемента списка (хвоста стека)
void TRList::PopTail()
{
if (r_count != 0)
//список не пуст
{
TRule *r_temp = r_head;
TRule *r_tail = r_head->r_next;
if (r_tail == NULL)
//один элемент в списке
148
{
r_head = NULL;
r_tail = r_temp;
}
else
{
while ((r_tail != NULL) && (r_tail->r_next != NULL))
{
r_temp = r_tail;
r_tail = r_tail->r_next;
}
r_temp->r_next = NULL;
}
delete r_tail;
r_count--;
}
}
//Функция проверки вхождения элемента в данный список
//1 - элемент найден в списке, 0 - не найден
bool TRList::Find(TRule *rel)
{
TRule *temp = r_head;
while (temp != NULL)
{
if (strcmp(temp->name_rule, rel->name_rule) == 0)
{
if ((temp->r_obj1.name_obj == NULL) && (rel->r_obj1.name_obj == NULL))//без арг.
{
return 1;
}
else
{
if ((temp->r_obj1.name_obj == NULL) || (rel->r_obj1.name_obj == NULL))
{
temp = temp->r_next;
}
else
{
if ((temp->r_obj2.name_obj == NULL) && (rel->r_obj2.name_obj == NULL))
{
if (strcmp(temp->r_obj1.name_obj, rel->r_obj1.name_obj) == 0)
{
return 1;
}
else
{
temp = temp->r_next;
}
}
else
{
if ((temp->r_obj2.name_obj == NULL) || (rel->r_obj2.name_obj == NULL))
{
temp = temp->r_next;
}
else
{
if ((strcmp(temp->r_obj1.name_obj, rel->r_obj1.name_obj) == 0) &&
(strcmp(temp->r_obj2.name_obj, rel->r_obj2.name_obj) == 0))
{
return 1;
}
149
else
{
temp = temp->r_next;
}
}
}
}
}
}
else
temp = temp->r_next;
}
return 0;
}
//Функция поиска совпадения головы списка и элемента
//1 - элементы совпадают, 0 - не совпадают
bool TRList::CompareHead(TRule *rul)
{
if ((r_head != NULL) && (strcmp(r_head->name_rule, rul->name_rule) == 0))
{
if ((r_head->r_obj1.name_obj == NULL) && (rul->r_obj1.name_obj == NULL))//без арг.
return 1;
else
{
if ((r_head->r_obj2.name_obj == NULL) && (rul->r_obj2.name_obj == NULL) &&
(strcmp(r_head->r_obj1.name_obj, rul->r_obj1.name_obj) == 0))
return 1;
else if ((strcmp(r_head->r_obj1.name_obj, rul->r_obj1.name_obj) == 0) &&
(strcmp(r_head->r_obj2.name_obj, rul->r_obj2.name_obj) == 0))
return 1;
}
}
return 0;
}
//Функция печати списка правил
void TRList::Print()
{
TRule *temp = r_head;
while (temp != NULL)
{
cout << temp->name_rule;
if (temp->r_obj1.name_obj != NULL)
{
cout << "(" << temp->r_obj1.name_obj;
if (temp->r_obj2.name_obj != NULL)
cout << "," << temp->r_obj2.name_obj;
if (temp->r_pos.numb_pos != 0)
cout << "," << temp->r_pos.numb_pos;
cout << ")";
}
cout << endl;
temp = temp->r_next;
}
}
//Функция печати списка правил со списками предусловий и добавлений
void TRList::PrintState()
{
TRule *temp = r_head;
while (temp != NULL)
{
150
cout << endl << " " << temp->name_rule;
if (temp->r_obj1.name_obj != NULL)
{
cout << "(" << temp->r_obj1.name_obj;
if (temp->r_obj2.name_obj != NULL)
cout << "," << temp->r_obj2.name_obj;
if (temp->r_pos.numb_pos != 0)
cout << "," << temp->r_pos.numb_pos;
cout << ")";
}
cout << endl;
cout << " Предусловия:" << endl;
temp->prec.Print();
cout << " Добавления:" << endl;
temp->add.Print();
temp = temp->r_next;
}
}
//Функция очищения списка
void TRList::Clean()
{
while (r_count != 0)
{
Pop();
}
}
//список не пуст
//Функция заполнения списков предусловия и добавления правила
void TRList::FillPA(TRule *rul)
{
TRule *fill_rule = r_head;
TRelation *temp_p = rul->prec.head;
TRelation *temp_a = rul->add.head;
while (temp_p != NULL) //заполняем список предусловия
{
if ((temp_p->obj1.id == 0) && (temp_p->obj2.id == 0))
fill_rule->prec.Push(temp_p->state.name_state);
else if ((temp_p->obj1.id == 1) && (temp_p->obj2.id == 0))
{
fill_rule->prec.Push(temp_p->state.name_state,r_head->r_obj1.name_obj,NULL);
}
else if ((temp_p->obj1.id == 2) && (temp_p->obj2.id == 0))
{
fill_rule->prec.Push(temp_p->state.name_state,r_head->r_obj2.name_obj,NULL);
}
else if ((temp_p->obj1.id == 1) && (temp_p->obj2.id == 2))
fill_rule->prec.Push(temp_p->state.name_state,r_head->r_obj1.name_obj,
r_head->r_obj2.name_obj);
else if ((temp_p->obj1.id == 2) && (temp_p->obj2.id == 1))
fill_rule->prec.Push(temp_p->state.name_state,r_head->r_obj2.name_obj,
r_head->r_obj1.name_obj);
fill_rule->prec.head->pos.flag_pos = temp_p->pos.flag_pos;
temp_p = temp_p->next;
}
while (temp_a != NULL) //заполняем список добавления
{
if ((temp_a->obj1.id == 0) && (temp_a->obj2.id == 0))
fill_rule->add.Push(temp_a->state.name_state);
else if ((temp_a->obj1.id == 1) && (temp_a->obj2.id == 0))
{
if (temp_a->pos.flag_pos == true)
151
{
fill_rule->add.Push(temp_a->state.name_state,r_head->r_obj1.name_obj,NULL,
r_head->r_pos.numb_pos);
}
else
fill_rule->add.Push(temp_a->state.name_state,r_head->r_obj1.name_obj,NULL);
}
else if ((temp_a->obj1.id == 2) && (temp_a->obj2.id == 0))
{
if (temp_a->pos.flag_pos == true)
{
fill_rule->add.Push(temp_a->state.name_state,r_head->r_obj2.name_obj,NULL,
r_head->r_pos.numb_pos);
}
else
fill_rule->add.Push(temp_a->state.name_state,r_head->r_obj2.name_obj,NULL);
}
else if ((temp_a->obj1.id == 1) && (temp_a->obj2.id == 2))
fill_rule->add.Push(temp_a->state.name_state,r_head->r_obj1.name_obj,
r_head->r_obj2.name_obj);
else if ((temp_a->obj1.id == 2) && (temp_a->obj2.id == 1))
fill_rule->add.Push(temp_a->state.name_state,r_head->r_obj2.name_obj,
r_head->r_obj1.name_obj);
fill_rule->add.head->pos.flag_pos = temp_a->pos.flag_pos;
temp_a = temp_a->next;
}
}
//Функция поиска свободной позиции (без лежащего объекта)
//для правила вершины списка, нуждающегося в задании позиции
void TRList::FindPos(TRelation *state_head)
{
TRelation *temp_state = state_head;
int numb = 1;
//номер позиции
while (temp_state != NULL)
{
if (temp_state->pos.numb_pos == numb)
{
numb++;
temp_state = state_head;
}
else
temp_state = temp_state->next;
}
r_head->r_pos.numb_pos = numb;
}
//Вспомогательная функция для заполнения подходящих правил вручную
void TRule::fill(char *c_rule, char *c_obj1, char *c_obj2)
{
name_rule = newstr(c_rule);
r_obj1.name_obj = newstr(c_obj1);
r_obj2.name_obj = newstr(c_obj2);
}
//Конструктор для класса состояний внешнего мира (облако)
TCloud::TCloud ()
{
id_cloud = 0;
c_next = NULL;
}
152
//Конструктор для класса списков облаков
TCList::TCList ()
{
c_count = 0;
c_head = NULL;
}
//Функция добавления нового облака в начало списка (на вершину стека)
void TCList::Push (TRelation *head_status, TRule *head_rule, TRelation *head_goals, TRule
*head_plan)
{
if (c_count == 0)
//список пуст
{
c_head = new TCloud;
}
else
{
TCloud *new_node = new TCloud;
new_node->c_next = c_head;
c_head = new_node;
}
//заполнение списка состояний облака
TRelation *temp_status = head_status;
while (temp_status != NULL)
{
c_head->status.Push(temp_status->state.name_state,
temp_status->obj1.name_obj,temp_status->obj2.name_obj,temp_status->pos.numb_pos);
c_head->status.head->pos.flag_pos = temp_status->pos.flag_pos;
temp_status = temp_status->next;
}
//заполнение списка подходящих правил облака
TRule *temp_rule = head_rule;
TRelation *temp_p;
TRelation *temp_a;
while (temp_rule != NULL)
{
c_head->rule.Push(temp_rule->name_rule,
temp_rule->r_obj1.name_obj,temp_rule->r_obj2.name_obj,temp_rule->r_pos.numb_pos);
c_head->rule.r_head->r_pos.flag_pos = temp_rule->r_pos.flag_pos;
temp_p = temp_rule->prec.head;
while (temp_p != NULL) //заполняем список предусловия
{
c_head->rule.r_head->prec.Push(temp_p->state.name_state,
temp_p->obj1.name_obj, temp_p->obj2.name_obj, temp_p->pos.numb_pos);
c_head->rule.r_head->prec.head->pos.flag_pos = temp_p->pos.flag_pos;
temp_p = temp_p->next;
}
temp_a = temp_rule->add.head;
while (temp_a != NULL) //заполняем список добавления
{
c_head->rule.r_head->add.Push(temp_a->state.name_state,
temp_a->obj1.name_obj, temp_a->obj2.name_obj, temp_a->pos.numb_pos);
c_head->rule.r_head->add.head->pos.flag_pos = temp_a->pos.flag_pos;
temp_a = temp_a->next;
}
temp_rule = temp_rule->r_next;
}
//заполнение стека миницелей, перевернутый стек
TRelation *temp_goals = head_goals;
while (temp_goals != NULL)
{
c_head->goals.Push(temp_goals->state.name_state,
temp_goals->obj1.name_obj, temp_goals->obj2.name_obj, temp_goals->pos.numb_pos);
153
c_head->goals.head->pos.flag_pos = temp_goals->pos.flag_pos;
temp_goals = temp_goals->next;
}
//заполнение стека плана действий робота, перевернутый стек
TRule *temp_plan = head_plan;
while (temp_plan != NULL)
{
c_head->plan.Push(temp_plan->name_rule,
temp_plan->r_obj1.name_obj,temp_plan->r_obj2.name_obj,temp_plan->r_pos.numb_pos);
c_head->plan.r_head->r_pos.flag_pos = temp_plan->r_pos.flag_pos;
temp_plan = temp_plan->r_next;
}
c_count++;
c_head->id_cloud = c_count;
//c_head->c_inplan = inplan;
}
//Функция удаления головы списка
void TCList::Pop()
{
if (c_count != 0)
{
TCloud *c_temp = c_head;
c_head = c_head->c_next;
delete c_temp;
c_count--;
}
}
//Функция поиска одинаковых облаков
//0 - голова списка облаков не совпадает ни с одним облаком,
//N - совпадает с облаком под номером N
bool TCList::MatchCloud()
{
TCloud *temp = c_head->c_next;
while (temp != NULL)
{
if (temp->status.MatchRel(c_head->status) && //совпадают состояния
(temp->goals.FindCon(c_head->goals.ViewTail())))//и миницель
return true;
temp = temp->c_next;
}
return false;
}
//Функция оптимизации плана (удаление ненужных действий из плана)
TRList TCList::UpgradePlan()
{
TRList upplan;
//оптимизированный план
TCloud *temp_cloud = c_head;
//выбранное облако
TCloud *temp;
while (temp_cloud != NULL)
{
if (temp_cloud->plan_rule.r_head != NULL) //правило входит в план
{
temp = temp_cloud->c_next;
upplan.Push(temp_cloud->plan_rule.r_head->name_rule,
temp_cloud->plan_rule.r_head->r_obj1.name_obj,
temp_cloud->plan_rule.r_head->r_obj2.name_obj,
temp_cloud->plan_rule.r_head->r_pos.numb_pos);
upplan.r_head->r_pos.flag_pos = temp_cloud->plan_rule.r_head->r_pos.flag_pos;
while (temp != NULL)
//сравнение состояний след. облаков
154
{
//если состояния облаков, входящих в план, совпадают
if ((temp->plan_rule.r_head != NULL) &&
(temp->status.MatchRel(temp_cloud->status)))
{
temp_cloud = temp;
//перейти на облако temp
temp = NULL;
}
else
temp = temp->c_next;
}
}
temp_cloud = temp_cloud->c_next;
}
return upplan;
}
/*//Деструктор для класса списков отношений
TList::~TList()
{
TRelation *delNode = head;
TRelation *temp;
while (delNode != NULL)
{
temp = delNode->next;
delete delNode;
delNode = temp;
}
}
//Деструктор для класса списков правил
TRList::~TRList()
{
TRule *delNode = r_head;
TRule *temp;
while (delNode != NULL)
{
temp = delNode->r_next;
delete delNode;
delNode = temp;
}
}*/
155
“Files.h” – заголовочный файл работы с файлами
#ifndef _FILES_H_
#define _FILES_H_
#include "State.h"
//функция выдачи ошибки и завершения работы
void error(const char *fmt, ...);
//Функция чтения из файла
void ReadFile(char *,TOList &,TList &,TRList &,TList &);
//Функция печати списка отношений в файл
void PrintTList(char *,TList);
//Функция печати списка правил в файл
void PrintTRList(char *,TRList);
//Функция записи в файл
void WritePlanFile(char *,char *,TRList);
#endif
156
“Files.cpp” – файл работы с файлами
#include
#include
#include
#include
#include
#include
<string.h>
<stdlib.h>
<stdarg.h>
<iostream>
<fstream>
"Files.h"
using namespace std;
#define MAX_LENGHT_STR 50
//количество символов в строке входного файла
//Функция выдачи ошибки и завершения работы
void error(const char *fmt, ...)
{
char s[255];
va_list argptr;
va_start(argptr, fmt);
vsprintf(s, fmt, argptr);
va_end(argptr);
puts(s);
cin.get();
exit(1);
}
//Функция чтения из файла
void ReadFile(char *name_file,TOList &object,TList &state_now,TRList &rule,TList &goal)
{
ifstream fl1;
//файл для чтения
char *str = new char[MAX_LENGHT_STR]; //указатель на строку файла
char *text;
//указатель на лексему строки
int numb;
//номер ключевого слова
char *obj=NULL,*obj1=NULL,*obj2=NULL; //указатели на имена объектов
char *strpos=NULL;
//указатель на строку с позицией
bool flag;
//наличие позиции
int pos;
//номер позиции кубика
char *name;
//указатель на имена состояний
fl1.open(name_file, ios::in); //открытие файла
if (fl1.fail())
//файл не найден
error("Файл '%s' не найден",name_file);
while (!fl1.eof())
//пока не конец файла
{
flag = false;
fl1.getline(str,MAX_LENGHT_STR); //считаем строку в файле
text = strtok(str," (,)\t");
//возвращ. указатель на лексему
if (text != NULL)
{
if (strcmp(text,"OBJECTS") == 0)
{
numb = 1;
fl1.getline(str,MAX_LENGHT_STR);
}
else if (strcmp(text,"STATE") == 0)
{
numb = 2;
fl1.getline(str,MAX_LENGHT_STR);
text = strtok(str," (,)\t");
}
else if (strcmp(text,"RULE") == 0)
{
numb = 3;
text = strtok(NULL," (,)\t");
}
157
else if (strcmp(text,"PREC") == 0)
{
numb = 4;
text = strtok(NULL," (,)\t");
}
else if (strcmp(text,"ADD") == 0)
{
numb = 5;
text = strtok(NULL," (,)\t");
}
else if (strcmp(text,"GOAL") == 0)
{
numb = 6;
fl1.getline(str,MAX_LENGHT_STR);
text = strtok(str," (,)\t");
}
switch (numb)
{
case 1:
obj = strtok(str," (,)\t");
while (obj != NULL)
{
object.Push(obj);
obj = strtok(NULL, " (,)\t");
}
break;
case 2:
name = text;
pos = 0;
strpos = strtok(NULL," (,)\t");
if ((strpos != NULL) && (atoi(strpos)
{
pos = atoi(strpos);
flag = true;
}
else
obj1 = strpos;
strpos = strtok(NULL," (,)\t");
if ((strpos != NULL) && (atoi(strpos)
{
pos = atoi(strpos);
flag = true;
}
else
obj2 = strpos;
state_now.Push(name,obj1,obj2,pos);
state_now.head->pos.flag_pos = flag;
break;
case 3:
name = text;
pos = 0;
strpos = strtok(NULL," (,)\t");
if ((strpos != NULL) && (atoi(strpos)
{
flag = true;
}
else
obj1 = strpos;
strpos = strtok(NULL," (,)\t");
if ((strpos != NULL) && (atoi(strpos)
{
flag = true;
}
!= 0))
!= 0))
!= 0))
!= 0))
158
else
obj2 = strpos;
rule.Push(name,obj1,obj2,pos);
rule.r_head->r_pos.flag_pos = flag;
break;
case 4:
name = text;
pos = 0;
strpos = strtok(NULL," (,)\t");
if ((strpos != NULL) && (atoi(strpos) != 0))
{
flag = true;
}
else
obj1 = strpos;
strpos = strtok(NULL," (,)\t");
if ((strpos != NULL) && (atoi(strpos) != 0))
{
flag = true;
}
else
obj2 = strpos;
rule.r_head->prec.PushRule(rule.r_head,name,obj1,obj2,pos);
rule.r_head->prec.head->pos.flag_pos = flag;
break;
case 5:
name = text;
pos = 0;
strpos = strtok(NULL," (,)\t");
if ((strpos != NULL) && (atoi(strpos) != 0))
{
flag = true;
}
else
obj1 = strpos;
strpos = strtok(NULL," (,)\t");
if ((strpos != NULL) && (atoi(strpos) != 0))
{
flag = true;
}
else
obj2 = strpos;
rule.r_head->add.PushRule(rule.r_head,name,obj1,obj2,pos);
rule.r_head->add.head->pos.flag_pos = flag;
break;
case 6:
name = text;
pos = 0;
strpos = strtok(NULL," (,)\t");
if ((strpos != NULL) && (atoi(strpos) != 0))
{
pos = atoi(strpos);
flag = true;
}
else
obj1 = strpos;
strpos = strtok(NULL," (,)\t");
if ((strpos != NULL) && (atoi(strpos) != 0))
{
pos = atoi(strpos);
flag = true;
159
}
else
obj2 = strpos;
goal.Push(name,obj1,obj2,pos);
goal.head->pos.flag_pos = flag;
break;
}
if ((obj1 != NULL) && (obj2 != NULL) && (strcmp(obj1,obj2) == 0))
error("Аргументы не могут быть одинаковыми");
}
}
fl1.close();
//закрытие файла
}
//Функция печати списка отношений в файл
void PrintTList(char *name_file,TList rel)
{
ofstream fl2;
//файл для записи
fl2.open(name_file, ios::app);
//открытие файла
TRelation *temp = rel.head;
while (temp != NULL)
{
fl2 << temp->state.name_state;
if (temp->obj1.name_obj != NULL)
{
fl2 << "(" << temp->obj1.name_obj;
if (temp->obj2.name_obj != NULL)
fl2 << "," << temp->obj2.name_obj;
if (temp->pos.numb_pos != 0)
fl2 << "," << temp->pos.numb_pos;
fl2 << ")";
}
fl2 << endl;
temp = temp->next;
}
fl2.close();
//закрытие файла
}
//Функция печати списка правил в файл
void PrintTRList(char *name_file,TRList rul)
{
ofstream fl2;
//файл для записи
fl2.open(name_file, ios::app);
//открытие файла
TRule *temp = rul.r_head;
while (temp != NULL)
{
fl2 << temp->name_rule;
if (temp->r_obj1.name_obj != NULL)
{
fl2 << "(" << temp->r_obj1.name_obj;
if (temp->r_obj2.name_obj != NULL)
fl2 << "," << temp->r_obj2.name_obj;
if (temp->r_pos.numb_pos != 0)
fl2 << "," << temp->r_pos.numb_pos;
fl2 << ")";
}
fl2 << endl;
temp = temp->r_next;
}
fl2.close();
//закрытие файла
}
//Функция записи в файл
160
void WritePlanFile(char *name_file,char *key,TRList plan)
{
ofstream fl2;
//файл для записи
if (strcmp(key,"debug") == 0)
//если введен ключ "debug"
{
fl2.open(name_file, ios::app);
fl2 << endl << "
Оптимизированный план:" << endl;
}
else
{
fl2.open(name_file, ios::out);
fl2 << "
План:" << endl;
}
TRule *temp = plan.r_head;
while (temp != NULL)
//вывод плана
{
fl2 << temp->name_rule;
if (temp->r_obj1.name_obj != NULL)
{
fl2 << "(" << temp->r_obj1.name_obj;
if (temp->r_obj2.name_obj != NULL)
fl2 << "," << temp->r_obj2.name_obj;
if (temp->r_pos.numb_pos != 0)
fl2 << "," << temp->r_pos.numb_pos;
fl2 << ")";
}
fl2 << endl;
temp = temp->r_next;
}
fl2.close();
//закрытие файла
}
161
ПРИЛОЖЕНИЕ 3
Федеральное государственное автономное образовательное учреждение
высшего профессионального образования
НАЦИОНАЛЬНЫЙ ИССЛЕДОВАТЕЛЬСКИЙ УНИВЕРСИТЕТ
ВЫСШАЯ ШКОЛА ЭКОНОМИКИ
МОСКОВСКИЙ ИНСТИТУТ ЭЛЕКТРОНИКИ И МАТЕМАТИКИ
НАЦИОНАЛЬНОГО ИССЛЕДОВАТЕЛЬСКОГО УНИВЕРСИТЕТА
«ВЫСШАЯ ШКОЛА ЭКОНОМИКИ»
Кафедра "Вычислительные системы и сети" (ВСиС)
Графические материалы
«Реализация метода интеллектуального планирования
семейства STRIPS на языке С++»
МОСКВА
162
Скачать