Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Prolog.doc
Скачиваний:
31
Добавлен:
12.03.2015
Размер:
929.28 Кб
Скачать

10.2. Пример разработки системы: деревья решений

В данном разделе мы разработаем систему для построения и об­хода дерева решении. На рис. 10.2.1 показан фрагмент дерева реше­ний, позволяющий выбрать нужную персональную ЭВМ среди ком­пьютеров, выпущенных в 1986 г. Вершины дерева решений содержат вопросы вида «Должна ли персональная ЭВМ быть портативной ?» или заключения «Наиболее подходящей является портативная персональная ЭВМ фирмы IBM». В зависимости от отпета на вопрос дуги, выходящие из вершины, определяют, какой должен быть задан следующий вопрос или какое будет получено заключение. Так, если на вопрос «Портативная ?» (см. дерево решений на рис. 10.2.1) мы от­ветим «да», то попадаем в вершину «Карманная ?». При ответе «нет» дуга направляет нас в узел «IBM-совместимая ? ».

Рис. 10.2.1. Дерево решений

Описание дерева решений

Создать описание дерева решений позволяют факты вида

верш(Верш_ид, Вопрос, Дуги).

где Верш_ид - идентифицирует вершину. Вопрос - это вопрос или решение, а Дуги - список дуг, выходящих из вершины. Вⲓпрос определяется списком строк, каждая из которых имеет тип «строка». Дуга представляется термом дуга(Отв,Верш_ид), означающим, что при ответе Отв следует перейти к вершине с идентификатором Верш_ид. Вершина «Портативная ?» (см. рис. 10.2.1) .записывается следующим образом:

верш(1,["Портативная ?'."], [дуга (да,2),дуга(нет,3)]).

Мы присвоили идентификаторы 1, 2 и 3 вершинам «Портативная ?», «Карманная ?» и «IBM-совместимая?» соответственно.

Дерево решений не должно иметь циклов. Например, дерево

не является правильным деревом решений.

Разработка системы

Мы будем разрабатывать систему в два этапа. Сначала попыта­емся создать удобный интерфейс для пользователя, а затем напишем целевые утверждения для построения и обхода дерева решений.

Этап 1: интерфейс. Спроектируем меню возможных действий пользователя. Каждое действие будет осуществляться соответствую­щим утверждением выполнить.

меню :-

repeat,nl,

write('Хотите ли вы'),nl,

write('1) загрузить дерево'),nl,

write('2) сохранить дерево'),nl,

write('3) добавить вершину'),nl,

write('4) убрать вершину'),nl,

wrile('5) добавить дугу'),nl,

write('6) убрать дугу'),nl,

write('7) просмотреть дерево'),nl,

write('8) совершить обход дерева'),nl,

write('9) стоп'),nl,

write('Введите 1-9'),nl,

