Точно Не проект 2 / Не books / Источник_1
.pdf290 |
Глава 5 |
|
|
Функция поиска в глубину depth-first может быть легко преобразована в функцию, реализующую метод поиска в ширину. Для этого необходимо поменять местами аргументы функции append:
( append ( rest open ) (list-children (first n )))
Это обеспечит добавление дочерних вершин в конец списка OPEN, что соответствует поиску в ширину.
5.25.3.Эвристический поиск на основе А-алгоритма
ВА-алгоритме список OPEN упорядочен в соответствии со значением оценочной функции вида (2.1). Опишем граф состояний с помощью а- списка, в котором каждый подсписок имеет вид
( p d c h ) ,
где p – родительская вершина; d – соответствующая дочерняя вершина; c – стоимость участка графа между вершинами p и d; h– оценка стоимости кратчайшего пути из вершины d целевую вершину. Тогда граф состояний, изображенный на рисунке 2.9, можно представить с помощью а-списка:
(setq graph ’((s d 1 10)(s c 3 7)(s b 5 4) (s a 7 1)(a g 10 0) (b a 1 1) (c b 1 4)(c a 2 1)(d c 1 7)))
Определим на языке Лисп функцию A-SEARCH, соответствующую процедуре эвристического поиска с помощью А-алгоритма, описанного в главе 2. Общая схема построения функции A-SEARCH будет соответствовать приведенной выше функции DEPTH-FIRST. Отличия будут заключаться в том, что список открытых вершин OPEN упорядочивается в соответствии со значением оценочной функции, а элементы списков OPEN и CLOSED будут представляться в виде подсписков
( d p g f ),
где d – некоторая вершина на графе состояний; p – ссылка на родительскую вершину; g – оценка стоимости пути из начальной вершины в вершину d; f – значение оценочной функции (2.1).
В соответствии с А-алгоритмом вершины из списка CLOSED могут перемещаться обратно в список OPEN. Это происходит в том случае, когда обнаруживается более короткий путь к вершине, которая уже была закры-
Язык Лисп |
291 |
|
|
та. Тогда необходимо удалить соответствующую вершину из списка CLOSED и вставить ее в список OPEN с учетом значения оценочной функции. Кроме этого, если в процессе раскрытия очередной вершины будет установлено, что ее дочерние вершины уже включены в список OPEN, то выполняется сравнение оценочных функций вершин, уже имеющихся в списке OPEN, и вновь полученных дочерних вершин. Если оценочные функции вновь полученных дочерних вершин меньше, то они заменяют уже имеющиеся вершины в списке OPEN. Приведем определение функции
A-SEARCH:
(defun a-search(start goal)
;поместить в open стартовую вершину, описываемую
;подсписком со структурой ( s s 0 h)
(setq open (cons start nil)) ; инициализация closed
(setq closed nil)
(loop ; цикл
(cond ((null open)(return ’неудача)) ; на графе нет пути
(t
;запомнить первый подсписок списка open (setq n (first open))
;удалить n из open
(setq open (remove n open :test #'equal)) ; внести n в closed
(setq closed (cons n closed))
;проверить, является ли (first n) целевой вершиной
(if (eq (first n) goal) (return ’удача))
;внести в open дочерние вершины и удалить
;их при необходимости из closed
(put-in-list (list-children n))
;печать промежуточных результатов
(terpri)
(princ ”closed=”)(prin1 closed) (terpri)
(princ ”open=”) (prin1 open)))))
Вэтом определении функция LIST-CHILDREN возвращает а-список дочерних вершин для вершины (first n), которая при вызове LIST-CHILDREN является первым подсписком списка OPEN.
(defun list-children (n) (setq L nil)
(dolist (temp graph L) ; выполнить действия для каждого
;подсписка из graph
;переставить первый и второй элементы temp
(setq rtemp (rev temp))
;выяснить, содержит ли temp дочернюю вершину для (first n)
292 |
Глава 5 |
|
|
(when (eq (first n) (first temp))
;вычислить значения оценки g(n) для дочерней вершины
(setq rtemp (put-third rtemp (+ (third n)(third rtemp))))
;вычислить значения оценки f(n) для дочерней вершины
(setq rtemp (put-fourth rtemp (+ (third rtemp) (fourth rtemp)))) ;добавляем описание дочерней вершины в виде подсписка rtemp
;к результирующему списку L (setq L (cons rtemp L)))))
;функция, переставляющая местами первый и второй элементы в temp (defun rev(temp)
(setq a (first temp)) |
(setq temp (rest temp)) |
(setq b (first temp)) |
(setq temp (rest temp)) |
(setq temp (cons b (cons a temp))))
;функция, меняющая значение третьего элемента
;в temp (оценка g(n)) на x
(defun put-third (str x)
(setq temp1 (butlast str 2)) (setq temp2 (last str))
(append temp1 (cons x temp2)))
;функция, меняющая значение четвертого элемента
;в temp (оценка f(n)) на x
(defun put-fourth (str x)
(setq temp1 (butlast str 1)) (append temp1 (cons x nil)))
Список дочерних вершин, возвращаемый функцией LIST-CHILDREN, используется в функции для соответствующей модификации списков OPEN и CLOSED:
(defun put-in-list (dv) ; dv – а-список дочерних вершин (cond
;условие завершения рекурсии
((null dv ) nil)
;если очередная дочерняя вершина не входит в
;списки OPEN и CLOSED, то добавить её в список OPEN
((and (not (member1 (caar dv) open)) (not (member1 (caar dv) closed)))
(setf open (add (first dv) open))
(put-in-list (rest dv)))
;если очередная дочерняя вершина входит в
;один из списков OPEN или CLOSED, то удалить её из
;этих списков и заново внести в список OPEN с учетом значения
;оценочной функции
Язык Лисп |
293 |
|
|
(t (setf open (del (first dv) open))
(setf closed (del (first dv) closed)) (setf open (add (first dv) open)) (put-in-list (rest dv)))))
Функция DEL(V L) выполняет удаление подсписка V, представляющего вершину графа состояний, из списка L. Удаление выполняется, если оценочная функция для удаляемой вершины V меньше либо равна значению оценочной функции вершины из списка L:
(defun del(v l)
(cond ((null l) nil) ; завершение рекурсии
; проверка вхождения v в l
((and (eq (first v)(first(first l)))
; сравнение оценочных функций
(<= (fourth v)(fourth (first l))))
(setf l (cdr l)))
(t (append (list(car l)) (del v (cdr l))))))
Вставка дочерних вершин в упорядоченный список OPEN выполняется с помощью функции ADD(V L):
(defun add(v l)
(cond ((null l) (cons v l)) ; выход из рекурсии
;сравнить значения оценочных функций
((<= (fourth v)(fourth (first l)))
; если меньше, то вставить вершину v в l (setf l (cons v l)))
(t (append (list(car l)) (add v (cdr l))))))
Функция MEMBER1(V L) определяется следующим образом:
(defun member1 (v l) (cond ((null l) nil)
((eq v (first(first l))) t)
(t (member1 v (rest l)))))
Если функция A-SEARCH завершает поиск сообщением “УДАЧА”, то восстановить путь из исходной вершины в целевую вершину можно с помощью функции BACK-WAY, описанной в предыдущем параграфе.
Рассмотрим выполнение эвристического поиска на примере графа состояний, который изображен на рисунке 2.9. Найдем путь из стартовой вершины S в целевую вершину G. Для этого выполним вызовы:
(a-search ’(s s 0 14) ’g)
(back-way ’g ’s))
294 |
Глава 5 |
|
|
В ходе выполнения функции A-SEARCH будут получены следующие промежуточные значения списков OPEN и CLOSED:
> (poisk)
closed=((s s 0 14))
open=((a s 7 8) (b s 5 9) (c s 3 10) (d s 1 11)) closed=((a s 7 8) (s s 0 14))
open=((b s 5 9) (c s 3 10) (d s 1 11) (g a 17 17)) closed=((b s 5 9) (s s 0 14))
open=((a b 6 7) (c s 3 10) (d s 1 11) (g a 17 17)) closed=((a b 6 7) (b s 5 9) (s s 0 14))
open=((c s 3 10) (d s 1 11) (g a 16 16)) closed=((c s 3 10) (s s 0 14))
open=((a c 5 6) (b c 4 8) (d s 1 11) (g a 16 16)) closed=((a c 5 6) (c s 3 10) (s s 0 14)) open=((b c 4 8) (d s 1 11) (g a 15 15)) closed=((b c 4 8) (c s 3 10) (s s 0 14)) open=((a b 5 6) (d s 1 11) (g a 15 15)) closed=((a b 5 6) (b c 4 8) (c s 3 10) (s s 0 14)) open=((d s 1 11) (g a 15 15))
closed=((d s 1 11) (a b 5 6) (b c 4 8) (s s 0 14)) open=((c d 2 9) (g a 15 15))
closed=((c d 2 9) (d s 1 11) (s s 0 14)) open=((a c 4 5) (b c 3 7) (g a 15 15)) closed=((a c 4 5) (c d 2 9) (d s 1 11) (s s 0 14)) open=((b c 3 7) (g a 14 14))
closed=((b c 3 7) (c d 2 9) (d s 1 11) (s s 0 14)) open=((a b 4 5) (g a 14 14))
closed=((a b 4 5) (b c 3 7) (c d 2 9) (d s 1 11) (s s 0 14)) open=((g a 14 14))
Функция BACK-WAY возвратит искомый путь в виде:
((s s 0 14) (s d 1 11) (d c 2 9) (c b 3 7) (b a 4 5) (a g 14 14))
5.25.4. Сопоставление с образцом
Сопоставление с образцом является важнейшей операцией, выполняемой во многих программах искусственного интеллекта. В ходе сопоставления с образцом осуществляется сравнение поступающих на вход списков (или других структур данных) с известными символьными структурами (образцами). Благодаря этому выполняется распознавание входных данных.
Язык Лисп |
295 |
|
|
Сопоставление с образцом – неотъемлемая часть СИИ, использующих продукционную модель представления знаний. В этом случае предпосылки продукционных правил выступают в качестве образцов, а факты, хранящиеся в рабочей памяти, выступают в роли входных данных. Если предпосылка правила сопоставима с данными, имеющимися в рабочей памяти, то правило активизируется. Сопоставление с образцом широко применяется в СИИ, основанных на логических моделях представления знаний. В частности, унификация предикатов представляет собой сопоставление, в ходе которого выявляют условия совпадения предикатов.
Рассмотрим сопоставление с образцом, представляя образец и сопоставляемые с ним факты (данные) в виде списков языка Лисп. Простейшие возможности сопоставления реализуются встроенными предикатами Лиспа, например EQ, EQL, EQUAL. Если необходимо осуществлять сопоставление произвольных s-выражений, то следует определить отдельную функцию. Для начала определим функцию, выполняющую сопоставление одноуровневых списков.
К сопоставлению образца и данных можно предъявлять различные требования. Пусть символ ‘?’ в образце отождествляется с любым элементом из списка данных, а символ ‘$’ – с произвольным числом (возможно нулевым) элементов списка данных. Символы ‘?’ и ‘$’ рассматривают как универсальные символы подстановки, которым может быть сопоставлен любой элемент или сегмент списка, соответственно. Обозначим сопоставимость двух выражений знаком :=:, и сформулируем простые правила сопоставления [39]:
ОБРАЗЕЦ :=: ДАННЫЕ
если |
; правило 1 |
|
ДАННЫЕ = NIL и ОБРАЗЕЦ = NIL |
или |
; правило 2 |
|
ДАННЫЕ = NIL и (CAR ОБРАЗЕЦ) = $ |
|
и (CDR ОБРАЗЕЦ) = NIL |
или |
; правило 3 |
|
(ДАННЫЕ NIL) и (ОБРАЗЕЦ) NIL |
|
и (CAR ОБРАЗЕЦ) = (CAR ДАННЫЕ) |
|
и (CDR ОБРАЗЕЦ) :=: (CDR TEST) |
или |
; правило 4 |
|
(ДАННЫЕ NIL) и (ОБРАЗЕЦ NIL) |
|
и (CAR ОБРАЗЕЦ) = ? |
|
и (CDR ОБРАЗЕЦ) :=: (CDR ДАННЫЕ) |
или |
; правило 5 |
|
(ДАННЫЕ NIL) и (ОБРАЗЕЦ NIL) |
|
и (CAR ОБРАЗЕЦ) = $ |
|
и (CDR ОБРАЗЕЦ) :=: ДАННЫЕ |
296 |
Глава 5 |
|
|
или |
; правило 6 |
|
(ДАННЫЕ NIL) и (ОБРАЗЕЦ NIL) |
|
и (CAR ОБРАЗЕЦ) = $ |
|
и ОБРАЗЕЦ :=: (CDR ДАННЫЕ) |
Введенные правила рекурсивно определяют понятие “сопоставимость”. Первое правило утверждает сопоставимость пустых списков и обеспечивает выход из рекурсии. Правило 2 позволяет сопоставить списки ($) и ( ), ($ $) и ( ), … , т.е. в этом случае символ ‘$’ не отождествляется ни с одним из элементов списка данных. Третье правило утверждает, что образец сопоставим с данными, если первый элемент образца равен первому элементу данных, и хвостовые части образца и данных сопоставимы. Например,
(a |
b |
c |
d) :=: (a |
b |
c |
d). |
Правило 4 обеспечивает сопоставление образца с данными, если первый элемент образца представлен символом ‘?’, и хвостовые части образца и данных сопоставимы:
(? |
b |
c |
d) :=: (a |
b |
c |
d). |
Правила 5 и 6 позволяют учесть особенности сопоставления символа ‘$’. В соответствии с правилом 5 символ ‘$’ может не отождествляться ни с одним из элементов списка данных. Например,
($ |
b |
c |
d) :=: (a |
b |
c). |
Правило 6 сопоставляет с символом ‘$’ сегмент списка данных. Например,
($ |
d) :=: (a |
b |
c |
d). |
В этом случае символ ‘$’ отождествляется с последовательностью элементов a, b, c.
Определим функцию (MATCH P D), выполняющую сопоставление образца P и данных D с помощью введенных выше правил:
; функция сопоставления образца p и данных d
(defun match (p d) (cond
;правило 1
((and (null p)(null d)) t)
;правило 2 ((and (null d)
(eq (car p) ’$)
(null (cdr p))) t)
Язык Лисп |
297 |
|
|
; один из списков исчерпан
((or (null p)(null d)) nil)
;правило 3 и правило 4 ((or (equal (car p) ’?)
(equal (car p) (car d))) (match (cdr p)(cdr d)))
;правило 5 и 6 ((eq (car p) ’$)
(cond ((match (cdr p) d) t) ((match p (cdr d)) t))) ))
Здесь предварительное условие для правил 3 – 6 – (ДАННЫЕ NIL) и (ОБРАЗЕЦ NIL) – записано перед правилом 3 в виде:
((or (null p)(null d)) nil)
Если один из списков p или d исчерпан, то это исключает применение последующих правил, и функция MATCH возвращает NIL.
Функцию MATCH можно легко расширить, чтобы она могла сопоставлять списки с вложенными подсписками. Для этого достаточно добавить в текст функции следующее правило:
;правило 7 - сопоставление списков,включающих подсписки
((and (not (atom (car p)))
(not (atom (car d))) (match (car p)(car d)))
(match (cdr p) (cdr d)) )
В этом случае функция MATCH будет обеспечивать сопоставление более сложных символьных структур. Например,
(match ’( (a |
b) ? (? |
d) |
$) |
’( (a |
b) c (c |
d) |
1 2 3 4 5)) |
T |
|
|
|
Несмотря на то, что введенная функция MATCH реализует простейшую схему сопоставления, её уже можно использовать для решения практических задач, например, доступа к базе данных. Определим для этого функцию GET-MATCHES с двумя аргументами, представленными s- выражениями. Первый аргумент будет представлять собой запрос к базе данных, записываемый в виде образца, а второй – список, соответствующий базе данных, в которой выполняется поиск. Функция GET-MATCHES
Язык Лисп |
299 |
|
|
‘$’. Например, ?V или $V. В первом случае в переменную V подставляется произвольный символ данных, во втором – сегмент списка данных.
Расширим возможности функции MATCH так, чтобы она могла запоминать подстановки в переменных. Например:
(match ’(AVTO ?MODEL ?YEAR) '(AVTO ford 1998)) T model ford
year 1998
Сопоставления с конструкциями ?V или $V могут быть реализованы с помощью двух новых функций – CAR-LETTER и CDR-NAME. Функция CAR-LETTER обеспечивает выделение первой литеры из имени очередного атома образца, а функция CDR-NAME формирует имя переменной из оставшихся литер:
(defun car-letter (x) (car (coerce (string x) ’list)))
(defun cdr-name (x)
(intern (coerce (cdr (coerce (string x) ’list)) ’string)))
Здесь x – атом образца, который с помощью вызова (string x) преобразуются в строку. Затем из строки выделяются первая литера (функция carletter) и все оставшиеся литеры (функция cdr-name), которые образуют имя переменной. Полученное имя переменной с помощью функции INTERN включается в список имён символов. Функция INTERN проверяет, есть ли символ с данным именем в списке имен символов. Если такой символ имеется, то функция возвращает ссылку на имя этого символа, если нет, то добавляет новый символ в список имен и возвращает ссылку на него.
Добавим в функцию МАТСН правило, обеспечивающее сопоставление очередного элемента данных с параметром вида ?V:
;правило 8
((and (atom (car p))
(eq (car-letter (car p)) #\?) (match (cdr p)(cdr d)))
(set (cdr-name (car p)) (car d) t)
В соответствии с правилом сначала убеждаемся, что очередной элемент образца представляет собой атом, начинающийся с литеры #\?, а также проверяем сопоставимость хвостовых частей образца и данных. Если указанные проверки выполняются успешно, то переменной присваивается значение очередного элемента списка данных.
Для обработки параметра образца вида $V введем в функцию МАТСН дополнительное правило: