在Prolog中将事实提取为矩阵



假设我们有以下内容:

edge(a, 1, 10).
edge(b, 2, 20).
edge(c, 3, 30).
edge(d, 4, 40).

我想提取这些事实的矩阵表示(M),这样

M = [[a,b,c,d],[1,2,3,4],[10,20,30,40]]

这里有一个无需思考的解决方案:

edgeMatrix(M) :-
findall(A, edge(A, _, _), As),
findall(B, edge(_, B, _), Bs),
findall(C, edge(_, _, C), Cs),
M = [As, Bs, Cs].

然而,这种方法存在一些问题,

  1. 我们遍历数据库n次,其中n是参数的数量;以及
  2. 这并不能很好地推广到任意的n

所以问题是:在Prolog中实现这一点最常用的方法是什么?

关于:

edgeMatrix(M) :-
findall([A,B,C],edge(A,B,C),Trans),
transpose(Trans,M).

现在,您可以简单地从clpfd模块导入transpose/2矩阵,或者像这个答案中那样自己实现一个矩阵(是的,我知道这很懒,但重新发明轮子有什么意义?)。

如果我在swipl中运行这个,我得到:

?- edgeMatrix(M).
M = [[a, b, c, d], [1, 2, 3, 4], [10, 20, 30, 40]].

看起来和你想要的一模一样。

当然,你可以说计算transpose/2仍然有一些计算开销,但收集阶段只完成一次(如果这些不是简单的事实,而是子句的答案),这也可能很昂贵,而且我认为模块无论如何都可能非常有效地实现子句。

我认为您不会找到一个既通用又高效的解决方案。以下是N=3:的简单解决方案

edges(Edges) :-
Goal = edge(_A, _B, _C),
findall(Goal, Goal, Edges).
edges_abcs_([], [], [], []).
edges_abcs_([edge(A,B,C)|Edges], [A|As], [B|Bs], [C|Cs]) :-
edges_abcs_(Edges, As, Bs, Cs).
edges_abcs([As, Bs, Cs]) :-
edges(Edges),
edges_abcs_(Edges, As, Bs, Cs).

在添加100000个额外的edge/3事实后,执行如下操作:

?- time(edges_abcs(M)).
% 200,021 inferences, 0.063 CPU in 0.065 seconds (97% CPU, 3176913 Lips)
M = [[a, b, c, d, 1, 2, 3, 4|...], [1, 2, 3, 4, 1, 2, 3|...], [10, 20, 30, 40, 1, 2|...]].

为了进行比较,以下是从以下问题进行实施的度量:

?- time(edgeMatrix_orig(M)).
% 300,043 inferences, 0.061 CPU in 0.061 seconds (100% CPU, 4896149 Lips)
M = [[a, b, c, d, 1, 2, 3, 4|...], [1, 2, 3, 4, 1, 2, 3|...], [10, 20, 30, 40, 1, 2|...]].

以下是Willem基于transpose/2的更通用的解决方案:

?- time(edgeMatrix_transpose(M)).
% 700,051 inferences, 0.098 CPU in 0.098 seconds (100% CPU, 7142196 Lips)
M = [[a, b, c, d, 1, 2, 3, 4|...], [1, 2, 3, 4, 1, 2, 3|...], [10, 20, 30, 40, 1, 2|...]].

因此,就推论的数量而言,我的解决方案似乎是最好的:findall/3的100000个推论和遍历列表的100000个推断。该问题的解决方案对每个findall/3有100000个推论,但仅此而已。然而,它稍微快一点,因为它的内存效率更高:分配的所有内容最终都在最终解决方案中,而我的程序分配了一个100000个edge/3项的列表,然后必须对这些项进行垃圾收集。(在SWI-Prolog中,如果打开探查器和/或GC跟踪,您可以看到垃圾收集。)

如果我真的需要它尽可能快可以推广到N的许多不同值,我会写一个宏,扩展到类似于问题中的解决方案。

编辑:如果取消了"惯用"要求,我会将edge数据库作为列表存储在SWI-Prolog全局变量中。在这种情况下,我的单程实现将在没有findall/3开销和不产生中间垃圾的情况下工作。

相关内容

  • 没有找到相关文章

最新更新