edge(a,b)
edge(a,c)
edge(b,a)
edge(c,d)
edge(d,d)
edge(d,e)
edge(e,f)
edge(f,g)
edge(g,e)
我们被要求定义一个规则cycle(X)
,以确定从节点X
开始是否存在循环。
我真的不知道如何做到这一点,我尝试遍历节点并检查下一个节点是否再次成为起始节点,但似乎无法使其工作。
edge(a,b)
edge(a,c)
edge(b,a)
edge(c,d)
edge(d,d)
edge(d,e)
edge(e,f)
edge(f,g)
edge(g,e)
我们被要求定义一个规则cycle(X)
,以确定从节点X
开始是否存在循环。
我真的不知道如何做到这一点,我尝试遍历节点并检查下一个节点是否再次成为起始节点,但似乎无法使其工作。
阿奇的想法是一个很好的起点,但是如果在寻找路径时发现另一个循环,它将创建一个无限循环。
我也已经多年没有使用过Prolog了,但你需要类似于path(X,Y,Visited)
这样的东西,其中你要跟踪访问过的节点,以防止无尽的循环。
我使用深度优先搜索算法,同时记录已访问的节点列表。如果在遍历过程中遇到任何已访问过的节点,则返回true。我已经对小规模数据进行了测试,看起来运行正确。
cycle(X):- cycleh(X,[X]).
cycleh(X,Visited) :- edge(X,Y), (member(Y,Visited) -> !,true; cycleh(Y,[Y|Visited])).
cycle( X ) :-
cycle( X , [] ).
cycle( Curr , Visited ) :-
member( Curr, Visited ) ,
!.
cycle( Curr , Visited ) :-
edge( Curr , Next ) ,
cycle( Next , [Curr|Visited] ) .
cycle/2
辅助谓词中的第一个子句),则存在一个循环。此时,我们使用cut(!)截断并宣布成功。使用cut(!)的原因是,如果没有它,回溯将导致重新访问已经访问过的节点,从而形成无限的循环集。cycle/2
的第二个子句会访问下一条边,因此一旦特定路径耗尽,cycle/2
会回溯并尝试另一条路径。我已经有一段时间没有使用Prolog了,但是这是我解决这个问题的方法。
你可以创建规则path(X,Y)
,检查从节点X
到Y
是否存在路径。路径可以是单个边缘或通往路径的边缘。有了这个,很容易找到从节点X
开始的循环-- 它将简单地是path(X,X)
。这是我的实现(从我的记忆中取出来的,不一定正确,但是给出了思路):
path(X,Y) :- edge(X,Y).
path(X,Y) :- edge(X,Z), path(Z,Y).
cycle(X) :- path(X,X).
path/2
事实。\+
用于明确消除重复项::- forward edge/2.
:- forward path/2.
path(X,Y) :- edge(X,Y), \+ path(X,Y).
path(X,Y) :- edge(X,Z), path(Z,Y), \+ path(X,Y).
cycle(X) :- path(X,X).
edge(d,d)
。以下是一个样例运行:?- postulate(edge(a,b)), postulate(edge(a,c)), postulate(edge(b,a)),
postulate(edge(c,d)), postulate(edge(d,e)), postulate(edge(e,f)),
postulate(edge(f,g)), postulate(edge(g,e)), cycle(X).
X = a ;
X = b ;
X = e ;
X = f ;
X = g
postulate/1
谓词在此处发布事件并保持前向链接器的传播器运行。如何编写您的前向规则取决于您正在使用的Prolog系统各自的库。
P.S.:仍有一些研究正在进行中:
http://x10.sourceforge.net/documentation/papers/X10Workshop2011/elton_slides.pdf
我已经有一段时间没有使用Prolog了,但也许这种方法会起作用:一个路径是一系列边缘,每个边缘都从前一个边缘结束的节点开始(例如a -> b,b -> c,c -> d)。微不足道的路径是单个边缘,更复杂的路径可以通过采取现有路径并向其添加边缘来形成。循环是从同一节点开始并结束的路径。您能否使用这些定义构建您的Prolog规则?