私はプロローグにとても慣れていません。 graph.pl
次のグラフ:
そしてこれが私の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
が得られます...これは正しくありません!このコードの何が問題になっているのでしょうか?
グラフの循環。あなたが訪れているノードを追跡し、それらをチェックする必要があります。ヘルパー述語とアキュムレータを使用して、訪れたノードを追跡します。
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).
使用する形式(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からすべての到達可能な頂点を取得します。
ifを知りたい場合は、パスが存在しますが、必ずしも実際のパスではないため、 推移的閉包 の二項関係_Edge/2
_。
幸いにも、 transitive-closure は prolog の一般的なイディオムです!
_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.
_
サイクルでチェック中です!私は事前にtrace.
コマンドを使用して例を実行しましたが、プログラムが問題に戻り、循環した後にaからfへのパスを見つけることがわかりました。 path(a、f)は、path(e、f)がtrueである必要があります。簡単な表記では、trueである必要があります。
(d、f)、(c、f)、(b、f)、そして(a、f)です。
ループを解決するには、ループを防止するメカニズムが必要です。最初のアイデアは、ノード名のリストを保持し、パスをチェックするときにリストに現在のXが含まれていないかどうかもチェックすることです。