【问题标题】:Counting number of paths between two nodes in Prolog program在Prolog程序中计算两个节点之间的路径数
【发布时间】:2017-10-30 18:43:28
【问题描述】:

我需要一些帮助来计算可以到达目标节点的组合数量。

我找到了寻找不同路径的程序。但最后我需要一些查询

%Edge List (Knowledge Base)

edge(1,2).
edge(1,4).
edge(2,4).
edge(3,6).
edge(3,7).
edge(4,3).
edge(4,5).
edge(5,6).
edge(5,7).
edge(6,5).
edge(7,5).
edge(8,6).
edge(8,7).

%Program

path(X,Y,[X,Y]):- edge(X,Y).
path(X,Y,[X|Xs]):- edge(X,W), path(W,Y,Xs).

-------------------------------------------------

%Query
path(1, 7, P).

%Results
Z = [1, 2, 4, 3, 6, 5, 7];
Z = [1, 2, 4, 3, 6, 5, 6, 5, 7];
.........................

但是如果我想运行一个查询来提供这些路径的数量。

?-path(1, 7, count). 

should return 2

【问题讨论】:

  • 嗯,你试过什么?
  • 我尝试计算递归的步骤。但结果并不如预期 move2(X,Y,N):- N 是 N+1, edge(X,Y)。 move2(X,Y,N):- N 为 N+1, edge(X,Z), move2(Z,Y,N)。
  • N is N+1?这没有任何意义。
  • 但在 Prolog 中,您不能将变量接地两次(使用不同的值)。在这种情况下,您需要一个新变量。例如,您可以写N1 is N+1
  • 您需要编辑您的问题并添加您尝试过的代码,格式正确。注释不是您放置代码的地方。但这就是说,从您的评论来看,您似乎正在尝试计算节点遍历,而不是总路径数。简单的答案是findall(P, path(1,7,P), Paths), length(Paths, N)N 将是您的答案。

标签: algorithm prolog graph-theory graph-traversal


【解决方案1】:

首先,您的答案会陷入循环并且不会终止,您可以保留您访问过的内容的列表,以避免两次访问相同的节点:

path(X,Y,L):-path(X,Y,L,[X]).

path(X,Y,[X,Y],L):- \+member(Y,L),edge(X,Y).
path(X,Y,[X|Xs],L):- edge(X,W),\+ member(W,L) ,path(W,Y,Xs,[W|L]).

现在如果你查询:

?- path(1, 7, P).
P = [1, 2, 4, 3, 7] ;
P = [1, 2, 4, 3, 6, 5, 7] ;
P = [1, 2, 4, 5, 7] ;
P = [1, 4, 3, 7] ;
P = [1, 4, 3, 6, 5, 7] ;
P = [1, 4, 5, 7] ;
false.

所以有效路径不是2,因为以上6个路径都是有效的。

现在来计算你可以尝试的路径:

findall(P, path(1,7,P), Paths), length(Paths, N).

按照 cmets 中的建议,但这不是很有效,因为您需要首先构建所有路径的列表并计算长度。

如果您使用 Swipl,您可以尝试使用失败驱动循环来计算所有可能的路径,并使用 nb_getval/2nb_setval/2 进行计数:

count(X,Y):-
            nb_setval(counter, 0),
            path(X,Y,_),
            nb_getval(counter, Value),
            New_value is Value+1,
            nb_setval(counter, New_value),
            fail;
            nb_getval(counter, Value),
            write(Value).

例子:

?- count(1,7).
6
true.

【讨论】:

  • 生成路径并通过列表对其进行计数比使用nb_setval/2 &co. 生成路径并对其进行计数的效率如何?对我来说,这似乎是相同数量的工作,只是一个是普通的 Prolog,另一个不是。
  • @DanielLyons,但是使用findall,您将计算相同的东西,但您必须将它们保存在一个列表中,因此这不适用于大输入(由于堆栈溢出)以及您最后需要另一个 O(n) 来计算长度......另一方面,在计算解决方案时计算失败驱动循环。在这两种情况下,计算解决方案都是不可避免的。
  • 我自己没有在普通 Prolog 中看到堆栈溢出的大量输入......原则上它可能会发生,但我对你答案的后半部分有一种“恶心”的反应,因为它是很单调。我认为nb_setval/2 是不得已而为之的工具。但是,是的,对于大量输入,这可能是必要的。
  • @DanielLyons ,考虑有一个关于 10^8 路径和每个路径 10^6 个节点的列表(由于 findall),这是很多数据,这只是一个可能的例子......但我同意在“编写普通 Prolog”与避免可能的溢出之间进行权衡......
  • 非常感谢。这非常清楚地解释了我的疑问! :)
猜你喜欢
  • 2012-05-21
  • 1970-01-01
  • 2019-04-20
  • 2023-04-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-18
  • 1970-01-01
相关资源
最近更新 更多