Prolog阶乘递归

时间:2012-03-06 01:09:06

标签: recursion prolog factorial

我无法理解以下因子程序

fact1(0,Result) :-
    Result is 1.
fact1(N,Result) :-
    N > 0,
    N1 is N-1,
    fact1(N1,Result1),
    Result is Result1*N.

fact1被嵌套在第二个fact1中时,这是否意味着永远不会调用最后一行Result is Result1*N.?或者在Prolog中,最后一行是在递归调用之前执行的吗?

9 个答案:

答案 0 :(得分:12)

一旦你理解了基本的递归,就试着尽可能地实现尾递归,这里它是:

factorial(N, R) :- factorial(N, 1, R).
factorial(0, R, R) :- !.
factorial(N, Acc, R) :-
    NewN is N - 1,
    NewAcc is Acc * N,
    factorial(NewN, NewAcc, R).

尾递归与先前使用的递归不同,允许解释器/编译器在进行下一步递归时刷新上下文。因此,假设您计算factorial(1000),您的版本将维持1000个上下文,而我的版本只维持1.这意味着您的版本最终将无法计算所需的结果,只会导致Out of call stack memory错误崩溃。

您可以在维基百科上read more了解它。

答案 1 :(得分:7)

这样做的一个相当简单的方法是:

fac(0,1).
fac(N,F):-
    N>0,N1 is N-1,fac(N1,F1),F is N*F1.

答案 2 :(得分:6)

不,递归调用首先发生!它必须,或者最后一个条款毫无意义。该算法分解为:

factorial(0) => 1
factorial(n) => factorial(n-1) * n;

如您所见,您需要在乘法之前计算递归的结果,以便返回正确的值!

您的prolog实现可能有一种方法来启用跟踪,这将让您看到整个算法运行。这可能会帮助你。

答案 3 :(得分:5)

一般来说,@m09's answer基本上是关于尾递归的重要性的。

对于大N,以不同方式计算产品会获胜!想想“二叉树”,而不是“线性列表”......

让我们尝试两种方式并比较运行时间。首先,@ m09的factorial/2

?- time((factorial(100000,_),false)).
% 200,004 inferences, 1.606 CPU in 1.606 seconds (100% CPU, 124513 Lips)
false.

接下来,我们使用 reduce/3lambda expressions一起使用树形式:

?- time((numlist(1,100000,Xs),reduce(\X^Y^XY^(XY is X*Y),Xs,_),false)).
% 1,300,042 inferences, 0.264 CPU in 0.264 seconds (100% CPU, 4922402 Lips)
false.

最后,让我们定义并使用专用的辅助谓词x_y_product/3

x_y_product(X, Y, XY) :- XY is X*Y.

有什么好处?我们问秒表

?- time((numlist(1,100000,Xs),reduce(x_y_product,Xs,_),false)).
% 500,050 inferences, 0.094 CPU in 0.094 seconds (100% CPU, 5325635 Lips)
false.

答案 4 :(得分:2)

factorial(1, 1).
factorial(N, Result) :- M is N - 1,
factorial(M, NextResult), Result is NextResult * N.

答案 5 :(得分:2)

宣布基本案例。 N必须为正且与前一项相乘的条件。

{{1}}

运行:

  

阶乘(-1,X)。

答案 6 :(得分:0)

一种简单的方法:

 factorial(N, F):- N<2, F=1.

 factorial(N, F) :- 
     M is N-1, 
     factorial(M,T), 
     F is N*T.

答案 7 :(得分:-1)

我会做类似的事情:

fact(0, 1).
fact(N, Result):-
    Next is N - 1,
    fact(Next, Recursion),
    Result is N * Recursion.

尾部版本就像:

tail_fact(0, 1, 0).         /* when trying to calc factorial of zero */
tail_fact(0, Acc, Res):-    /* Base case of recursion, when reaches zero return Acc */
     Res is Acc.
tail_fact(N, Acc, Res):-    /* calculated value so far always goes to Acc */
     NewAcc is N * Acc,
     NewN is N - 1,
     tail_fact(NewN, NewAcc, Res).

所以你打电话给:

非尾递归方法:事实(3,结果)。

尾递归方法: tail_fact(3,1,Result)。

这可能会有所帮助;)

答案 8 :(得分:-1)

非tailer递归:

 fact(0,1):-!. 
   fact(X,Y):- Z=X-1,
         fact(Z,NZ),Y=NZ*X.

tailer递归:

fact(X,F):- X>=0,fact_aux(X,F,1).
fact_aux(0,F,F):-!.
   fact_aux(X,F,Acc):- 
       NAcc=Acc*X, NX=X-1, 
    fact_aux(NX,F,NAcc).