- •Аннотация
- •Лабораторная работа № 1 «Логическое программирование задачи поиска пути на конечных графах пространства состояний»
- •1.1.1. Прямой поиск (от исходной вершины до целевой вершины).
- •Самостоятельная часть работы
- •1.1.2. Обратный поиск (от целевой вершины к исходной).
- •Самостоятельная часть работы.
- •Самостоятельная часть работы
- •Задание №1.3. Поиск пути в неориентированном графе.
- •Самостоятельная часть работы
- •Задание №1.4. Поиск в псевдо бинарном дереве.
- •Самостоятельная часть работы
- •Самостоятельная часть работы
- •Задание 1.6. Поиск пути в ориентированном ациклическом графе с обходом вершин «в ширину».
- •I j k
- •Самостоятельная часть работы.
- •Задание № 1.7. Программирование поиска в глубину на конечном графе пространства состояний на языке clips
- •Самостоятельная часть работы
Самостоятельная часть работы
Составьте протокол поиска другой целевой вершины.
Поменяйте порядок расположения фактов. Убедитесь, что порядок обхода, но не результат, зависит от порядка расположения фактов в программе.
Добавьте транзитивные рёбра, например, edge(d,i), и убедитесь, что программа отыскивает все альтернативные пути. Объясните, как это происходит .
Введите цель в тексте программы посредством команды Goalpath(a,i,L). Как увидеть теперь результат на экране? Как вывести все альтернативные решения?
1.1.2. Обратный поиск (от целевой вершины к исходной).
Приведенная ниже программа обратного поиска rev_search на первый взгляд очень напоминает предыдущую, изменено только имя предиката:
rev_search(X,X,[X]).
rev_search(X,Y,[Y|P]):-edge(Z,Y), rev_search(X,Z,P).
Отличие заключается, во-первых, в порядке расположения аргументов предиката edge. Здесь свободная переменная Z является 1-м аргументом, т.к. отыскивается родительская вершина для текущей вершины Y. Поэтому программа будет отыскивать по фактам edge вершину Z, являющуюся прямымпредшественникомданной вершины Y (а не потомком вершины Х, как было ранее).
Во-вторых, изменён порядок аргументов в вызывающем предикате rev_search(X,Z,P): на первом месте - заданная начальная вершина X (так как ищем путь к нейотY). Второй аргумент - найденная с помощью факта edge очередная родительская вершинаZискомого обратного пути.
В-третьих, в головном предикате rev_search(X,Y,[Y|P]) в список добавляется вершинаY, а неX, как было в прямом поиске.
Рассмотрим протокол поиска, задав на том же графе (см. п.1.1.1.) аналогичную цель поиска rev_search(a,i,L). Первое предложение не подходит, потому чтоa≠i.
По второму предложению правила rev_search, подставляя в него каждый раз значения переменных и констант из очередной цели, а вместо переменнойZ- её значение из фактовedge, имеем:
rev_search(a, i, [i|P]): - edge(f, i), rev_search(a, f, P).
rev_search(a, f, [f|P1]): - edge(c, f), rev_search(a, c, P1).
rev_search(a, c, [c|P2]): - edge(a, c), rev_search(a, a, P2).
Теперь подходит первое предложение:
rev_search(a,a,[a])., т.е. P2=[a].
Возврат рекурсии позволяет означить переменные в стеке:
P1 = [c|P2] = [c, a]; P = [f|P1] = [f, c, a]; L = [i|P] = [i, f, c, a].
Список пути получается обратным. При выводе на печать можно преобразовать его в прямой, но эту задачу решите самостоятельно.
Преимущества обратного поиска заключаются в том, что граф не надо обходить: у каждой вершины дерева только одна родительская вершина-предшественница и, идя от цели Y, мы сразу отыскиваем кратчайший к вершинеX(если таковой существует, разумеется).
Если граф имеет транзитивные рёбра, и как минимум у одной из вершин окажется более одной предшествующей, программа должна отыскать все альтернативные пути из Y в X.
Самостоятельная часть работы.
Напишите протокол поиска пути от Yк Х.
Введите в граф транзитивные рёбра, например, edge(d, i).
Попытайтесь повысить полезность программы lab1_1.pro, придав ей следующие дополнительные функции:
определения корневой вершины графа;
вывода на печать прямого списка пути.
Задание №1.2. Поиск пути в ориентированном графе с циклом.
Рассмотрим циклический граф и соответствующий список фактов:
edge(x,y).
x
edge(y,z).
edge(z,x).
y
z
edge(y,u).
edge(z,v).
v
u
Если поставить цель path(x, v, Path) и применить программу из задания №1.1, то решение найдено не будет, так как возникает цикл x,y,z,x,y,z..., ведущий к переполнению стека. В данном случае решение можно получить, если факт edge(z,x) поставить в конец списка. Но такой прием не всегда очевиден, особенно при большом графе и не единственном цикле.
Общее решение состоит во введении в предикат path четвертого аргумента Vis, представляющего собой список посещенных в процессе поиска вершин (visited). При этом в тело правила рекурсии path следует ввести проверку дополнительного условия о не членстве очередной проверяемой вершины N в этом списке cпомощью правилаmember. Правила будут выглядеть теперь следующим образом:
way(X, Y, P): - path(X, Y, P, [X]).
path(X, X, [X],_).
path(X, Y, [X|P], Vis,]): - edge(X, N), not(member(N, Vis)), path(N, Y, P, [N|Vis]).
member(X, [X|_]).
member(X, [_|Ys]): - member(X, Ys).
Целевым здесь является предикат way. Поставим цель: way(x, v, Path).
Первое предложение приведенного набора правил переопределяет цель, вводя четвёртый аргумент - список посещённых вершин - и задаёт его начальное значение Vis = [X].
Второе предложение является обычным условием окончания рекурсии. Третье предложение – основное рекурсивное правило поиска. Правило member проверяет членство текущей вершины N в списке Vis. Если её нет в этом списке, member терпит неудачу, а выражение not(member(N, Vis)) принимает значение «истина». Вершина N добавляется в голову списка Vis последним предикатом path в теле основного правила, и идет рекурсивное обращение к голове того же правила в том случае, если очередная проверяемая вершина N не совпадёт с конечной вершиной пути Y. Если такое совпадение происходит, то идет обращение к условию окончания рекурсии (предложению 2 набора правил). Аргументы этого правила принимают значения (в данном примере) path(v,v,[v],Vis), где Vis есть список всех посещенных вершин на пути из x в v. Все правило принимает значение «истина», и прямой ход рекурсии на этом заканчивается. Далее идёт возврат рекурсии, при этом в голову списка [Х|P] на каждом “этаже” стека по очереди добавляются в обратном порядке пройденные вершины, а список Vis, наоборот, укорачивается до начальной вершины [X], в данном случае до [x]. Тело первого предложения правила в итоге принимает значение «истина», а значит, истинна и цель way(x, v, Path), где в переменной Path будет содержаться искомый путь.