在这个答案中,我们使用clpfd,就像之前的答案一样。
:- use_module(library(clpfd)).
为了便于进行正面比较(稍后),我们将这里展示的谓词称为n_fac/2
:
n_fac(N_expr,F_expr) :-
N #= N_expr, % eval arith expr
F #= F_expr, % eval arith expr
n_facAux(N,F).
就像在前面的答案中一样,n_fac/2
承认使用算术表达式。
n_facAux(0,1). % 0! = 1
n_facAux(1,1). % 1! = 1
n_facAux(2,2). % 2! = 2
n_facAux(N,F) :-
N #> 2,
F #> N, % redundant constraint
% to help `n_fac(N,N)` terminate
n0_n_fac0_fac(3,N,6,F). % general case starts with "3! = 6"
辅助谓词n_facAux/2
将任何“实际”工作委托给n0_n_fac0_fac/4
:
n0_n_fac0_fac(N ,N,F ,F).
n0_n_fac0_fac(N0,N,F0,F) :-
N0 #< N,
N1 #= N0+1, % count "up", not "down"
F1 #= F0*N1, % calc `1*2*...*N`, not `N*(N-1)*...*2*1`
F1 #=< F, % enforce redundant constraint
n0_n_fac0_fac(N1,N,F1,F).
让我们比较n_fac/2
一下n_factorial/2
!
?- n_factorial(47,F).
F = 258623241511168180642964355153611979969197632389120000000000
; false.
?- n_fac(47,F).
F = 258623241511168180642964355153611979969197632389120000000000
; false.
?- n_factorial(N,1).
N = 0
; N = 1
; false.
?- n_fac(N,1).
N = 0
; N = 1
; false.
?- member(F,[3,1_000_000]), ( n_factorial(N,F) ; n_fac(N,F) ).
false. % both predicates agree
好的!相同,到目前为止...为什么不做一点蛮力测试呢?
?- 时间((F1 #\= F2,n_factorial(N, F1 ),n_fac(N, F2 )))。
% 57,739,784 推理,6.415 CPU 在 7.112 秒内(90% CPU,9001245 唇)
% 执行中止
?-时间((F1 #\= F2,n_fac(N, F2 ),n_factorial(N, F1 )))。
% 52,815,182 推理,5.942 CPU 在 6.631 秒内(90% CPU,8888423 唇)
% 执行中止
?- 时间((N1 #> 1,N2 #> 1,N1 #\= N2,n_fac( N1 ,F),n_factorial( N2 ,F)))。
% 99,463,654 推理,15.767 CPU 在 16.575 秒内(95% CPU,6308401 唇)
% 执行中止
?- 时间((N1 #> 1,N2 #> 1,N1 #\= N2,n_factorial( N2 ,F),n_fac( N1 ,F)))。
% 187,621,733 推理,17.192 CPU 在 18.232 秒内(94% CPU,10913552 唇)
% 执行中止
前几百个值没有差异N in 2..sup
......好!
继续:以下如何(建议在对此答案的评论中)?
?- n_factorial(N,N), false.
false.
?- n_fac(N,N), false.
false.
过得不错!相同的终止行为... 更多?
?- N #< 5, n_factorial(N,_), false.
false.
?- N #< 5, n_fac(N,_), false.
false.
?- F in 10..100, n_factorial(_,F), false.
false.
?- F in 10..100, n_fac(_,F), false.
false.
好吧!仍然相同的终止属性!让我们再深入一点!下面的呢?
?- inf..10 中的 F,n_factorial(_,F),错误。
... % Execution Aborted %不会普遍终止
?- inf..10 中的 F,n_fac(_,F),错误。
错误的。% 普遍终止
哦!第一个查询不会终止,第二个会终止。什么加速!:)
让我们做一些经验性的运行时测量!
?- member(Exp,[6,7,8,9]), F #= 10^Exp, time( n_factorial (N,F)) ; 真的。
% 328,700 次推理,0.043 秒内 0.043 CPU(100% CPU,7660054 唇)
% 1,027,296 次推理,0.153 秒内 0.153 CPU(100% CPU,6735634 唇)
% 5,759,864 推理,1.967 CPU 在 1.967 秒内(100% CPU,2927658 唇)
% 22,795,694 推理,23.911 CPU 在 23.908 秒内(100% CPU,953351 唇)
真的。
?- member(Exp,[6,7,8,9]), F #= 10^Exp, time( n_fac (N,F)) ; 真的。
% 1,340 次推理,0.000 CPU 在 0.000 秒内(99% CPU,3793262 唇)
% 1,479 次推理,0.000 CPU 在 0.000 秒内(100% CPU,6253673 唇)
% 1,618 次推理,0.000 CPU 在 0.000 秒内(100% CPU,5129994 唇)
% 1,757 次推理,0.000 秒内 0.000 CPU(100% CPU,5044792 唇)
真的。
哇!多一点?
?- member(U,[10,100,1000]), time((N in 1..U, n_factorial (N,_),false)) ; 真的。
% 34,511 次推理,0.004 秒内 0.004 CPU(100% CPU,9591041 唇)
% 3,091,271 次推论,0.322 秒内 0.322 CPU(100% CPU,9589264 唇)
% 305,413,871 推理,90.732 CPU 在 90.721 秒内(100% CPU,3366116 唇)
真的。
?-成员(U,[10,100,1000]),时间((1..U中的N,n_fac(N,_),假));真的。
% 3,729 次推断,0.001 秒内 0.001 CPU(100% CPU,2973653 唇)
% 36,369 推理,0.004 CPU 在 0.004 秒内(100% CPU,10309784 唇)
% 362,471 推理,0.036 CPU 在 0.036 秒内(100% CPU,9979610 唇)
真的。
底线?
- 此答案中提供的代码与您应该去的一样低级:忘记
is/2
!
- 冗余约束可以而且确实有回报。
- 算术运算的顺序(计数“向上”与“向下”)也可以产生很大的不同。
- 如果要计算一些“大”的阶乘
N
,请考虑使用不同的方法。
- 使用clpfd!