I was studying about Tail call recursion and came across some documentation that mentioned. Sun Java doesn't implement tail call optimization. I wrote following code to calculate fibonacci number in 3 different ways: 1. Iterative 2. Head Recursive 3. Tail Recursive
public class Fibonacci {
public static void main(String[] args) throws InterruptedException {
int n = Integer.parseInt(args[0]);
System.out.println("\n Value of n : " + n);
System.out.println("\n Using Iteration : ");
long l1 = System.nanoTime();
fibonacciIterative(n);
long l2 = System.nanoTime();
System.out.println("iterative time = " + (l2 - l1));
System.out.println(fibonacciIterative(n));
System.out.println("\n Using Tail recursion : ");
long l3 = System.nanoTime();
fibonacciTail(n);
long l4 = System.nanoTime();
System.out.println("Tail recursive time = " + (l4 - l3));
System.out.println(fibonacciTail(n));
System.out.println("\n Using Recursion : ");
long l5 = System.nanoTime();
fibonacciRecursive(n);
long l6 = System.nanoTime();
System.out.println("Head recursive time = " + (l6 - l5));
}
private static long fibonacciRecursive(int num) {
if (num == 0) {
return 0L;
}
if (num == 1) {
return 1L;
}
return fibonacciRecursive(num - 1) + fibonacciRecursive(num - 2);
}
private static long fibonacciIterative(int n) throws InterruptedException {
long[] arr = new long[n + 1];
arr[0] = 0;
arr[1] = 1;
for (int i = 2; i <= n; i++) {
// Thread.sleep(1);
arr[i] = arr[i - 1] + arr[i - 2];
}
return arr[n];
}
private static long fibonacciTail(int n) {
if (n == 0)
return 0;
return fibHelper(n, 1, 0, 1);
}
private static long fibHelper(int n, int m, long fibM_minus_one, long fibM) {
if (n == m)
return fibM;
return fibHelper(n, m + 1, fibM, fibM_minus_one + fibM);
}
}
On running this program I drew some results:
System specs:
Intel core 5 processor,
Windows XP,
32 bit Java 1.6
Default stack size for JVM.
Java doesn't have tail call optimization for the same reason most imperative languages don't have it. Imperative loops are the preferred style of the language, and the programmer can replace tail recursion with imperative loops.
As we all know, the simplest algorithm to generate Fibonacci sequence is as follows: if(n<=0) return 0; else if(n==1) return 1; f(n) = f(n-1) + f(n-2); But this algorithm has some repetitive calculation. For example, if you calculate f(5), it will calculate f(4) and f(3).
Write a tail recursive function for calculating the n-th Fibonacci number. A recursive function is tail recursive when the recursive call is the last thing executed by the function.
fib() is a recursive function, and it's called 67 times.
Does it mean that java does some Tail call optimization internally?
No, it does not. The HotSpot JIT compilers do not implement tail-call optimization.
The results you are observing are typical of the anomalies that you see in a Java benchmark that doesn't take account of JVM warmup. For instance, the "first few" times a method is called, it will be executed by the interpreter. Then the JIT compiler will compile the method ... and it will get faster.
To get meaningful results, put a loop around the whole lot and run it a number of times until the timings stabilize. Then discard the results from the early iterations.
... why I did it give StackOverflowError at n > 5000?
That's just evidence that there isn't any tail-call optimization happening.
For the first question, what is 2^50 (or something close)? Each number N in a recursive Fib function calls it twice (prior 2). Each of those calls 2 prior iterations, etc.. so it's grows to 2^(N-k) of recursion (k is probably 2 or 3).
The 2nd question is because the 2nd one is a straight N recursion. Instead of going double headed (N-1),(N-2)
, it simply builds up from M=1, M=2... M=N. Each step of the way, the N-1 value is retained for adding. Since it is an O(N) operation, it is comparable to the iterative method, the only difference being how the JIT compiler optimizes it. The problem with recursion though is that it requires a huge memory footprint for each level that you stack onto the frame - you run out of memory or stack space at some limit. It should still generally be slower than the iterative method.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With