- •Иван Братко Программирование на языке Пролог для искусственного интеллекта
- •От редактора перевода
- •Предисловие
- •Предисловие автора
- •Благодарности
- •Упражнения
- •1.2. Расширение программы-примера с помощью правил
- •Упражнения
- •1.3. Рекурсивное определение правил
- •Упражнение
- •1.4. Как пролог-система отвечает на вопросы
- •Упражнение
- •1.5. Декларативный и процедурный смысл программ
- •Литература
- •Глава 2 Синтаксис и семантика Пролог-программ
- •2.1. Объекты данных
- •2.1.1. Атомы и числа
- •2.1.2. Переменные
- •2.1.3. Структуры
- •Упражнения
- •2.2. Сопоставление
- •Упражнения
- •2.3. Декларативный смысл пролог-программ
- •Упражнения
- •2.4. Процедурная семантика
- •Программа
- •Шаги вычисления
- •Упражнение
- •2.5. Пример: обезьяна и банан
- •2.6. Порядок предложений и целей
- •2.6.1. Опасность бесконечного цикла
- •2.6.2. Варианты программы, полученые путем переупорядочивания предложений и целей
- •2.6.3. Сочетание декларативного и процедурного подходов
- •2.7. Замечания о взаимосвязи между Прологом и логикой
- •Упражнение
- •Литература
- •Глава 3 Списки, операторы, арифметика
- •3.1. Представление списков
- •3.2. Некоторые операции над списками
- •3.2.1. Принадлежность к списку
- •3.2.2. Сцепление (конкатенация)
- •Упражнения
- •3.2.3. Добавление элемента
- •3.2.4. Удаление элемента
- •3.2.5. Подсписок
- •3.2.6. Перестановки
- •Упражнения
- •3.3. Операторная запись (нотация)
- •Упражнения
- •3.4. Арифметические действия
- •Упражнения
- •Глава 4 Использование структур: примеры
- •4.1. Получение структурированной информации из базы данных
- •Упражнения
- •4.2. Абстракция данных
- •Упражнение
- •4.3. Моделирование недетерминированного автомата
- •Упражнения
- •4.4. Планирование поездки
- •4.5. Задача о восьми ферзях
- •4.5.1. Программа 1
- •Упражнение
- •4.5.2. Программа 2
- •4.5.3. Программа 3
- •4.5.4. Заключительные замечания
- •Упражнение
- •Глава 5 Управление перебором
- •5.1. Ограничение перебора
- •5.1.1. Эксперимент 1
- •5.1.2. Эксперимент 2
- •5.2. Примеры, использующие отсечение
- •5.2.1. Вычисление максимума
- •5.2.2. Процедура проверки принадлежности списку, дающая единственное решение
- •5.2.3. Добавление элемента к списку, если он в нем отсутствует (добавление без дублирования)
- •5.2.4. Задача классификации объектов
- •Упражнения
- •5.3. Отрицание как неуспех
- •Упражнения
- •5.4. Трудности с отсечением и отрицанием
- •6.2. Обработка файлов термов
- •6.2.2. Вывод списков
- •6.2.3. Формирование термов
- •6.2.4. Обработка произвольного файла термов
- •Упражнения
- •6.3. Обработка символов
- •Упражнение
- •6.4. Создание и декомпозиция атомов
- •Упражнения
- •6.5. Ввод программ: consult, reconsult
- •Глава 7 Другие встроенные процедуры
- •7.1. Проверка типов термов
- •7.1.1. Предикаты var , nоnvar , atom , integer , atomic
- •7.1.2. Решение числового ребуса с использованием nonvar
- •Упражнения
- •Упражнения
- •7.3. Различные виды равенства
- •7.4. Работа с базой данных
- •Упражнения
- •7.5. Средства управления
- •Упражнения
- •Глава 8 Стиль и методы программирования
- •8.1. Общие принципы хорошего программирования
- •8.2. Как представлять себе программы на Прологе
- •8.2.1. Использование рекурсии
- •8.2.2. Обобщение
- •8.2.3. Использование рисунков
- •8.3. Стиль программирования
- •8.3.1. Некоторые правила хорошего стиля
- •8.3.2. Табличная организация длинных процедур
- •8.3.3. Комментирование
- •8.4. Отладка
- •8.5. Эффективность
- •8.5.1. Повышение эффективности решения задачи о восьми ферзях
- •8.5.2. Повышение эффективности программы раскраски карты
- •8.5.3. Повышение эффективности конкатенации списков за счет совершенствования структуры данных
- •8.5.4. Повышение эффективности зa счет добавления вычисленных фактов к базе данных
- •Упражнения
- •9.1. Представление списков. Сортировка
- •9.1.1. Замечания в некоторых альтернативных способах представления списков
- •Упражнения
- •9.1.2. Сортировка списков
- •Упражнения
- •9.2. Представление множеств двоичными деревьями
- •Упражнения
- •9.3. Двоичные справочники: добавление и удаление элемента
- •9.4. Отображение деревьев
- •Упражнение
- •9.5. Графы
- •9.5.1. Представление графов
- •9.5.2. Поиск пути в графе
- •9.5.3. Построение остовного дерева
- •Упражнение
- •Литература
- •Глава 10 Усовершенствованные методы представления множеств деревьями
- •10.1. Двоично-троичные справочники
- •Упражнение
- •Литература
- •Глава 11. Основные стратегии решения задач
- •11.1. Предварительные понятия и примеры
- •11.2. Стратегия поиска в глубину
- •Упражнения
- •11.3. Поиск в ширину
- •11.3.1. Списковое представление множества кандидатов
- •11.3.2. Древовидное представление множества кандидатов
- •Упражнения
- •11.4. Замечания относительно поиска в графах, оптимальности к сложности
- •Литература
- •Глава 12 Поиск с предпочтением: эвристический поиск
- •12.1. Поиск с предпочтением
- •Упражнение
- •12.2. Поиск c предпочтением применительно к головоломке "игра в восемь"
- •Упражнение
- •12.3. Применение поиска с предпочтением к планированию выполнения задач
- •Литература
- •Глава 13 Сведение задач к подзадачам. И/или-Графы
- •13.1. Представление задач в виде и/или-графов
- •13.2. Примеры и/или-представления задач
- •13.2.1. И/или-представление задачи поиска маршрута
- •13.2.2. Задача о ханойской башне
- •13.2.3. Формулировка игровых задач в терминах и/или-графов
- •13.3. Базовые процедуры поиска в и/или-графах
- •Упражнения
- •13.4. Поиск с предпочтением в и/или-графах
- •13.4.1. Эвристические оценки и алгоритм поиска
- •13.4.2. Программа поиска
- •13.4.3. Пример отношений, определяющих конкретную задачу: поиск маршрута
- •Упражнение
- •Литература
- •Глава 14 Экспертные системы
- •14.1. Функции, выполняемые экспертной системой
- •14.2. Грубая структура экспертной системы
- •14.3. Правила типа "если-то" для представления знаний
- •Упражнения
- •14.4. Разработка оболочки
- •14.4.1. Процесс рассуждений
- •14.4.2. Формирование ответа на вопрос "почему"
- •14.4.3. Формирование ответа на вопрос "как"
- •14.5. Реализация
- •14.5.1. Процедура рассмотреть
- •14.5.2. Процедура ответпольз
- •14.5.3. Усовершенствование процедуры ответпольз
- •14.5.4. Процедура выдать
- •14.5.5. Драйвер верхнего уровня
- •14.5.6. Одно замечание по поводу программы-оболочки
- •14.5.7. Цели с отрицанием
- •Упражнение
- •14.6. Работа с неопределенностью
- •14.6.1. Степень достоверности
- •14.6.2. Модель Prospector'а
- •14.6.3. Принципы реализации
- •14.7. Заключительные замечания
- •Проекты
- •Литература
- •Глава 15 Игры
- •15.1. Игры двух лиц с полной информацией
- •15.2. Минимаксный принцип
- •15.3. Альфа-бета алгоритм: эффективная реализация минимаксного принципа
- •15.4. Минимаксные игровые программы: усовершенствования и ограничения
- •15.5. Знания о типовых ситуациях и механизм "советов"
- •15.5.1. Цели и ограничения на ходы
- •15.5.2. Выполнимость совета
- •15.5.3. Правила и таблицы советов
- •15.6. Программа на языке al0 для игры в шахматном эндшпиле
- •15.6.1. Миниатюрный интерпретатор языка al0
- •15.6.2. Программа на языке советов для эндшпиля "король и ладья против короля"
- •Литература
- •Глава 16 Программирование в терминах типовых конфигураций
- •16.1. Архитектура, ориентированная на типовые конфигурации
- •16.1.1. Основные понятия
- •16.1.2. Прологовские программы как системы, управляемые образцами
- •16.1.3. Пример составления программы
- •16.2. Простой интерпретатор программ, управляемых образцами
- •16.3. Простая программа для автоматического доказательства теорем
- •16.4. Заключительные замечания
- •Литература
- •Ответы к некоторым упражнениям Глава 1
- •Глава 2
- •Глава 3
- •Глава 4
- •Глава 5
- •Глава 6
- •Глава 7
- •Глава 8
- •Глава 9
- •Глава 10
- •Глава 11
Упражнения
4.4. Почему не могло возникнуть зацикливание модели исходного автомата на рис. 4.3, когда в его графе переходов не было "спонтанного цикла"?
4.5. Зацикливание при вычислении допускается можно предотвратить, например, таким способом: подсчитывать число переходов, сделанных к настоящему моменту. При этом модель должна будет искать пути только некоторой ограниченной длины. Модифицируйте так отношение допускается. Указание: добавьте третий аргумент — максимально допустимое число переходов:
допускается( Состояние, Цепочка, Макс_переходов)
4.4. Планирование поездки
В данном разделе мы создадим программу, которая дает советы по планированию воздушного путешествия. Эта программа будет довольно примитивным советчиком, тем не менее она сможет отвечать на некоторые полезные вопросы, такие как:
• По каким дням недели есть прямые рейсы из Лондона в Любляну?
• Как в четверг можно добраться из Любляны в Эдинбург?
• Мне нужно посетить Милан, Любляну и Цюрих; вылетать нужно из Лондона во вторник и вернуться обратно в Лондон в пятницу. В какой последовательности мне следует посещать эти города, чтобы ни разу на протяжении поездки не пришлось совершать более одного перелета в день.
Центральной частью программы будет база данных, содержащая информацию о рейсах. Эта информация будет представлена в виде трехаргументного отношения:
расписание( Пункт1, Пункт2, Список_рейсов)
где Список_рейсов — это список, состоящий из структурированных объектов вида:
Время_отправления / Время_прибытия / Номер_рейса
/ Список_дней_вылета
Список_дней_вылета — это либо список дней недели, либо атом "ежедневно". Одно из предложений, входящих в расписание могло бы быть, например, таким:
расписание( лондон, эдинбург,
[ 9:40 / 10:50 / bа4733/ ежедневно,
19:40 / 20:50 / bа4833 / [пн, вт, ср, чт, пт, сб]] ).
Время представлено в виде структурированных объектов, состоящих из двух компонент — часов и минут, объединенных оператором ":".
Главная задача состоит в отыскании точных маршрутов между двумя заданными городами в определенные дни недели. Ее решение мы будем программировать в виде четырехаргументного отношения:
маршрут( Пункт1, Пункт2, День, Маршрут)
Здесь Маршрут — это последовательность перелетов, удовлетворяющих следующим критериям:
(1) начальная точка маршрута находится в Пункт1;
(2) конечная точка — в Пункт2;
(3) все перелеты совершаются в один и тот же день недели — День;
(4) все перелеты, входящие в Маршрут, содержатся в определении отношения расписание;
(5) остается достаточно времени для пересадки с рейса на рейс.
Маршрут представляется в виде списка структурированных объектов вида
Откуда - Куда : Номер_рейса : Время_отправления
Мы еще будем пользоваться следующими вспомогательными предикатами:
(1) рейс( Пункт1, Пункт2, День, N_рейса, Вр_отпр, Вр_приб)
Здесь сказано, что существует рейс N_рейса между Пункт1 и Пункт2 в день недели День с указанными временами отправления и прибытия.
(2) вр_отпр( Маршрут, Время)
Время — это время отправления по маршруту Маршрут.
(3) пересадка( Время1, Время2)
Между Время1 и Время2 должен существовать промежуток не менее 40 минут для пересадки с одного рейса на другой.
Задача нахождения маршрута напоминает моделирование недетерминированного автомата из предыдущего раздела:
• Состояния автомата соответствуют городам.
• Переход из состояния в состояние соответствует перелету из одного города в другой.
• Отношение переход автомата соответствует отношению расписание.
• Модель автомата находит путь в графе переходов между исходным и конечным состояниями; планировщик поездки находит маршрут между начальным н конечным пунктами поездки.
Неудивительно поэтому, что отношение маршрут можно определить аналогично отношению допускает, с той разницей, что теперь нет "спонтанных переходов". Существуют два случая:
(1) Прямой рейс: если существует прямой рейс между пунктами Пункт1 и Пункт2, то весь маршрут состоит только из одного перелета:
маршрут( Пункт1, Пункт2, День, [Пункт1-Пункт2 : Nр : Отпр]) :-
рейс( Пункт1, Пункт2, День, Np, Отпр, Приб).
(2) Маршрут с пересадками: маршрут между пунктами P1 и Р2 состоит из первого перелета из P1 в некоторый промежуточный пункт Р3 и маршрута между Р3 и Р2. Кроме того, между окончанием первого перелета и отправлением во второй необходимо оставить достаточно времени для пересадки.
маршрут( P1, Р2, День, [P1-Р3 : Nр1 : Отпр1 | Маршрут]) :-
маршрут( Р3, Р2, День, Маршрут ),
рейс( P1, Р3, День, Npl, Oтпpl, Приб1),
вр_отпр( Маршрут, Отпр2),
пересадка( Приб1, Отпр2).
Вспомогательные отношения рейс, пересадка и вр_отпр запрограммировать легко; мы включили их в полный текст программы планировщика поездки на рис. 4.5. Там же приводится и пример базы данных расписания.
Наш планировщик исключительно прост и может рассматривать пути, очевидно ведущие в никуда. Тем не менее его оказывается вполне достаточно, если база данных о рейсах самолетов невелика. Для больших баз данных потребовалось бы разработать более интеллектуальный планировщик, который мог бы справиться с большим количеством путей, участвующих в перебора при нахождении нужного пути.
% ПЛАНИРОВЩИК ВОЗДУШНЫХ МАРШРУТОВ
:- op( 50, xfy, :).
рейс( Пункт1, Пункт2, День, Np, ВрОтпр, ВрПриб) :-
расписание( Пункт1, Пункт2, СписРейсов),
принадлежит( ВрОтпр / ВрПриб / Nр / СписДней, СписРейсов),
день_выл( День, СписДней).
принадлежит( X, [X | L] ).
принадлежит( X, [Y | L] ) :-
принадлежит( X, L ).
день_выл( День, СписДней) :-
принадлежит( День, СписДней).
день_выл( День, ежедневно) :-
принадлежит( День, [пн, вт, ср, чт, пт, сб, вс] ).
маршрут( P1, P2, День, [P1-Р2 : Np : ВрОтпр] ) :-
% прямой рейс
рейс( P1, P2, День, Np, ВрОтпр, _ ).
маршрут( P1, Р2, День, [Pl-P3 : Np1 : Oтпp1 | Маршрут]) :-
% маршрут с пересадками
маршрут( Р3, P2, День, Маршрут ),
рейс( P1, Р3, День, Npl, Oтпp1, Приб1),
вр_отпр( Маршрут, Отпр2),
пересадка( Приб1, Отпр2).
вр_отпр( [P1-Р2 : Np : Отпр | _ ], Отпр).
пересадка( Часы1 : Минуты1, Часы2 : Минуты2) :-
60 * (Часы2-Часы1) + Минуты2 - Минуты1 >= 40
% БАЗА ДАННЫХ О РЕЙСАХ САМОЛЕТОВ
расписание( эдинбург, лондон,
[ 9:40 / 10:50 / bа4733 / ежедневно,
13:40 / 14:50 / ba4773 / ежедневно,
19:40 / 20:50 / bа4833 / [пн, вт, ср, чт, пт, вс] ] ).
расписание( лондон, эдинбург,
[ 9:40 / 10:50 / bа4732 / ежедневно,
11:40 / 12:50 / bа4752 / ежедневно,
18:40 / 19:50 / bа4822 / [пн, вт, ср, чт, пт] ] ),
расписание( лондон, любляна,
[13:20 / 16:20 / ju201 / [пт],
13:20 / 16:20 / ju213 / [вс] ] ).
расписание( лондон, цюрих,
[ 9:10 / 11:45 / bа614 / ежедневно,
14:45 / 17:20 / sr805 / ежедневно ] ).
расписание( лондон, милан,
[ 8:30 / 11:20 / bа510 / ежедневно,
11:00 / 13:50 / az459 / ежедневно ] ).
расписание( любляна, цюрих,
[11:30 / 12:40 / ju322 / [вт,чт] ] ).
расписание( любляна, лондон,
[11:10 / 12:20 / yu200 / [пт],
11:25 / 12:20 / yu212 / [вс] ] ).
расписание( милан, лондон,
[ 9:10 / 10:00 / az458 / ежедневно,
12:20 / 13:10 / bа511 / ежедневно ] ).
расписание( милан, цюрих,
[ 9:25 / 10:15 / sr621 / ежедневно,
12:45 / 13:35 / sr623 / ежедневно ] ).
расписание( цюрих, любляна,
[13:30 / 14:40 / yu323 / [вт, чт] ] ).
расписание( цюрих, лондон,
9:00 / 9:40 / bа613 /
[ пн, вт, ср, чт, пт, сб],
16:10 / 16:55 / sr806 / [пн, вт, ср, чт, пт, сб] ] ).
расписание( цюрих, милан,
[ 7:55 / 8:45 / sr620 / ежедневно ] ).
Рис. 4.5. Планировщик воздушных маршрутов и база данных о рейсах самолетов.
Вот некоторые примеры вопросов к планировщику:
• По каким дням недели существуют прямые рейсы из Лондона в Люблину?
?- рейс( лондон, любляна, День, _, _, _ ).
День = пт;
День = сб;
no (нет)
• Как мне добраться из Любляны в Эдинбург в четверг?
?- маршрут( любляна, эдинбург, чт, R).
R = [любляна-цюрих : уu322 : 11:30, цюрих-лондон:
sr806 : 16:10,
лондон-эдинбург : bа4822 : 18:40 ]
• Как мне посетить Милан, Любляну и Цюрих, вылетев из Лондона во вторник и вернувшись в него в пятницу, совершая в день не более одного перелета? Этот вопрос сложнее, чем предыдущие. Его можно сформулировать, использовав отношение перестановка, запрограммированное в гл. 3. Мы попросим найти такую перестановку городов Милан, Любляна и Цюрих, чтобы соответствующие перелеты можно было осуществить в несколько последовательных дней недели:
?- перестановка( [милан, любляна, цюрих],
[Город1, Город2, Город3] ),
рейс( лондон, Город1, вт, Np1, Oтпp1, Пpиб1),
peйc( Город1, Город2, ср, Np2, Отпр2, Приб2),
рейс( Город2, Город3, чт, Np3, Отпp3, Приб3),
рейс( Город3, лондон, пт, Np4, Отпр4, Приб4).
Город1 = милан
Город2 = цюрих
Город3 = любляна
Np1 = ba510
Отпр1 = 8:30
Приб1 = 11:20
Np2 =sr621
Отпр2 = 9:25
Приб2 = 10:15
Np3 = yu323
Отпр3 = 13:30
Приб3 = 14:40
Np4 = yu200
Отпр4 = 11:10
Приб4 = 12:20
