Path 在Prolog中查找图形中所有可能的不带循环的路径

Path 在Prolog中查找图形中所有可能的不带循环的路径,path,prolog,find,graph-theory,Path,Prolog,Find,Graph Theory,我有一个逻辑课的家庭作业,但或多或少都不知道如何解决它。。。 用这样的疑问 ?- find(a,[r(a,[b,d]),r(b,[a,c,e]),r(c,[b]),r(d,[a,e]), r(e,[b,d,f]),r(f,[e,g]),r(g,[f])],Path). Prolog应该返回给定图形中所有可能的路径。术语r(X,List)定义了图形,这意味着可以从节点X访问列表中的节点。在这种情况下,输出为: Path = [a,b,c] ; Path = [a,b,e,d] ; Pat

我有一个逻辑课的家庭作业,但或多或少都不知道如何解决它。。。 用这样的疑问

  ?- find(a,[r(a,[b,d]),r(b,[a,c,e]),r(c,[b]),r(d,[a,e]),
  r(e,[b,d,f]),r(f,[e,g]),r(g,[f])],Path).
Prolog应该返回给定图形中所有可能的路径。术语r(X,List)定义了图形,这意味着可以从节点X访问列表中的节点。在这种情况下,输出为:

Path = [a,b,c] ;
Path = [a,b,e,d] ;
Path = [a,b,e,f,g] ;
Path = [a,d,e,b,c] ;
Path = [a,d,e,f,g] ;
false.
虽然我在SE和web上掌握了许多解决类似问题的方法,但不知何故,我太笨了,不知道如何在本作业中使用图形的定义

我认为find(Start,…)应该与r(Start,list)中列表的所有成员一起递归调用,但是作为Prolog的新手(我们刚刚做了标准的家谱…),我不知道怎么做

任何帮助都将不胜感激。我知道我没有太多的开始,但我已经花了半个晚上试图弄明白一些事情,到目前为止我一点线索也没有

/编辑:

首先,我想我需要一些基本情况来中止递归。 我认为两者都应该

find([],_,_).
因为我猜最后一个递归调用没有任何开始,或者

find(_,[],_).
假设定义相邻节点的术语列表在程序完成处理时应为空

现在是实际通话。大概是

find(Start,[r(Start,[Adjacent|Restadj])|Rest],Path):-
           find(???).
我的问题如下:

-如何使程序使用r(…)术语中的列表成员作为下一个开始

-如何检查节点是否已被“访问”/如何从r中的特定列表中删除节点

-如何将找到的节点放入路径列表中?简单地附加?或者使用类似于[Path | Start]的方法执行递归调用

如你所见,这并不多。一些有启发性的问题会很好,因为序言看起来很有趣,所以学习起来很有趣


在花了一些时间使用整洁的PDT Eclipse跟踪工具之后,我想我理解了该程序正在做什么。我现在不明白的是为什么最后一个节点总是丢失。回溯失败后,例如,因为r(c,[b])是下一个找到的项,而memberchk(b,[b])由于否定而失败(这就是I thing+所做的),并且找不到其他与r(c,X)相关的项,所以它开始寻找从节点b出发的其他可能性,该节点b在r(b,[…])中留下了相邻的节点。但是为什么程序忘记将节点c放入路径列表中呢?有没有可能在这种情况下做一些if-then-else

 member(r(Node, Adjacent), Graph),
member(AdjNode, Adjacent),
\+ memberchk(AdjNode, Seen),

失败,是否仍将最后一个节点附加到路径?

我怀疑让您感到困惑的是,您必须从显式数据结构中查找数据,而不是从数据库中获取数据。第一条裂缝可能如下所示:

find(_, _, []).
find(Node, Graph, [Node|Path]) :-
    member(r(Node,Adjacent), Graph),
    member(AdjNode, Adjacent),
    find(AdjNode, Graph, Path).
查看我如何使用
member/2
从图中查找数据。但是这个解决方案是不正确的,因为它是循环的。一个改进可能是:

find(Node, Graph, Path) :- find(Node, Graph, Path, []).

find(_, _, [], _).
find(Node, Graph, [Node|Path], Seen) :-
    member(r(Node, Adjacent), Graph),
    member(AdjNode, Adjacent),
    \+ memberchk(AdjNode, Seen),
    find(AdjNode, Graph, Path, [Node|Seen]).
这个版本基本上与上面的版本相同,只是它有一个“已看到”列表来跟踪它已经到达的位置。这仍然不能产生您想要的输出,但我认为这足以让您走上正确的轨道

编辑响应您的编辑

首先,我想我需要一些基本情况来中止递归

对。我选择第一种情况是因为我认为在遍历过程中不能安全地“使用”图形。我想您可以使用
select/3
代替
member/2
并在不使用此节点的情况下向前传递图形。这可能是一个有趣的尝试(建议!)

  • 如何使程序使用r(…)术语中的列表成员作为下一个开始
如图所示,使用
member/2
从图形中检索内容。这很有趣,因为您使用了与所需谓词完全相同的单词。:)

  • 如何检查节点是否已被“访问”/如何从r中的特定列表中删除节点
正如我在第二组代码中所演示的,您有另一个参数作为辅助谓词,并使用
memberchk/3
member/3

  • 如何将找到的节点放入路径列表中?简单地附加?或者使用类似于[Path | Start]的方法执行递归调用
我使用递归调用<代码>附加/3会更贵

编辑:根据威尔的评论使用
findall/3
,我们可以一次找到所有路径:

all_paths(From, Graph, Paths) :- findall(Path, find(From, Graph, Path), Paths).
您可以这样调用它:

?- all_paths(a, [r(a,[b,d]),r(b,[a,c,e]),r(c,[b]),r(d,[a,e]),
                 r(e,[b,d,f]),r(f,[e,g]),r(g,[f])], AllPaths).

我还没有测试过,但它应该可以工作。

基于Daniel Lyons的清晰代码,广度优先搜索:

所有路径(节点、图形、路径):-
bfs(图,[[Node]-[]],R,[]),%或dfs(…)
映射列表(fst、路径、R)。
fst(A,A-)效用
对(B,A,A-B)。%助手
加(LS,H[H|LS])。%
bfs(_G,[],Z,Z)。%队列为空
bfs(图[H | Q],[H | R],Z):-
H=看到的路径,路径=[Node | U3;],
findall(Next,member(r(Node,Next),Graph),NS,
展平_diff(NS,Seen,WS),%节点工作集
映射列表(添加(路径)、WS、PS),%新路径
映射列表(对([Node | Seen])、PS、QH、%新添加到队列
%%追加(QH,Q,Q2),%DFS
附加(Q、QH、Q2),%BFS
bfs(图,Q2,R,Z)。
(未经测试)<代码>展平差异(A、B、C)应展平列表
A
,同时删除列表
B
中出现的元素,从而生成列表
C


正如PeterPanter所注意到的,Daniel Lyons的代码需要稍微调整,以不排除其结果路径中的最后一个节点

find(节点,图形,[Node | Path]):-find(节点,图形,路径,[])。
查找(u,u,[],u)。
查找(节点,图形,[AdjNode |路径],已看到):-
成员(r(节点,相邻),图),
M