在Prolog中声明中间结果

时间:2018-08-25 08:41:04

标签: prolog dynamic-programming

这是问题。

  

定义谓词sigma(N,S),使S = 1 + 2 + ... + N。并记住查询中的每个 new 中间结果。例如,查询sigma(3,S)后,它将把诸如sigma(2,3),sigma(3,6)之类的东西存储到数据库中,这样我们以后就不必重复和无用的工作了。

我尝试了以下方法来解决它。

sigmares(1,1).

mysigma(N,A,Sum) :-
    sigmares(N,SN),
    Sum is SN+A,
    !.

mysigma(N1,Acc,Sum) :-
    N is N1-1,
    A is Acc + N1,
    mysigma(N,A,Sum),
    assertz(sigmares(N1,Sum)). % <<<<<<<<<< This line doesn't work properly.

sigma(N,X) :-
    mysigma(N,0,X).

assertz行存在一些问题。由于sum只能被初始化一次,即从1到N的sum值,因此将插入查询sigma(3,S)的sigma(2,6),sigma(3,6)。还有其他方法可以存储中间西格玛吗?

2 个答案:

答案 0 :(得分:1)

首先,始终使用标准dynamic/1指令声明代码使用的动态谓词是一种很好的编码风格。只需在文件开头添加

:- dynamic(sigmares/2).

您对mysigma/3谓词的定义的一个有趣的方面是,它是非尾递归的,其结果是在其输入上需要线性空间。但这使它可以按预期缓存所有中间结果。您的代码的固定版本为:

:- dynamic(sigma_cache/2).

sigma_cache(1, 1).

sigma(N, S) :-
    sigma_cache(N, S),
    !.
sigma(N, S) :-
    N > 1,
    M is N - 1,
    sigma(M, SM),
    S is SM + N,
    assertz(sigma_cache(N, S)).

通话示例:

?- sigma(5, S).
S = 15.

?- listing(sigma_cache/2).
:- dynamic sigma_cache/2.

sigma_cache(1, 1).
sigma_cache(2, 3).
sigma_cache(3, 6).
sigma_cache(4, 10).
sigma_cache(5, 15).

true.

答案 1 :(得分:0)

这个替代答案提供了基于Prolog系统中的 tabling 机制的解决方案,包括B-Prolog,Ciao,SWI-Prolog,XSB和YAP:

:- table(sigma/2).

sigma(1, 1).
sigma(N, S) :-
    N > 1,
    M is N - 1,
    sigma(M, SM),
    S is SM + N.

让我们借助方便的SWI-Prolog time/1库谓词进行测试,该谓词报告了证明目标所需的时间和推理次数:

?- time(sigma(5, S)).
% 166 inferences, 0.000 CPU in 0.006 seconds (2% CPU, 1238806 Lips)
S = 15.

?- time(sigma(5, S)).
% 5 inferences, 0.000 CPU in 0.000 seconds (68% CPU, 208333 Lips)
S = 15.

请注意,我故意为sigma/2谓词使用了非尾递归定义,以便缓存所有中间结果(根据您的问题的要求)。例如:

?- time(sigma(4, S)).
% 5 inferences, 0.000 CPU in 0.000 seconds (70% CPU, 217391 Lips)
S = 10.

您可以看到,在第一次调用之后,结果通过制表机制进行缓存,当我们重复查询时,推断的数量要少得多。

?- time(sigma(6, S)).
% 32 inferences, 0.000 CPU in 0.000 seconds (86% CPU, 727273 Lips)
S = 21.

?- time(sigma(6, S)).
% 5 inferences, 0.000 CPU in 0.000 seconds (70% CPU, 217391 Lips)
S = 21.

再次注意推论次数。第一个查询为sigma(5, S)重用了缓存的结果,为sigma(6, S)缓存了结果,使得重复查询更快,因为它只是重用了缓存的结果。