Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lections 2007.doc
Скачиваний:
3
Добавлен:
01.03.2025
Размер:
841.22 Кб
Скачать
  1. Рекурсивный поиск

  1. О реализации поиска в пространстве состояний

Ранее были рассмотрены различные методы поиска в пространстве состо­яний. Этот подход «пространства состояний» к решению задачи позволяет использовать теорию графов для создания и анализа компьютерных про­грамм. Был определен общий алгоритм поиска с возвратами, алгоритмы по­иска в глубину и в ширину, а также эвристический поиск. Следующие по­нятия характеризуют данные и управляющие структуры, используемые для реализации поиска в пространстве состояний.

  1. Представление решения задачи в виде пути от начального состояния к целевому.

  2. Алгоритм поиска, систематически проверяющий наличие альтернатив­ных путей к цели.

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

  4. Списки, содержащие точную информацию о рассматриваемых в данный момент состояниях, в том числе:

  • список open, позволяющий алгоритму в случае необходимости иссле­довать ранее не рассмотренные состояния;

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

  1. Список open можно рассматривать как стек для поиска в глубину, оче­редь для поиска в ширину или приоритетную очередь для «жадного» алгоритма поиска.

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

  1. Рекурсия

В математике рекурсивное определение объекта — это его описание в терми­нах частей этого же определения. В информатике рекурсия используется для определения и анализа структур данных и процедур. Рекурсивная процедура состоит из следующих этапов.

  1. Рекурсивный шаг: вызов процедуры из этой же процедуры для повторе­ния последовательности действий.

  2. Условие, которое обеспечивает выход из процедуры и таким образом предотвращает зацикливание (рекурсивная версия бесконечного цикла).

Оба этих компонента необходимы и появляются во всех рекурсивных определениях и алгоритмах. Рекурсия — естественный инструмент управле­ния массивами данных, которые имеют правильную структуру и неопреде­ленный размер, например, списками, деревьями и графами. Рекурсия осо­бенно удобна для поиска в пространстве состояний.

Простой пример — алгоритм определения принадлежности данного эле­мента списку. Списком (list) называют упорядоченную последовательность элементов и фундаментальных строительных блоков структур данных. Спис­ки использовались, например, для представления списков open и closed в алгоритмах поиска. Процедура проверки принадлежности элемента списку определяется так.

function member(item, list); begin

if список пуст then return FAIL 'останов

else if item = первому элементу списка then return SUCCESS

'останов

else

begin

tail := список после удаления первого элемента; member(item,tail) 'рекурсия

end

end.

Эта процедура сначала проверяет, пуст ли список, и, если да, алгоритм возвращает ошибку. В противном случае он сравнивает выбранный элемент с первым элементом списка. Если они равны, происходит успешное заверше­ние процедуры. Таким образом, это условия завершения процедуры. Если ни одно из приведенных выше условий завершения не выполнено, процедура удаляет первый элемент из списка и снова вызывает сама себя для сокра­щенного списка. Это позволяет исследовать каждый элемент списка по оче­реди. Обратите внимание на то, что список конечен, и каждый шаг рекурсии уменьшает его размер на один элемент. Это означает, что данный алгоритм не приведет к зацикливанию.

В приведенном выше алгоритме используются две фундаментальные опе­рации со списком: первая из них (head) возвращает первый элемент спис­ка, вторая операция (tail) возвращает список, полученный после удаления первого элемента. Эти операции наряду с рекурсией составляют основу вы­сокоуровневых операций со списком типа member. Данные операции под­держиваются языками обработки списков (например, LISP). Рекурсия, под­держиваемая каким-либо языком программирования, расширяет возможно­сти более традиционных управляющих конструкций, например, циклов и условных операций. Другими словами, любая итерационная процедура мо­жет быть также реализована рекурсивно. Удобство рекурсивных формулиро­вок—ясность и компактность выражения. Математические понятия логики или функций не поддерживают таких механизмов, как упорядочение, ветвле­ние и итерация. Для индикации повторения можно использовать рекурсию. Поскольку рекурсию проще описать математически, чем явные итерации, то легче формально проанализировать правильность и сложность рекурсивных алгоритмов. Рекурсия часто используется в системах автоматической генера­ции или верификации программ, необходимых при создании компиляторов и интерпретаторов. Однако более важным является тот факт, что рекур­сия — это естественный способ реализации таких стратегий искусственного интеллекта, как поиск на графе.

  1. Рекурсивный поиск

