【问题标题】:Prolog factorial recursionProlog阶乘递归
【发布时间】:2012-03-06 01:09:06
【问题描述】:

我无法理解以下阶乘程序

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 中最后一行在递归调用之前执行?

【问题讨论】:

  • 进行了调用,当它完成时,控件返回以继续执行以下目标(如果存在)。对自身的呼唤与否无关紧要。一个电话就是一个电话。

标签: recursion prolog factorial


【解决方案1】:

顺便说一句,一旦您了解了基本递归,请尽可能尝试实现尾递归,如下所示:

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 了解它。

【讨论】:

  • 目标factorial(0,0)应该失败---它不会。
【解决方案2】:

不,递归调用首先发生!它必须,否则最后一个子句毫无意义。该算法分解为:

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

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

您的 prolog 实现可能有一种启用跟踪的方法,它可以让您看到整个算法正在运行。这可能会对你有所帮助。

【讨论】:

  • 我希望你的意思是factorial(0) => 1 :)
  • factorial(-1) 怎么样?有了上面的定义,查询?- fact1(-1,_). 正确地失败了; factorial 没有。
【解决方案3】:

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

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

让我们尝试两种方法并比较运行时。一、@m09的factorial/2

?-时间((阶乘(100000,_),假))。 % 200,004 次推理,1.606 CPU 在 1.606 秒内(100% CPU,124513 唇) 错误的。

接下来,我们以树的形式进行 - 使用 reduce/3lambda expressions

?- time((numlist(1,100000,Xs),reduce(\X^Y^XY^(XY is X*Y),Xs,_),false))。 % 1,300,042 次推理,0.264 CPU 在 0.264 秒内(100% CPU,4922402 唇) 错误的。

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

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

有什么收获?让我们问秒表

?-时间((numlist(1,100000,Xs),reduce(x_y_product,Xs,_),false))。 % 500,050 次推理,0.094 CPU 在 0.094 秒内(100% CPU,5325635 唇) 错误的。

【讨论】:

    【解决方案4】:
    factorial(1, 1).
    factorial(N, Result) :- M is N - 1,
    factorial(M, NextResult), Result is NextResult * N.
    

    【讨论】:

    • 您应该在答案中添加解释。仅代码帖子是不够的。阅读here了解详情。
    【解决方案5】:

    声明了基本情况。 N必须为正且与前项相乘的条件。

     factorial(0, 1).
     factorial(N, F) :- 
           N > 0, 
           Prev is N -1, 
           factorial(Prev, R), 
           F is R * N.
    

    运行:

    阶乘(-1,X)。

    【讨论】:

      【解决方案6】:

      一个简单的方法:

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

      【讨论】:

        【解决方案7】:

        我会这样做:

        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).
        

        所以你打电话给:

        非尾递归方法:fact(3, Result)。

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

        这可能会有所帮助;)

        【讨论】:

        • ?- fact(0,1), false. 不会终止。它应该。 ?- tail_fact(0,1,0), false. 相同
        • 你说得对! fact(0,0).fact(N, R):- fact_aux(N, R).fact_aux(0, 1).fact_aux(N, R):- N &gt; 0, NewN is N - 1, fact_aux(NewN, Rec), R is N * Rec.
        • 好一点。但是?- fact(0,0). 应该会失败。它成功了。
        • 顺便说一句。这看起来与之前的答案非常相似:stackoverflow.com/a/28799827/4609915
        【解决方案8】:

        非尾递归:

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

        尾递归:

        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).
        

        【讨论】:

        • 你在使用visual-prolog吗?
        • 你使用的是哪个 Prolog 系统?
        • 使用 SWI-Prolog,?- fact(10,F). 循环并且不给出答案。
        • 这是视觉序言中停止条件的问题,在正确输出但在 0 之后没有停止,因此导致堆栈溢出
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-01-28
        • 2015-04-19
        • 2019-08-06
        • 2019-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多