Control and Implementation of State Space Search
Control and Implementation of State Space Search
о переводе статьи
«Control and implementation of state space search»
(25 000 печатных знаков)
Abstract
Summary
Questions
Special
1. How can recursive algorithm help with search in program?
2. What are the two algorithms that used for search in program?
3. What definitions that used to implement state space search do you know?
Alternative
6. Did George F. Luger’s book release in 1998 or 1988?
General
7. Does recursion repeat itself?
Tag-question
8. Recursive search is a problem-solving technique, isn’t it?
Examples on grammatical phenomena
«Passive voice»
Term list
If we carefully factor out the influences of the task environments from the influences of the
underlying hardware components and organization, we reveal the true simplicity of the
adaptive system. For, as we have seen, we need postulate only a very simple information
processing system in order to account for human problem solving in such tasks as chess, logic,
and cryptarithmetic. The apparently complex behavior of the information processing system in
a given environment is produced by the interaction of the demands of the environment with a
few basic parameters of the system, particularly characteristics of its memories.
5.0 Introduction
Chapters 3 and 4 represented problem solving as search through a set of problem states.
The state space model of problem solving allows graph theory to be used as a tool for designing
and analyzing intelligent programs. Chapter 3 defined a general backtracking graph search
algorithm as well as algorithms for both depth-first and breadth-first search. Chapter 4 presented
algorithms for heuristic search. The following definitions characterize the data and control
structures used to implement state space search:
1. Representation of a problem solution as a path from a start state to a goal.
2. Search to test systematically alternative paths to a goal.
3. Backtracking to allow an algorithm to recover from paths that fail to find a goal.
4. Lists to keep explicit records of states under consideration.
a. The open list allows the algorithm to explore untried states if necessary.
b. The closed list of visited states allows the algorithm to implement loop
detection and avoid repeating fruitless paths.
5. Implementation of the open list as a stack for depth-first search, a queue for breadth-
first search, and a priority queue for best-first search.
Chapter 5 introduces higher-level techniques for implementing search algorithms. The
first of these, recursive search, implements depth-first search with backtracking in a more
concise, natural fashion than in Chapter 3 and forms the basis for many of the algorithms in this
text, Section 5.1. Recursive search is augmented through the use of unification to search the state
space generated by predicate calculus assertions. This pattern- directed search algorithm. Section
5.2, is the basis of PROLOG, see Chapter 9, and many of the expert system shells discussed in
Chapter 6. Next, in Section 5.3 we introduce production systems, a general architecture for
pattern-directed problem solving that has been used extensively both to model human problem
solving and to build expert systems and other AI applications. In Section 5.4 we show how
predicate calculus and pattern matching may be used in robot planning or problem solving across
time periods. Finally we present another AI problem-solving technique, the blackboard.
This procedure first tests whether list is empty; if so, the algorithm returns fail. Other-
wise, it compares item to the first element of list; if these are equal, the procedure halts with
success. These are the terminating conditions of die procedure. If neither terminating condition is
met, member removes the first element from list and calls itself on the shortened list. In this
fashion, member examines each element of the list in turn. Note that because list is finite and
each step reduces its size by one element, the algorithm halts.
This algorithm uses two fundamental list operations: one that returns the first element
(the head) of a list and a tail operation, which returns the list with its first element removed.
When coupled with recursion, these operations are the basis for higher-level list operations such
as member. These operations are supported by both LISP and PROLOG and are described in
detail in the chapters on each language.
When supported by a programming language, recursion offers all the power of теме
traditional control constructs such as loops and conditional branching. In other words, anything
that can be done using explicit iteration can be done recursively. The benefit of recursive
formulations is greater clarity and compactness of expression. Mathematical notations such as
logic or functions do not support such concepts as sequencing, branching, and iteration; instead,
they use recursion to indicate repetition. As recursion is easier to describe mathematically than
explicit iteration, it is easier to analyze formally the correctness and complexity of recursive
algorithms. Recursive formulations are also used frequently by systems that automatically
generate or verify programs, and they play an important role in implementing compilers and
interpreters. More important, however, is the power and naturalness of recursion as a tool for
implementing AI problem-solving strategies such as graph search.
Breadth-first search can be designed with virtually the same algorithm, that is, by
retaining closed as a global data structure and by implementing the open list as a queue rather
than as a stack.
Depth-first search as just presented does not utilize the full power of recursion. It is
possible to simplify the procedure further by using recursion itself (rather than an explicit open
list) to organize states and paths through the state space. In this version of the algorithm, a global
closed list is used to detect duplicate states and prevent loops, and the open list is implict in the
activation records of the recursive environment
Rather than generating all children of a state and placing them on an open list this
algorithm produces the child states one at a time and recursively searches the descendants of
each child before generating its sibling. Note that the algorithm assumes an order to the state
generation operators. In recursively searching a child state, if some descendant of that state is a
goal, the recursive call returns success and the algorithm ignores the siblings. If the recursive call
on the child state foils to find a goal, the next sibling is generated and all of its descendants are
searched. In this fashion, the algorithm searches the entire graph in a depth-first order. The
reader should verify that it actually searches the graph in the same order as the depth-first search
algorithm of Section 3.2.3.
The omission of an explicit open list is made possible through recursion. The
mechanisms by which a programming language implements recursion include a separate
activation record (Aho and Ullman 1977) of each recursive call. Each activation record captures
the local variables and state of execution of each procedure call. When the procedure is called
recursively with a new state, a new activation record stores its parameters (the state), any local
variables, and the current state of execution. In a recursive search algorithm, the series of states
on the current path are recorded in the sequence of activation records of the recursive calls. The
record of each call also indicates the last operation used to generate a child state; this allows the
next sibling to be generated when needed.
Backtracking is effected when all descendants of a state fail to include a goal, causing the
recursive call to fail. This returns foil to the procedure expanding the parent state, which then
generates and recurs on the next sibling. In this situation, the internal mechanisms of recursion
do the work of the open list used in the iterative version of the algorithm. The recursive
implementation allows die programmer to restrict his or her point of view to a single state and its
children rather than having to explicitly maintain an open list of states. The ability of recursion to
express global concepts in a closed form is a major source of its power.
State space search is an inherently recursive process, To find a path from a current state
to a goal, move to a child state and recur. If that child state does not lead to a goal, try its siblings
in order. Recursion breaks a large and difficult problem (searching the whole space) into smaller,
simpler pieces (generate the children of a single state) and applies this strategy (recursively) to
each of them. This process continues until a goal state is discovered or the space is exhausted.
Symbolic integration, discussed in Section 3.3.3, is an excellent example of the power of
a recursive approach to search problems. When attempting to integrate an expression, it applies
either a substitution or a decomposition to the expression, replacing it with one (in the case of a
substitution) or more (in the case of a decomposition) simpler subproblems. These subproblems
are then solved recursively, with the separate solutions being recombined into a final answer. For
example, after applying the rule that the integral of a sum equals the sum of the integrals of the
terms, it recursively attempts to integrate each term. This may lead to further decompositions or
substitutions, until each term has been integrated. These results are then combined (summed) to
produce the final result Recursion is a natural tool for systematically decomposing a problem and
recombining partial solutions into a final answer.
In the next section, this recursive approach to problem solving is extended into a
controller for a logic-based problem solver that uses unification and inference to generate and
search a space of logical relations. The algorithm supports the and of multiple goals as well as
back chaining from a goal to premises.
5.2 Pattern-Directed Search
In Section 5.2 we apply recursive search to a space of logical inferences; the result is a
general search procedure for predicate calculus.
Suppose, for example, we want to write an algorithm that determines whether a predicate
calculus expression is a logical consequence of some set of assertions. The algorithm must find a
sequence of inferences that produce the goal expression. This suggests a goal- directed search
with the initial query forming the goal, and modus ponens defining the transitions between
states. Given a goal (such as p(a)>, the algorithm uses unification to select the implications
whose conclusions match the goal (e.g., q(X) —» p(X)>. Because the algorithm treats
implications as potential rules for solving the query, they are often simply called rules. After
unifying the goal with the conclusion of the implication (or rule) and applying the resulting
substitutions throughout the rule, the rule premise becomes a new goal (q(a)). This is called a
subgoal. The algorithm then recurs on the subgoal. If a subgoal matches a fact in the knowledge
base, search terminates. The series of inferences that led from the initial goal to the given facts
prove the truth of the original goal.
In the function pattern_search, search is performed by a modified version of the
recursive search algorithm that uses unification, Section 2.3.2, to determine when two
expressions match and modus ponens to generate the children of states. The current focus of the
search is represented by the variable current__goal. If curren_goal matches with a fact, the
algorithm returns success. Otherwise the algorithm attempts to match Current_goal with the
conclusion of some rule, recursively attempting to solve the premise. If Current_goal does not
match any of the given assertions, the algorithm returns fail. This algorithm also handles
conjunctive goals such as are often found in the premise of a rule.
For simplicity, this algorithm does not address the problem of maintaining consistency
among the variable substitutions produced by unification. This is important when solving
conjunctive queries with shared variables (as in p(X) л q(X) ). Not only must both conjuncts
succeed, but they must succeed with unifiable bindings for X, Section 2.3.2. This problem is
addressed at the end of this section.
The major advantage of using general methods such as unification and modus ponens to
generate states is that the resulting algorithm may search any space of logical inferences. The
specifics of a problem are described using predicate calculus assertions. Thus, we have a means
of separating problem-solving knowledge from its control and implementation on the computer.
pattern_search provides our first implementation of the separation of knowledge and control.
This rule says that for all locations X and Y, a two-move path exists between them if
there exists a location Z such that the knight can move from X to Z and then move from Z to Y.
The general path2 rule can be applied in a number of ways. First, it may be used to
determine whether there is a two-move path from one location to another. If pattem.search is
called with the goal path2(1,3), it matches the goal with the consequence of the rule
path2(X,Y), and the substitutions are made in the rule's premise; the result is a specific rule that
defines the conditions required for the path:
pattern.search then calls itself on this premise. Because this is a conjunction of two
expressions, pattern_search will attempt to solve each subgoal separately. This requires not
only that both subgoals succeed but also that any variable bindings be consistent across subgoals.
Substituting 8 for Z allows both subgoals to succeed.
Another request might be to find all locations that can be reached in two moves from
location 2. This is accomplished by giving pattern_search the goal path2(2,Y). Through a
similar process, a number of such substitutions may be found, including {6/Y} and {2/Y} (with
intermediate Z being 7) and {2/Y} and {4/Y} (with intermediate location 9). Further requests
could be to find a two-move path from a number to itself, from any number to 5, and so on.
Notice here one of the advantages of pattern-driven control: a variety of queries may be taken as
the initial goal.
Similarly, a three-move path is defined as including two intermediate locations that are
part of the path from the initial location to the goal. This is defined by:
This clause can solve such goals as path3(1,2), path3(1 ,X), or even path3(X,Y).
Tracing the results of these queries is left as an exercise.
It soon becomes evident that the path moves are the same for a path of any length, simply
requiring die proper number of intermediate places to "land." It is also evident that the path
moves could be stated in terms of each other, such as
Note once again the elegance and simplicity of the recursive formulation. When
combined with the recursive control provided by pattern_search, these rules will search the space
of possible paths in the knight's tour problem. Combined with the move rules, this yields the
complete problem description (or knowledge base):
It is important to note that the solution to the problem is implemented through both the
logical descriptions that define the state space and the use of pattem_search to control search of
that space. Although the path rule is a satisfactory definition of a path, it does not tell us how to
find that path. Indeed, many undesirable or meaningless paths around the chessboard also fit this
definition. For example, without some way to prevent loops, the goal path(1,3) could lead to a
path that simply goes back and forth between 1 and 8, instead of finding the correct path from 1
to 8 to 3. Both the loop and the correct path are logical consequences of the knowledge base.
Similarly, if the recursive rule is tried before the terminating condition, the fact that path(3,3)
should terminate the search could be overlooked, allowing the search to continue meaninglessly.
5.0. Введение
В главах 3 и 4 представлены задачи, решаемые истодом поиска в пространстве со-
стояний. Этот подход "пространства состояний" к решению задачи позволяет использо-
вать теорию графов для сознания и анализа компьютерных пробами. В главе 3 определен
общий алгоритм поиска и возвратами, а также алгоритмы поиска в глубину и в ширину.
Эвристический поиск был представлен в главе 4. Следующие понятия характеризуют
данные и управляющие структуры, используемые для реализации поиска в пространстве
состояний.
1.Представление решения задачи в виде пути от начального состояния к целевому.
2. Алгоритм поиска, ссистематически проверяющим наличие альтернативных
путей к цели.
3. Поиск с возвратами или другой механизм, позволяющий выходить из тупиковых
состояний и находить путь к цели.
4. Списки, содержащие точную информацию о рассматриваемых в данный момент
состояниях, в том числе:
a. список open, позволяющий алгоритму в случае необходимости
исследовать ранее не рассмотренные состояния;
b. список closed, содержащий рассмотренные состояния. Он позволяет
алгоритму избегать зацикливаний и учитывать тупиковые пути.
5. Список open можно рассматривать, как стек для поиска в глубину, очередь для
поиска в ширину или приоритетную очередь для “жадного” алгоритма поиска.
В данной главе вводится высокоуровневые методы для построения алгоритмов
поиска. Первый из них — рекурсивный поиск, определяющий поиск в глубину с
возвратами более кратким н естественным способом, чем в главе 3. Этот механизм
составляет основу многих алгоритмов в разделе 5.1. Применение унификации в процессе
поиска в пространстве состояний расширяет возможности рекурсивного поиска. Этот
алгоритм поиска но образцу (раздел S3) положен в основу языка PROLOG, описанного в
главе 14, и многих экспертных систем, рассмотренных в главе 7.Раздел 5.3 посвящен гак
называемым продукционным системам. Это общая архитектура для решения образно-
ориентированных проблем, которая широко используется как для моделирования
человеческого образа мышления при решении многих задач, так и для построения
экспертных систем и других приложений ИИ. Наконец, в разделе 5.4 рассматривается еще
одно представление для решения задач искусственного интеллекта — так называемая
модель классной доски (blackboard).
Вместо того чтобы находить все потомки данного состояния, а затем помещать их в
список open, этот алгоритм рассматривает потомки по одному. Для каждого из них ре-
курсивно находятся все потомки, а затем рассматривается очередной потомок исходного
состояния. Следует заметить, что алгоритм предполагает определенный порядок опера-
торов генерации состоянии. Если при рекурсивном поиске некоторый потомок является
целевым состоянием, то процедура поиска успешно завершается, и, естественно, алгоритм
игнорирует братьев данного потомка. Если рекурсивный вызов для одного из потомков
приведет в тупик, то будет рассмотрен брат этого потомка, а также все его потомки.
Таким образом, алгоритм обходит весь граф аналогично алгоритму поиска в Ширину (см.
подраздел 3.2.3).
Рекурсия позволяет обойтись без списка open. Механизм, посредством которого
можно реализовать рекурсию на каком-либо языке программирования, должен включать
отдельную дались активации (activation record) [Aho и UHman. 1977] для каждого
рекурсивного вызова. Каждая такая запись активации должна содержать локальные
переменные и состояние выполнения для каждого вызова процедуры. При каждом
рекурсивном вызове процедуры с новым состоянием новая запись активации сохраняет
параметры процедуры (состояние), все локальные переменные и текущее состояние
выполнения. В рекурсивном алгоритме поиска состояния, находящиеся на
рассматриваемом пути, сохраняются в последовательсноти записей активации
рекурсивных вызовов. Запись каждого вызова содержит также последнюю операцию,
которая была сделана при генерации соответствующего потомка. Это позволяет
генерировать при необходимости брата данного потомка.
Возврат производится, если ни один из потомков данного состояния не привел к
цели, что послужило причиной неудачного завершения процедуры. В этом случае в
процедуру генерации потомков возвращается значение FAIL. Затем эта процедура
применяется к брату данного состояния. В рассматриваемом случае внутренние меха-
низмы рекурсии заменяют список open, который был использован, а итерационной версии
алгоритма. Рекурсивная реализация позволяет программисту ограничить рассмотрение
единственным состоянием н его потомками, а не иметь дело со всем списком open.
Возможность рекурсии выражать глобальные понятия в простой форме — главный
источник ее удобства.
Поиск в пространстве состояний — процесс рекурсивный, но природе. Чтобы
найти путь от текущего состояния к цели, необходимо двигаться в дочернее состояние,
затем из него перейти к потомку и так далее. Если соответствующее дочернее состояние
не ведет к цели, то необходимо рассмотреть состояние тою же уровня, на котором
находится родительское состояние. Рекурсия разбивает большую и трудную проблему
(поиск во всем пространстве состояний) на более простые части (генерирование потомков
отдельного состояния), а затем применяет (рекурсивно) эту стратегию к каждому потомку.
Этот процесс продолжается до тех пор. пока не будет найдено целевое состояние или
исчерпано все пространство состояний.
Задача символьного интегрирования, рассмотренная в подразделе 3.3.3. является
превосходным примером мощности рекурсивного подхода в задачах поиска. При ин-
тегрировании выражения обычно выполняется замена переменных или декомпозиция
интеграла, сведение его к совокупности более простых интегралов (подзадач). Эти
подзадачи также могут быть решены рекурсивно с последующим объединением ре-
зультатов в решении исходной задачи. Например, после применения правила суммы
интегралов (интеграл суммы равняется сумме интегралов) можно попытаться рекурсивно
проинтегрировать каждое из слагаемых. В дальнейшем можно снова применять
декомпозицию и замену, пока не будет проинтегрирован каждый из членов. Затем ре-
зультаты интеграции объединяются (посредством суммирования) в конечным результат.
Таким образом, рекурсия — это естественный инструмент для систематической
декомпозиции задачи с последующим объединением результатов решения частных задач
для решения исходной проблемы.
В следующем разделе этот рекурсивный подход будет расширен до некоторого
контроллера логического решателя задач, который использует унификацию и вывод для
генерации пространства логических отношений и поиска в нем. Алгоритм поддерживает
операцию логического умножения нескольких целей and, а также построение обратных
цепочек от цели к предпосылкам.
5.2 Поиск по образцу
В разделе 5.2 рекурсивный поиск будет применен к пространству логического
вывода. В результате получим общую процедуру поиска для исчисления предикатов.
Допустим, необходимо построить алгоритм, который определяет является ли
выражение исчисления предикатов логическим следствием некоторого набора
утверждений. Имея цель (типа р(а)), алгоритм использует унификацию для выбора
импликаций, заключения которых соответствует цели (например, q[X) р(*М. Поскольку
алгоритм обрабатывает импликации как потенциальные правила дня разрешения запроса,
их часто называют просто правилами. После унификации цели с заключением
импликации (или привила) и применения результирующих подстановок ко всему правил)*
предпосылка зтого правила становится новой целью (q(e)}. Ее называют подцелью. Затем
алгоритм повторяется для подцелей. Если подцель соответствует факту в бак факту в бак
знаний, поиск прекращается. Последовательность выводов, ведущая от исходной цели к
данным фактам, доказывает истинность первоначальной цели.
В функции pattern search поиск выполняется с помощью модифицировании» версии
рекурсивного алгоритма, который, чтобы установил, соответствие двух выражений.
использует унификацию (подраздел 2.5 2), а для создания потомков состояний — правило
модус поненс. Текущее состояние поиска представлено переменной сиг- rent_goal. Если
current_goal соответствует факту, алгоритм возвращает SUC- CBSS. В противном случае
алгоритм пытается сопоставить current_goal с заключением некоторого правила,
рекурсивно просматривая все предпосылки. Если current, goal не согласуется ми с одним
из утверждений, алгоритм возвращает FAIL. Этот алгоритм также обрабатывает
конъюнктивные цели, которые часто встречаются в предпосылках правила.
Для простоты этот алгоритм не решает проблему согласования подстановок
переменных при унификации. Это важно при разрешении конъюнктивных запросов с
общими переменными . Оба конъюнкта должны быть согласованы не только между собой,
но и с унифицированными значениями переменной X (подраздел 2.3.2). Эту проблему мы
рассмотрим и конце раздела.
Главное преимущество использования таких общих методов, как унификация и
правило модус поненс, для генерации состояний заключается в том. что результирующий
алгоритм может просматривать любое пространство логических выводов. Специфические
проблемы описываются утверждениями исчисления предикатов. Таким образом. знания о
задаче отделяются от реализации ее решения на компьютере. Функция pattern_search
обеспечивает первую реализацию такого отделения знаний от управления.
Это правило утверждает, что для всех клеток X и У существует путь с двумя
ходами, если существует такая клетка Z, которая может переместиться из X в Z. а затем —
из Z в У.
Общее правило path2 можно применять различными способами. Например, оно
подходит для проверки существования двухходового пути из одной клетки в другую. Если
функция pattem_search вызывается для цели ра(/>2<1.3), то проверяется соответствие этой
цели последовательности правил path2{X,Y). При этом выполняются подстановки для
предпосылок правила. Результатом является конкретное правило, которое определяет
условия, необходимые для такого пути.
Затем функция pattem_search вызывает сама себя с этим параметром. Поскольку это
конъюнкция двух выражений, pat tem_ae arch пытается разрешить каждую подцель
отдельно. Это требует не только согласованности обеих подцелей, но и непро-
тиворечивости связывания всех переменных. Подстановка 8 вместо Z дает возможность
согласовать обе подцели.
Другой запрос может состоять в том, чтобы найти все клетки, которые могут быть
достигнуты из клетки 2 за два хода. Это достигается вызовом pattern_search для цели
ра№2(2, У). Используя подобный процесс, можно найти все возможные подстановки,
включая {б/У) и {2/У) (с промежуточным значением Z. равным 7), а также (2/У) и (4/У) (с
промежуточным значением 9). Дальнейшие запросы могут состоять в том. чтобы найти
путь за два хода из клетки с некоторым номером в ту я» самую клетку, из любой клетки в
клетку 5 и так далее. Обратите внимание на одно из преимуществ поиска по образцу: в
качестве начальной цели могут быть взяты самые разнообразные запросы.
Аналогично путь с тремя ходами проходит через две промежуточные клетки,
которые являются частью пути от начального состояния к цели. Такой путь может быть
определен следующим образом.
Используя это определение, можно рассматривать такие задачи, как parft3{ 1.2),
P9th3( 1 ,Х) или даже paf/i3(X, У). Эти задачи мы оставляем читателю в качестве уп-
ражнений.
Очевидно, что аналогичным способом можно решать задачи нахождения пути
произвольной длины. Просто необходимо указать соответствующее количество
промежуточных мест "приземления". Также очевидно, что путь большей длины может
быть определен в терминах пути меньшей длины.