Определить граф в Prolog: edge и path, найти, существует ли путь между двумя вершинами

Я очень новичок в Prolog. Я определил в graph.pl следующий график:

graph

И вот мой код Пролога:

edge(a,e).
edge(e,d).
edge(d,c).
edge(c,b).
edge(b,a).
edge(d,a).
edge(e,c).
edge(f,b).
path(X,X).
path(X,Y):- edge(X,Z) ; path(Z,Y).

Я понимаю это так: существует путь между вершиной X и вершиной Y, только если существует ребро между вершиной X и вершиной Z И есть путь между вершинами Z и vertex Y (некоторая рекурсия).

Правильно ли это для представленного графика? Когда я спрашиваю Пролог о пути между вершиной A и вершиной F, он дает мне true... который даже не прав! Что может быть неправильным в этом коде?

Ответ 1

Циклы на графике. Вам нужно отслеживать, какие узлы вы посещаете, и проверить их. Попробуйте это, используя хелперный предикат с аккумулятором для отслеживания посещенных узлов:

path(A,B) :-   % two nodes are connected, if
  walk(A,B,[]) % - if we can walk from one to the other,
  .            % first seeding the visited list with the empty list

walk(A,B,V) :-       % we can walk from A to B...
  edge(A,X) ,        % - if A is connected to X, and
  not(member(X,V)) , % - we haven't yet visited X, and
  (                  % - either
    B = X            %   - X is the desired destination
  ;                  %   OR
    walk(X,B,[A|V])  %   - we can get to it from X
  )                  %
  .                  % Easy!

edge(a,e).
edge(e,d).
edge(d,c).
edge(c,b).
edge(b,a).
edge(d,a).
edge(e,c).
edge(f,b).

Ответ 2

Формат, который вы используете (edge ​​/2), имеет смысл для изучения Prolog, и вы должны следовать советам mbratch о учебнике.

На самом деле, есть хорошие альтернативы, которые уже доступны, в некоторых случаях с полезными предикатами, готовыми к работе: например, в библиотеке (ugraph), reachable/3. Теперь, с вашими данными, этот предикат path/2

path(X,Y) :-
    findall(A-B, edge(A,B), Es),
    vertices_edges_to_ugraph([],Es,G),
    reachable(X,G,Path),
    member(Y,Path).

делает

?- path(a,X).
X = a ;
X = b ;
X = c ;
X = d ;
X = e.

Посмотрим, что это значит:

findall(A-B, edge(A,B), Es)

поместите в Es все ребра с обозначением, требуемым библиотекой,

vertices_edges_to_ugraph([],Es,G)

строит в G соответствующий граф

reachable(X,G,Path)

составить список Путь всех вершин, достижимых (duh) из X

member(Y,Path)

посмотрим, присутствует ли Y в пути.

Поскольку я запросил у Y бесплатно, я получаю все достижимые вершины из X, что я привязан к a.

Ответ 3

Если вам интересно узнать, существует ли путь к файлу — но необязательно в фактическом пути — вычислять транзитное закрытие бинарного отношения edge/2.

К счастью для вас, общая идиома в !

Чтобы выразить нерефлексивное транзитивное замыкание edge/2, используйте closure/3 — определено в более раннем вопросе Определение рефлексивного транзитивного закрытия ":

?- closure(edge, X, Y).
   X = a, Y = e
;  X = a, Y = d
;  X = a, Y = c
;  ...

Обратите внимание, что closure/3 имеет очень хорошие свойства завершения.

Если все кластеры edge/2 являются основными фактами, closure(edge, _, _) завершается повсеместно! Посмотрите:

?- closure(edge, _, _), false.
false.

Ответ 4

Он проверяет цикл! Я выполнил пример с помощью команды trace. перед вами и увидел, что программа вернется к проблеме, чтобы найти путь от a до f после циклического перемещения. path (a, f) требует, чтобы путь (e, f) был истинным, следуя короткой нотации, мы должны быть правдой:

(d, f), (c, f), (b, f), затем (a, f) снова.

Чтобы решить цикл, вам нужен механизм для предотвращения цикла. Моя первая идея состояла бы в том, чтобы сохранить список node -names, а также проверить, если список не содержит текущий X при проверке пути.