web-dev-qa-db-ja.com

Prologでグラフを定義:エッジとパス、2つの頂点間にパスがあるかどうかを見つける

私はプロローグにとても慣れていません。 graph.pl次のグラフ:

graph

そしてこれが私のPrologコードです:

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と頂点Yの間にパスがあります(ある種の再帰)。

提示されたグラフは正しいですか?頂点Aと頂点Fの間のパスについてPrologに尋ねると、trueが得られます...これは正しくありません!このコードの何が問題になっているのでしょうか?

13
yak

グラフの循環。あなたが訪れているノードを追跡し、それらをチェックする必要があります。ヘルパー述語とアキュムレータを使用して、訪れたノードを追跡します。

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).
15
Nicholas Carey

使用する形式(Edge/2)は、Prologについて学ぶのに意味があり、チュートリアルに関するmbratchのアドバイスに従う必要があります。

実際には、すでに利用可能な優れた代替案があり、場合によっては、すぐに使える述語が用意されています。たとえば、library( graph )には 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)

xから到達可能な(duh)すべての頂点のリストパスを作成する

member(Y,Path)

yがパスに存在するかどうかを確認します。

私はYfreeで照会したので、aにバインドしたXからすべての到達可能な頂点を取得します。

5
CapelliC

ifを知りたい場合は、パスが存在しますが、必ずしも実際のパスではないため、 推移的閉包 の二項関係_Edge/2_。

幸いにも、 transitive-closureprolog の一般的なイディオムです!

_Edge/2_の再帰的ではない推移閉包を表現するには、 meta-predicate_closure/3_ を使用します-以前の質問で定義された " Definition of Reflexive推移的閉包 ":

?-閉鎖(エッジ、X、Y)。
 X = a、Y = e 
; X = a、Y = d 
; X = a、Y = c 
; ... 

_closure/3_には非常に優れた終了プロパティがあることに注意してください。

_Edge/2_のすべての句が事実である場合、closure(Edge, _, _)は普遍的に終了します!見て:

_?- closure(Edge, _, _), false.
false.
_
5
repeat

サイクルでチェック中です!私は事前にtrace.コマンドを使用して例を実行しましたが、プログラムが問題に戻り、循環した後にaからfへのパスを見つけることがわかりました。 path(a、f)は、path(e、f)がtrueである必要があります。簡単な表記では、trueである必要があります。

(d、f)、(c、f)、(b、f)、そして(a、f)です。

ループを解決するには、ループを防止するメカニズムが必要です。最初のアイデアは、ノード名のリストを保持し、パスをチェックするときにリストに現在のXが含まれていないかどうかもチェックすることです。

2
nxthor