Прямое преобразование описанного ранее алгоритма поиска в глубину в ре­курсивную форму иллюстрирует эквивалентность рекурсии и итерационного подхода. Этот алгоритм для поддержки списка состояний использует гло­бальные переменные open и closed.

function DepthSearch; ’переменные open и closed глобальные

begin

if список open пуст then return FAIL;

CurrentState := первый элемент списка open;

if CurrentState равно целевому состоянию then return SUCCESS

else

begin

open := хвост списка open;

closed := closed с добавлением CurrentState;

для каждого потомка CurrentState

if не в списке closed или open ’построение стека

then добавить потомок в начало списка open

end;

DepthSearch ’рекурсивный вызов

end.

Поиск в ширину можно описать фактически тем же самым алгоритмом. При этом необходимо сохранять список closed как глобальную структуру данных и рассматривать список open как очередь, а не как стек.

Из представленного алгоритма ясно, что поиск в глубину не использует все возможности рекурсии. Процедуру можно упростить, используя рекур­сию непосредственно (а не через список open) для рассмотрения состояний и нахождения путей в их пространстве. В новой версии алгоритма глобальная переменная closed используется для обнаружения повторяющихся состоя­ний и предотвращения циклов. Список open неявно используется в записях активации рекурсивной среды.

Вместо того чтобы находить все потомки данного состояния, а затем по­мещать их в список open, алгоритм DepthSearch2 рассматривает потомки по одному. Для каждого из них рекурсивно находятся все потомки, а затем рассматривается очередной потомок исходного состояния. Следует заметить, что алгоритм предполагает определенный порядок операторов генерации со­стояний. Если при рекурсивном поиске некоторый потомок является целе­вым состоянием, то процедура поиска успешно завершается, и, естествен­но, алгоритм игнорирует братьев данного потомка. Если рекурсивный вызов для одного из потомков приведет в тупик, то будет рассмотрен брат этого потомка, а также все его потомки. Таким образом, алгоритм обходит весь граф аналогично алгоритму поиска в глубину.

Рекурсия позволяет обойтись без списка open. Механизм, посредством которого можно реализовать рекурсию на каком-либо языке программиро­вания, должен включать отдельную запись активации (activation record) для каждого рекурсивного вызова. Каждая такая запись активации должна со­держать локальные переменные и состояние выполнения для каждого вызо­ва процедуры. При каждом рекурсивном вызове процедуры с новым состоя­нием новая запись активации сохраняет параметры процедуры (состояние), все локальные переменные и текущее состояние выполнения. В рекурсивном алгоритме поиска состояния, находящиеся на рассматриваемом пути, сохра­няются в последовательности записей активации рекурсивных вызовов. За­пись каждого вызова содержит также последнюю операцию, которая была сделана при генерации соответствующего потомка. Это позволяет генериро­вать при необходимости брата данного потомка.

function DepthSearch2(CurrentState);

’closed - глобальная переменная

begin

if CurrentState равно целевому состоянию then return SUCCESS; добавить CurrentState в список closed; while CurrentState имеет непроверенные потомки do begin

child := следующий непроверенный потомок; if потомок не член списка closed then

if depthsearch(child) = SUCCESS then return SUCCESS

end;

return FAIL ’поиск исчерпан

end.

Возврат производится, если ни один из потомков данного состояния не привел к цели, что послужило причиной неудачного завершения процедуры. В этом случае в процедуру генерации потомков возвращается значение FAIL. Затем эта процедура применяется к брату данного состояния. В рассматри­ваемом случае внутренние механизмы рекурсии заменяют список open, ко­торый был использован в итерационной версии алгоритма. Рекурсивная ре­ализация позволяет программисту ограничить рассмотрение единственным состоянием и его потомками, а не иметь дело со всем списком open. Возмож­ность рекурсии выражать глобальные понятия в простой форме — главный источник ее удобства.

Поиск в пространстве состояний — процесс рекурсивный по природе. Что­бы найти путь от текущего состояния к цели, необходимо двигаться в дочер­нее состояние, затем из него перейти к потомку и так далее. Если соответ­ствующее дочернее состояние не ведет к цели, то необходимо рассмотреть состояние того же уровня, на котором находится родительское состояние. Рекурсия разбивает большую и трудную проблему (поиск во всем простран­стве состояний) на более простые части (генерирование потомков отдельного состояния), а затем применяет (рекурсивно) эту стратегию к каждому по­томку. Этот процесс продолжается до тех пор пока не будет найдено целевое состояние или исчерпано все пространство состояний.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]