从Fibonacci到尾递归(tail recurrence)

     Fibonacci函数有两种很经典的实现

一种是以while为基础的:

public long Fib_Loop(int n)
{
     long b1 = 1, b2 = 1, temp, begin = 3;
     for (; begin <= n; ++begin)
     {
        temp = b1;
        b1 = b2;
        b2 = temp + b2;
     }
     return b2;
}

 一种是以递归为基础的:

public long Fib_Recur(int n)
{
    if (n == 1 || n == 2)     //line1
        return 1;     //line2
    else
        return Fib_Recur(n - 1) + Fib_Recur(n - 2);     //line 3
}

 很明显这种递归效率极低:

Fib(6) = Fib(5) + Fib(4);
     = Fib(4) + Fib(3) + Fib(3) + Fib(2);
     = Fib(3) + Fib(2) + Fib(2) + Fib(1) + Fib(2) + Fib(1) + Fib(2);
     = Fib(2) + Fib(1) + Fib(2) + Fib(2) + Fib(1) + Fib(2) + Fib(1) + Fib(2);
     = 8

       Notice that the first call on line 3,Fib(N-1),actually computes Fib(N-2) at some point.This information is thrown away and recomputed by the second call on line3.The amount of information thrown away componds recursively and results in the huge running time.This is perhaps the finest example of the maxim "Don't compute anything more than once" and should not scare you away from using recursion.

                                                                                      --《Data Structures and Algorithm Analysis》

另外一种高效率的递归:

public long Fib_Tail(int n)
{
      if (n == 1 || n == 2)
        return 1;
      else
        return Tail(n, 1, 1, 3);
}

//Typical help function...
private long Tail(int n, long b1, long b2, int begin)
{
      if (n == begin)
      {
        return b1 + b2;
      }
      else
        return Tail(n, b2, b1 + b2, begin + 1);
}

 使用尾递归,而且计算几乎完全发生在尾递归中。

 

再说到Scala中的尾递归。由于函数式编程提倡在函数体中不适用var,所以一般的while式的命令式代码(imperative code)是建议被替换的,也就是说你可能会先写出while形式的语句,然后对其进行一番考究,看其是否能转换为函数式的代码。同时,在Scala中,尾递归是会被编译器优化的(虽然编写尾递归是有许多的限制),使其能够达到一般while语句的执行效率。

 

def boom(x: Int): Int =
     if (x == 0) throw new Exception("boom!")
     else boom(x - 1) + 1

//This function is not tail recursive, because it performs an increment operation 
//after the recursive call.
 
def bang(x: Int): Int =
     if (x == 0) throw new Exception("bang!")
     else bang(x 1)

     A tail-recursive function will not build a new stack frame for each call; all calls will execute in a single frame.

     The Scala compiler detects tail recursion and replaces it with a jump back to the beginning of the function, after updating the function parameters with the new values.
     The moral is that you should not shy away from using recursive algorithms to solve your problem. Often, a recursive solution is more elegant and concise than a loop-based one. If the solution is tail recursive, there won’t be any runtime overhead to be paid.

你可能感兴趣的:(编程,scala)