280 likes | 529 Views
Логическое программирование. Факультет Прикладной математики и физики Кафедра Вычислительной математики и программирования Московский авиационный институт (государственный технический университет). Пример. Описание условия. door(a,b). door(b,c). door(b,d). door(c,d). door(d,e).
E N D
Логическое программирование Факультет Прикладной математики и физики Кафедра Вычислительной математики и программирования Московский авиационный институт (государственный технический университет)
Описание условия door(a,b). door(b,c). door(b,d). door(c,d). door(d,e). door(d,f). door(f,g). door(h,i). move(X,Y) :- door(X,Y); door(Y,X).
Наивное решение path(X,X). path(X,Y) :- move(X,Z), path(Z,Y).
Предотвращение петель • Идея борьбы с зацикливанием – использовать список уже посещённых вершин и не посещать их повторно path(X,Y) :- path(X,Y,[X]). path(X,X,_). path(X,Y,L) :- move(X,Z), not(member(Z,L)), path(Z,Y,[Z|L]).
Задача поиска пути в графе • Графом (точнее, направленным графом) называется пара <U,V>, где U — множество вершин, VU× U — множество дуг. • В отличие от конечных графов, которые могут быть заданы матрицей смежности, мы будем рассматривать потенциально бесконечные графы, задаваемые генератором вершин move/2(in,out) • Путем в графе G=<U,V> из вершины aU в вершину bU называется последовательность {xi}i=0n, xi U, где x0=a, xn=b, и i <xi-1,xi> V. Задача поиска в графе ставится следующим образом: Пусть имеется граф G, заданный предикатом move/2. Необходимо по двум заданным вершинам a и b найти все пути (один путь, кратчайший путь и т.д.) из a в b в графе G.
Представление задачи в виде графа door(a,b). door(b,c). door(b,d). door(c,d). door(d,e). door(d,f). door(f,g). door(h,i). move(X,Y) :- door(X,Y); door(Y,X).
Совершенствуем поиск - 1 path(X,Y) :- path(X,Y,[X]). path(X,X,_). path(X,Y,L) :- move(X,Z), not(member(Z,L)), path(Z,Y,[Z|L]). path(X,Y,P) :- path(X,Y,[X],P). path(X,X,L,L). path(X,Y,L,R) :- move(X,Z), not(member(Z,L)), path(Z,Y,[Z|L],R).
Совершенствуем поиск - 2 path(X,Y,P) :- path(X,Y,[X],P). path(X,X,L,L). path(X,Y,L,R) :- move(X,Z), not(member(Z,L)), path(Z,Y,[Z|L],R). path(X,Y,P) :- path1([X],Y,P). path1([X|T],X,[X|T]). path1([X|T],Y,R) :- move(X,Z), not(member(Z,[X|T])), path([Z,X|T],Y,R).
Совершенствуем поиск - 3 path(X,Y,P) :- path1([X],Y,P). path1([X|T],X,[X|T]). path1([X|T],Y,R) :- move(X,Z), not(member(Z,[X|T])), path1([Z,X|T],Y,R). path(X,Y,P) :- path1([X],Y,P). prolong([X|T],[Y,X|T]) :- move(X,Y), not(member(Y,[X|T])). path1([X|T],X,[X|T]). path1(P,Y,R) :- prolong(P,P1), path1(P1,Y,R).
Поиск в глубину (Depth-First Search, DFS) • Поиск в глубину напоминает алгоритм действий заблудившегося в лабиринте человека • На каждом перекрестке идем по первой стене • В случае неудачи возвращаемся на шаг назад и пробуем другое направление path(X,Y,P) :- path1([X],Y,P). prolong([X|T],[Y,X|T]) :- move(X,Y), not(member(Y,[X|T])). path1([X|T],X,[X|T]). path1(P,Y,R) :- prolong(P,P1), path1(P1,Y,R).
Особенности • Расход по памяти – O(n) • Первым находится не обязательно кратчайший путь • Даже есть есть короткий путь к решению – он может быть не найден, а будут исследоваться длинные тупиковые ветки • Пример – кубик рубика • Естественная (простая) реализация алгоритма – за счет схожести механизмов обхода и вывода • Обход дерева вывода также делается в глубину!
Алгоритм фронта волны (Форда-Фалкерсона) • Идея – параллельно рассматривать все возможные пути по мере возрастания длины • [a] • [ab] • [abc] [abd] • [abcd] [abdc] [abdf] [abde] • [abcde] [abcdf] [abdfg] • [abcdfg] • Изначально алгоритм ФФ формулировался для матриц смежности => не подходит для потенциально бесконечных графов
Поиск в ширину - идея • Алгоритм ФФ подразумевает построение по множеству путей (фронту) длины n следующего фронта длины (n+1) • Такое продление требует на каждом шаге: • Рассматривать все текущие пути длины n • Для каждого генерировать множества всех продлений • Объединять результат • Идея свести двойное продление к одинарному • Вводим очередь путей на продление!
Поиск в ширину • [a] — очередь из одной исходной вершины • [a,b] • [a,b,c], [a,b,d] • [a,b,d], [a,b,c,d] • [a,b,c,d], [a,b,d,e], [a,b,d,f] • [a,b,d,e], [a,b,d,f], [a,b,c,d,e], [a,b,c,d,f] • [a,b,d,f], [a,b,c,d,e], [a,b,c,d,f] — тупиковый путь [a,b,d,e] убирается из очереди • [a,b,c,d,e], [a,b,c,d,f], [a,b,d,f,g] • [a,b,c,d,f], [a,b,d,f,g] — тупиковый путь [a,b,c,d,e] убирается из очереди • [a,b,d,f,g], [a,b,c,d,f,g] • [a,b,c,d,f,g] — получено первое решение [a,b,d,f,g], убирается из очереди • [] — получено второе решение [a,b,c,d,f,g], убирается из очереди
Как построить список всех продлений пути P? • Если prolong завершается неуспехом (тупик), то findall также завершается неуспехом • Хотя логичнее было бы возвращять [] ?- finall(X, prolong(P,X), L).
Поиск в ширину • Первым находит кратчайший путь • Алгоритм выглядит менее естественно, требует явного хранения путей в очереди и рассмотрения множественных вариантов • Низкая степень декларативности • Позволяет искать в путях с циклами path(X,Y,P):- bdth([[X]],Y,P). bdth([[X|T]|_],X,[X|T]). bdth([P|QI],X,R) :- findall(Z,prolong(P,Z),T), append(QI,T,QO),!, bdth(QO,X,R). bdth([_|T],Y,L) :- bdth(T,Y,L).
Найдите отличие! path(X,Y,P):- bdth([[X]],Y,P). bdth([[X|T]|_],X,[X|T]). bdth([P|QI],X,R) :- findall(Z,prolong(P,Z),T), append(QI,T,QO),!, bdth(QO,X,R). bdth([_|T],Y,L) :- bdth(T,Y,L). path(X,Y,P):- bdth([[X]],Y,P). bdth([[X|T]|_],X,[X|T]). bdth([P|QI],X,R) :- findall(Z,prolong(P,Z),T), append(Т,QI,QO),!, bdth(QO,X,R). bdth([_|T],Y,L) :- bdth(T,Y,L).
Можно ли достичь идеала? • Нахождение кратчайших путей первыми? • Требования по памяти – O(lmax) • Поиск в графах с циклами
Поиск с ограничением длины • Чтобы избежать зацикливания поиска в ширину, ограничим глубину погружения параметром DepthLimit • Если постепенно увеличивать этот параметр на 1, то получим поиск, в котором находится первым кратчайший путь search_id(Start,Finish,Path,DepthLimit) :- depth_id([Start],Finish,Path,DepthLimit). depth_id([Finish|T],Finish,[Finish|T],_). depth_id(Path,Finish,R,N) :- N>0, prolong(Path,NewPath), N1 is N-1, depth_id(NewPath,Finish,R,N1).
Поиск с итерационным заглублением • Сложность: • Для пути длины l поиск происходит l+1раз • Первая вершина обходится (l+1) раз, второй уровень – l раз и т.д. • (l+1)*1 + l*b + (l-1)*b2 + … + 1*bl = O(bl) • Поиск в глубину по сложности не сильно превосходит поиск в ширину, сохраняя все его положительные стороны и исключая требования к памями. search_id(Start,Finish,Path):- integer(Level), search_id(Start,Finish,Path,Level). integer(1). integer(M) :- integer(N), M is N+1.
Библиотека поиска на Mercury :- module search. :- interface. :- import_module std_util, list. • Основные типы данных и сопутствующие элементы: :- type path(N) == list(N). :- type graph(N) == pred(N,N). :- inst graph == (pred(in,out) is nondet). :- pred search_dpth(graph(N),N,N,path(N)). :- mode search_dpth(in(graph), in,in,out) is nondet. :- pred search_bdth(graph(N),N,N,path(N)). :- mode search_bdth(in(graph), in,in,out) is nondet.
Реализация поиска на Mercury :- implementation. :- import_module queue. :- pred prolong(graph(N), path(N), path(N)). :- mode prolong(in(graph), in, out) is nondet. prolong(G,[X|T],[Y,X|T]) :- G(X,Y), not(member(Y,[X|T])). search_dpth(G,A,B,R) :- dpth(G,[A],B,R).:- pred dpth(graph(N),path(N),N,path(N)). dpth(_,[X|T],X,[X|T]). dpth(G,P,F,L) :- prolong(G,P,P1), dpth(G,P1,F,L). search_bdth(G,A,B,R) :- bdth(G,put(init,[A]),B,R).:- pred bdth(graph(N), queue(path(N)), N, path(N)). bdth(_,Q,X,[X|T]) :- first(Q,[X|T]). bdth(G,Q,Y,R) :- get(Q,P,T), solutions(lambda([Z::out] is nondet,prolong(G,P,Z)),L), bdth(G,put_list(T,L),Y,R).
Решение задач методом поиска в пространстве состояний B C E D A s(<положение обезьяны>,<полож.ящика>,<внизу/вверху>,<есть ли банан>) s(a,c,down,no)
Обезьяна и банан • Проследите за перемещением обезьяны! • Реальные перемещение не будут совпадать с последовательностью вызовов move, поскольку будут откаты • В ширину vs. в глубину vs.погружение – ответ сразу не очевиден! move(s(A,B,down,YN),s(C,B,down,YN)) :- vertex(C). move(s(A,A,down,YN),s(B,B,down,YN)) :- vertex(B). move(s(A,A,UD,YN),s(A,A,DU,YN)) :- permute([DU,UD],[down,up]). move(s(e,e,up,no),s(e,e,up,yes)).