Prolog 图中的圈检测
我们得到了一个包含以下事实的图表:Prolog 图中的圈检测,prolog,directed-graph,Prolog,Directed Graph,我们得到了一个包含以下事实的图表: 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了,但这种方法可能会起作用:路径是一系列边,其中每条边从上一条边结束的节点开始(例
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了,但这种方法可能会起作用:路径是一系列边,其中每条边从上一条边结束的节点开始(例如A->b,b->c,c->d)。普通路径是一条边,通过获取现有路径并向其添加边,可以形成更复杂的路径。循环是在同一节点上开始和结束的路径。您可以使用这些定义来构建Prolog规则吗?我已经有一段时间没有使用Prolog了,但下面是我解决这个问题的方法 您可以创建规则
path(X,Y)
,检查是否存在从节点X
到Y
的路径。路径是单条边或通向路径的边。有了这个,很容易找到从节点X
开始的循环——它将是路径(X,X)。以下是我的实现(摘自我的头顶,不一定正确,但给出了想法):
阿尔奇的想法是一个很好的起点,但如果它在搜索路径时发现另一个循环,它将创建一个无限循环
我也已经多年没有使用prolog了,但是您需要类似于
路径(X,Y,visted)
的东西,在这里您可以跟踪访问的节点,防止无休止的循环。我使用深度优先搜索访问的节点列表,如果我们在遍历过程中遇到任何访问的节点,它将返回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] ) .
虽然这似乎是一个类似于@Gökhan Uras的解决方案——但伟大的思想都是一样的!或者什么的(B)
基本逻辑是,如果当前节点已经被访问过,就有一个循环(在cycle/2
helper谓词中的第一个子句)。在这一点上,我们切割(!)并声明成功切割(!)的原因是,如果没有它,回溯将导致重新访问已经访问过的节点,从而产生无限的循环集
如果当前节点未被访问,我们将获取锚定在当前节点上的一条边并访问该边。回溯到
cycle/2
的第2个子句将访问下一条边,因此,一旦某个特定路径耗尽,cycle/2
将回溯并尝试另一条路径。如果您的Prolog系统具有前向链接器,您可以使用它但要注意,它可能会消耗大量内存,因为它将生成并保留路径/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).
为了让这个例子更有趣一点,我删除了边(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
此处的postate/1
谓词发布一个事件,并保持转发链接器的传播程序运行。如何编写转发规则取决于您使用的Prolog系统各自的库
附言:还有一些研究正在进行:获取edge(a,b)、edge(b,c)、edge(c,b)和查询?-cycle(X)。深度优先Prolog将运行到一个无限循环中,并且找不到该循环。
?- 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