Добавил:
natribu.org Все что нашел в интернете скидываю сюда Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Точно Не проект 2 / Не books / Источник_1

.pdf
Скачиваний:
10
Добавлен:
01.02.2024
Размер:
20.67 Mб
Скачать

290

Глава 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)

PUT-IN-LIST

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

298

Глава 5

 

 

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

Пусть рассматривается база данных, содержащая сведения о служа-

щих:

(setq *database*

’(((Иванов Сергей) 14000 1235)

((Абрамов Николай) 17500 3325) ((Гавриленко Ольга) 14000 3143)

((Денисенко Сергей) 22000 2528) ((Коль Татьяна) 18250 3133)

))

Вбазе данных хранятся данные о годовых доходах служащих и их табельные номера. Определим функцию GET-MATCHES, используя рекурсию:

(defun get-matches (p db) ;db - база данных

;p - запрос (образец) (cond ((null db) nil)

((match p (car db))

(cons (car db) (get-matches p (cdr db))))

(t (get-matches p (cdr db)))))

С помощью функции GET-MATCHES, можно получать ответы на следующие запросы:

;проверка наличия конкретной записи

(get-matches ’((Денисенко Сергей) 22000 2528) *database*)(((Денисенко Сергей) 22000 2528))

;выдать сведения о всех сотрудниках с годовым доходом 14000 (get-matches ’(? 14000 ?) *database*)

((( Иванов Сергей) 14000 1235) ((Гавриленко Ольга) 14000 3143))

;выдать сведения о всех сотрудниках, которых зовут Сергей

(get-matches ’((? Сергей) ? ?) *database*)

(((Иванов Сергей) 14000 1235) ((Денисенко Сергей) 22000 2528))

Приведенный выше вариант реализации функции MATCH, хотя и позволяет сопоставлять сложные символьные структуры, имеет ряд недостатков. Во-первых, подстановки в символы ‘?’ и ‘$’ не запоминаются. Вовторых, отсутствует возможность указать, что на определенных позициях списков могут находиться любые, но одинаковые выражения.

С целью устранения указанных недостатков введем в образец переменные. Имена переменных в образце будут начинаться с символов ‘?’ или

Язык Лисп

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 введем в функцию МАТСН дополнительное правило:

Соседние файлы в папке Не books