Prolog中定义图:边和路径,查找两个顶点之间是不是有路径

Posted

技术标签:

【中文标题】Prolog中定义图:边和路径,查找两个顶点之间是不是有路径【英文标题】:Define graph in Prolog: edge and path, finding if there is a path between two verticesProlog中定义图:边和路径,查找两个顶点之间是否有路径 【发布时间】:2014-02-05 08:24:50 【问题描述】:

我对 Prolog 很陌生。我在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和顶点Z之间有一条边并且顶点@之间有一条路径时,顶点X和顶点Y之间才有一条路径987654329@ 和顶点Y(某种递归)。

对于所呈现的图表是否正确?当我向 Prolog 询问顶点 A 和顶点 F 之间的路径时,它给了我 true ......这甚至都不对!这段代码可能有什么问题?

【问题讨论】:

; 是 OR。 , 是 AND。所以你的path 子句不正确。 @mbratch:当我将; 更改为, 时,Prolog 闲逛...没有回答。对于我的图表,答案应该是错误/否。 ; 仍然不正确,必须是 ,。另一个问题是代码不处理电路中的路径问题,因此它可以在电路中循环,直到堆栈溢出,然后才能找到解决方案。您需要收集“您去过的地方”的列表,以确保您不会重复路径。 @mbratch:好的,谢谢,现在说得通了。但是,我的图表的适当规则应该是什么样子? 一种方法是让您的规则收集您走过的边缘列表,如果您已经去过那里,则不要选择它们。如果你用谷歌搜索“prolog graph”,你会在网上找到几个例子,这些例子都清楚地说明了这个问题,比如csupomona.edu/~jrfisher/www/prolog_tutorial/2_15.html 【参考方案1】:

您使用的格式 (edge/2) 对于学习 Prolog 很有意义,您应该遵循 mbratch 关于本教程的建议。

实际上,已经有很好的替代方案可用,在某些情况下可以使用有用的谓词:例如,在 library(ugraph) 中,有 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。

由于我使用 Y free 进行查询,我从 X 获得了所有可到达的顶点,我绑定到 a

【讨论】:

【参考方案2】:

如果您有兴趣了解是否路径存在——但不一定在实际路径中——计算二元关系edge/2transitive closure

幸运的是,transitive-closure 是 prolog 中的常用习语!

要表达edge/2 的非自反传递闭包,请使用meta-predicate closure/3——在前面的问题“Definition of Reflexive Transitive Closure”中定义:

?- 闭包(边缘,X,Y)。 X = a, Y = e ; X = a, Y = d ; X = a, Y = c ; ...

请注意,closure/3 具有非常好的终止属性。

如果edge/2 的所有子句都是基本事实,closure(edge, _, _) 将普遍终止!看:

?- closure(edge, _, _), false.
false.

【讨论】:

edge(X,s(X)). 普遍终止,但closure(edge,0,X) 描述了一个无限集,只能用无限多个答案来描述。因此不终止! 太棒了!而且也很可怕。将编辑澄清!谢谢!【参考方案3】:

图中的循环。您需要跟踪您正在访问的节点,并检查它们。试试这个,使用带有累加器的辅助谓词来跟踪访问的节点:

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).

【讨论】:

更简单的方法来完成它。也许 memberchk/2 比 member/2 更可取。 这个谓词有一些令人惊讶的实际应用。通过一些简单的更改,它可以用作English-to-Prolog translator。【参考方案4】:

它正在检查一个循环! 我事先使用trace. 命令执行了该示例,并看到程序在循环后返回问题以查找从a 到f 的路径。 path(a,f) 需要 path(e,f) 为真,简而言之,我们需要为真:

(d,f), (c,f), (b,f), 然后是 (a,f)。

要解决循环,您需要一种机制来防止循环。我的第一个想法是保留一个节点名称列表,并在检查路径时检查该列表是否不包含当前 X。

【讨论】:

以上是关于Prolog中定义图:边和路径,查找两个顶点之间是不是有路径的主要内容,如果未能解决你的问题,请参考以下文章

图查找排序

Python 学习 第十六篇:networkx

bzoj1050

三个最小生成树题

Django 在图中查找两个顶点之间的路径

bzoj1050 旅行comf(并查集)