我无法理解以下因子程序
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中,最后一行是在递归调用之前执行的吗?
答案 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.
接下来,我们使用meta-predicate reduce/3
和lambda 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).