这是问题。
定义谓词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)。还有其他方法可以存储新中间西格玛吗?
答案 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)
缓存了结果,使得重复查询更快,因为它只是重用了缓存的结果。