write("),nl,

геаl(Вариант),nl,

выполнить(Вариант).

Утверждение выполнить должно закончиться возникновением состо­яния неудачи для всех вариантов от 1 до 8. чтобы после совершения требуемого действия на экране снова показалось меню. Нужное утверждение выбирается при сопоставлении цели выполнить(Вариант) с головой утверждения.

Дерево загружается с помощью предиката reconsult:

выполнить(1) :-

write('имя файла ?'),

readb(Ф),

nl,write('чтение файла...'),

write(Ф),

reconsult(Ф),

write('...закончилось'),nl,!,fail.

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

выполнить(2) :-

write('имя файла ?'),

readb(Ф),

open (Ф,блок,write),!,

nl,write ('запись в файл...'),

write(Ф),

assertz (верш(-1,-1,-1)),

верш(Вид,В,Дуги),

сохр_верш(Вид,В,Дуги,блок),

write ('...закончилась'),nl,

close(блок),!,fail.

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

Вьполнить(3) :-

чит_нов_ид(Вид),

write('Введите вопрос (в конце

напишите слово ' конец'):'),nl,

чит_вопрос(В),

чит_дуги(Дуги),

not (цикл (Вид, Дуги)),

assertz (верш (Вид, В, Дуги)),!, fail.

Вершина уничтожается следующим образом:

выполнить(4) :-

чит_стар_ид(Вид),

уничт_верш(Вид),!,fail.

Здесь чит_стар_ид(Вид) вводит идентификатор существующей вер­шины.

Дуга, выходящая из вершины, добавляется в том случае, если при этом не образуется цикл:

выполнить(5) :-

write('Из вершины'),

чит_стар_ид(Вид),

чит_дугу(Дуга),

not(цикл (Вид, [Дуга])),

доб_дугу (Вид,Дуга),!,fail.

Уничтожение дуги:

выполнить(6) :-

чит_стар_ид(Вид),

чит_дугу(Дуга),

уничт_дугу(Вид,Дуга),',fail.

Показ дерева на экране дисплея:

выполнить,(7) :-

чит_стар_ид(Вид),nl,

write ('Просмотр дерева'),nl,

показ_дерева(Вид),!,fail.

Обход дерева:

выполнить(8) :-

чит_стар_ид (Вид),

обход(Вид),!,fаil.

Выход из меню:

выполнить(9) :- nl,write('Дo свидания'),nl,!.

Чтобы сохранить вершину, мы записываем ее в память при усло­вии, что она не является специальным маркером:

coxp_вepш(-l ,-1,-1,_) :- !,

retract(Bepш(-l,-l ,-1).

сохр_верш (Вид, В, Дуги, Б) : -

writeq(Б,верш(Вид,В,Дуги)),

write(Б,'.'),

nl(Б),!,fail.

Циклы в дереве возникают в том случае, если дуга из множества Дуги ведет к вершине, имеющей идентификатор Вид.

цикл (Вид,Дуги) :-

ведет (Вид,Дуги,Р),

write ('Дуги'), write (Дуги) ,nl.

write ('вызывают образование цикла:'),

write(P),nl.!.

Целевые утверждения уничт_верш, доб_дугу, уничт_дугу, об­ход, ведет определяются на второй стадии разработки. Описание пре­дикатов чит_нов_ид, чит_стар_ид и чит_дуги достаточно очевидно:

чит_нов_ид(Вид) :-

repeat,

write('Идеитификатор новой вepшины ?'),

геаdb(Вид),nl,

несуществ(Вид),nl.

чит_стар_ид(Вид) :-

repeat,

write('Идeнтификaтop вершины ?'),

readb(Вид),nl,

существует(Вид),!.

несуществ(Вид) :-

верш(Вид,_,_),

write ('Такой идентификатор уже есть !'),

nl,

!,fail.

несуществ(_).

существует(Вид) :- верш(Вид, _,_),!.

существует (_) :-

write('Такого идентификатора нет !'),

nl,

!,fail.

чит_дуги(Дуги) :-

repeat,

nl, write('Cколькo всего дуг ?'),

readb(N).nl,

N=0,

чит_н_дyги(N,Дyги).

чит_н_дуги(0, []) :-!.

чит_н_дуги(N, [Дуга | Дуги]) :-

M is N-1,

чит_н_дуг❽(М,Дуги),

write('Дуга'), wite(N),nl,nl,

чит_дугу(Дуга),!.

чит_дугу(дуга(Отв,Вид) :-

nl, write('Oтвeт ?'),

readb(Oтв),nl,

write('K вершине'),

чит_стар_ид(Вид).

Чтобы прочитать заданный вопрос, надо читать все строки на эк­ране до тех пор, пока не встретится строка 'конец'. Для чтения строк мы используем встроенный предикат readline.

чит_вопрос(В) :-

write('>'),

readline(строка),

продолжать (Строка, В).

продолжать('конец',[]) :-!.

продолжать(Строка, (Строка [В]) :-

чит_вопрос(В).

Вопрос выводится на дисплей построчно:

запис_вопр([]) :-!.

запис_вопр([H | T]) :-

nl,write(H),

запис_вопр(Т).

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

Дерево решений на экране будет иметь вид:

??

[Форма мяча ?]

овальная

[Мяч для игры в регби]

круглая

[размер ?]

большой

[Футбольный мяч]

маленький

[Крикетный мяч]

Предположим, задан список дуг. Из каждой дуги начинается свое дерево. Сначала на экране изображается первая дуга:

Отв

[Вид]

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

Дерево, вырастающее из вершины Вид, показывается, начиная с дуги дуга(??,Вид):

показ_дерево(Вид) :-

показ_дуги([дуга('?? ',Вид)]0,8).

Первым аргументом предиката показ_дуги является список дуг, вторым аргументом - уровень сдвига, а третьим - величина сдвига.

показ_дуги ([],_,_) :-!.

показ_дуги([дуга (Д, Вид) | Дуги |, М,S) :-

МI is M * S,

tab(MI),write(' '),

write (Д),nl,

tab(MI),

write ('_____________'),

write('['),

write (Вид),

write('] '),nl,

пок_дерево(Вид,М,S),

показ_дуги(Дуги,М,S).

пок_дерево(Вид,N,S) :-

верш(Вид,В,Дуги),

М is N + 1,

показ_дуги(Дуги,М,S).

Этап 2: целевые утверждения для построения и обхода дерева решений. При уничтожении вершины мы уничтожаем также все входящие в нее дуги:

уничт_верш(Вид) :-

retract (верш (Вид, В, Дуги)),

уничт_все_вх_дуги (Вид),

nl,write ('вершина уничтожена'),nl.

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

yнич_все_вх_дуги(Вид) :-

assertz(верш(-l,-1,-1)).

гetraсt(верш(Вид2,В,Дуги)),

уничт2(Вид2,В,Дуги,Вид),!.

уничт2(-1,-1,-1,_).

уничт2(Вид2,В,Дуги,Вид) :-

уничт_список(Дуги,дуга(Д,Вид),Дуги2),

assertz(верш (Вид2,В,Дуги2)),!,

fail.

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