List 打印图形的指定节点Prolog的所有图形周期

List 打印图形的指定节点Prolog的所有图形周期,list,prolog,graph-theory,cycle,transitive-closure,List,Prolog,Graph Theory,Cycle,Transitive Closure,我是序言世界的新手。我正在尝试编写谓词,以便为图形的指定节点(即给定节点的元素)打印图形的所有周期。我的图表是这样的 edge(a,e). edge(e,d). edge(d,c). edge(c,b). edge(b,a). edge(d,a). edge(e,c). edge(f,b). cycle(X) :- cycle(X, []). cycle(Curr, Visited) :- member(Curr, Visited), !. cycle(Curr, V

我是序言世界的新手。我正在尝试编写谓词,以便为图形的指定节点(即给定节点的元素)打印图形的所有周期。我的图表是这样的

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

cycle(X) :-
    cycle(X, []).

cycle(Curr, Visited) :-
    member(Curr, Visited),
    !.
cycle(Curr, Visited) :-
    edge(Curr, Next),
    cycle(Next, [Curr|Visited]).

不幸的是,现在我找不到特定节点的循环。例如,我正在查找node
d

的所有周期,我认为问题在于您根本没有编写任何逻辑来返回周期

源于节点的循环 这并不难,您已经使用了一个累加器,您只需要一个机制,一旦找到循环,就将累加器作为循环返回。您可以通过向循环提供一个附加参数来完成此操作:

cycle(Node,Cycle) :-
    cycle(Node,[],Cycle).

cycle(Curr,Visited,Cycle) :-
    member(Curr,Visited),
    !,
    reverse([Curr|Visited],Cycle).
cycle(Curr,Visited,Cycle) :-
    edge(Curr,Next),
    cycle(Next,[Curr|Visited],Cycle).
在这个实现中,我使用了
reverse/2
,因为这将为您提供正确的节点顺序(沿边)。如果您对此不感兴趣(例如,您只希望分析周期,您可以以相反的方式进行分析,您可以简单地将
cycle/3
的第一个子句中的
cycle
替换为
[Curr | visted]

但问题是,如果调用此谓词,它将返回:

?- cycle(d,Cycle).
Cycle = [d, c, b, a, e, d] ;
Cycle = [d, c, b, a, e, c] ;
Cycle = [d, a, e, d] ;
Cycle = [d, a, e, c, b, a].
因此,它不会搜索周期,而
d
本身就是周期的一部分,它会搜索所有可以源自
d
的周期。这可能不是您想要的

具有给定节点的循环 但是,您可以重写谓词:您需要存储您发起的节点:

cycle(Node,Cycle) :-
    edge(Node,Next),
    cycle(Node,Next,[Node],Cycle).
因此,我们在这里查找源自
节点
的每个
边缘/2
,并调用
循环/4
,使用初始
节点
(在找到循环时进行检查),
下一个
节点,访问的
[Node]
列表和
循环
参数返回找到的循环

现在,
循环/4有三个可能的选项:

  • 我们到达原始节点,在这种情况下,我们找到了一个循环,
    节点
    是其中的一部分,我们进行的处理与第一个版本类似:

    cycle(Curr,Curr,Visited,Cycle) :-
        !,
        reverse([Curr|Visited],Cycle).
    
  • 我们到达一个已经访问过的节点:
    Curr
    已访问
    的一个元素,在这种情况下,我们需要中断搜索:否则我们将循环无限次:

    cycle(_,Curr,Visited,_) :-
        member(Curr,Visited),
        !,
        fail.
    
    fail
    是一个声明分支失败的谓词。这可能很有用,因为现在我们指定它如何失败

  • 最后,我们只需访问另一个边缘并尝试查找下一个节点:

    cycle(Node,Curr,Visited,Cycle) :-
        edge(Curr,Next),
        cycle(Node,Next,[Curr|Visited],Cycle).
    
全文如下:

cycle(Node,Cycle) :-
    edge(Node,Next),
    cycle(Node,Next,[Node],Cycle).

cycle(Curr,Curr,Visited,Cycle) :-
    !,
    reverse([Curr|Visited],Cycle).
cycle(_,Curr,Visited,_) :-
    member(Curr,Visited),
    !,
    fail.
cycle(Node,Curr,Visited,Cycle) :-
    edge(Curr,Next),
    cycle(Node,Next,[Curr|Visited],Cycle).
由此产生:

?- cycle(d,Cycle).
Cycle = [d, c, b, a, e, d] ;
Cycle = [d, a, e, d] ;
因此,所有这些都是从
d
开始的循环,然后再次访问
d
。我们可以通过将第二个和第三个场景压缩到一个子句中来提高代码的效率:

cycle(Node,Cycle) :-
    edge(Node,Next),
    cycle(Node,Next,[Node],Cycle).

cycle(Curr,Curr,Visited,Cycle) :-
    !,
    reverse([Curr|Visited],Cycle).
cycle(Node,Curr,Visited,Cycle) :-
    \+ member(Curr,Visited),
    edge(Curr,Next),
    cycle(Node,Next,[Curr|Visited],Cycle).

其中
\+
的意思不是。

我觉得你在寻找什么……不需要重新发明轮子

只需基于“”中所示的经过测试的代码进行构建,并定义:

in_cycle(R_2, AZ, Path) :- % cf. "simple cycle" First = AZ, Last = AZ, path(R_2, Path, First, ButLast), % all "hops" but the last one ... call(R_2, ButLast, Last). % ... and here comes the last one
查询#1的所有解决方案也满足了更一般的查询#2单调序言,在工作中:)

谢谢威廉,我真的很感激你的回答,它对我帮助很大。现在我完全明白了。谢谢 | ?- in_cycle(edge, d, Path). Path = [d,c,b,a,e] ? ; Path = [d,a,e] ? ; no | ?- in_cycle(symm(edge), d, Path). Path = [d,c] ? ; Path = [d,c,b,a] ? ; Path = [d,c,b,a,e] ? ; Path = [d,c,e] ? ; Path = [d,c,e,a] ? ; Path = [d,a] ? ; Path = [d,a,e] ? ; Path = [d,a,e,c] ? ; Path = [d,a,b,c] ? ; Path = [d,a,b,c,e] ? ; Path = [d,e] ? ; Path = [d,e,c] ? ; Path = [d,e,c,b,a] ? ; Path = [d,e,a] ? ; Path = [d,e,a,b,c] ? ; no