Search code examples
crecursiontail-recursion

tail recursive version of the fibonacci function


I am having trouble understanding the concept of tail recursion, I want to make a tail recursive version of the fibonacci function and so far this is what I came up with but I don't know if its correct or not,can someone help me out, any help would be appreciated

#include <stdio.h>

int fibonacci(int n)
{
    if(n==1)return 1;
    if(n==2)return 1;
    return fibonacci(n-1)+fibonacci(n-2);
}


int fibonacci_tail_Recusive(int n,int prev1,int prev2)
{
    
    if(n<0) return -1;
    if(n==1||n==0) return prev2;
    return fibonacci_tail_Recusive(n-1,prev2+prev1,prev1);
}

int fibonacci_tail_Recusive_wrapper(int n)
{
    return fibonacci_tail_Recusive(n,1,1);
}


int main()
{
    printf("tail recursive result: %d  normal recursive result:%d", fibonacci_tail_Recusive_wrapper(23) ,fibonacci(23));

    return 0;
}

the code compiles and outputs the correct result


Solution

  • int fibonacci_tail_Recusive(int n,int prev1,int prev2)
    {
        
        if(n<0) return -1;
        if(n==1||n==0) return prev2;
        return fibonacci_tail_Recusive(n-1,prev2+prev1,prev1);
    }
    

    This function is, correctly, tail-recursive.

    A tail-recursive function is one where all the paths end (i.e., return) either a value (-1 for negative, prev2 for 1 and 0) or a call to a function (it doesn't need to be itself directly; though if it doesn't call itself either directly or indirectly it wouldn't be tail-recursive).

    Fibonacci isn't a great example to show tail-recursion because here it muddles the benefit of tail-recursion (being equivalent to an iterative loop) with the optimization of avoiding redundant calls (the original fibonacci function calls itself twice in the final case).

    Consider the factorial function:

    int factorial(int n)
    {
        if (n == 0 || n == 1) return 1;
        return factorial(n - 1) * n;
    }
    

    When you call factorial(5), the call stack looks like this:

    factorial(5)
       5 * factorial(4)
       5 * (4 * factorial(3))
       5 * (4 * (3 * factorial(2)))
       5 * (4 * (3 * (2 * factorial(1))))
       5 * (4 * (3 * (2 * 1)))
       5 * (4 * (3 * 2))
       5 * (4 * 6)
       5 * 24
       120
    

    at each step, a multiplication is waiting for the next operand so it can calculate its result, meaning each needs to hold some amount of memory for each step.

    With the tail-recursive function, like this:

    int factorial(int n, int acc)
    {
        if (n == 0 || n == 1) return acc;
        return factorial(n - 1, acc * n);
    }
    

    the call stack looks like this:

    factorial(5, 1);
    factorial(4, 5);
    factorial(3, 20);
    factorial(2, 60);
    factorial(1, 120);
    120
    

    since at each step the function has finished all the calculations it needs to perform, it doesn't need to hold any memory for the results; each call overwrites the current frame; in other words, it could be rewritten as a loop:

    int factorial(int n, int acc)
    {
        while (true) {
            if (n == 0 || n == 1) return acc;
            acc = acc * n;
            n = n - 1;
        }
    }
    

    If the compiler is smart enough, the code for the tail-recursive function gets converted to the equivalent code that this function would generate.