6

So the Fibonacci number for log (N) — without matrices.

Ni // i-th Fibonacci number
= Ni-1 + Ni-2              // by definition
= (Ni-2 + Ni-3) + Ni-2     // unwrap Ni-1
= 2*Ni-2 + Ni-3            // reduce the equation
= 2*(Ni-3 + Ni-4) + Ni-3   //unwrap Ni-2
                           // And so on
= 3*Ni-3 + 2*Ni-4
= 5*Ni-4 + 3*Ni-5
= 8*Ni-5 + 5*Ni-6

= Nk*Ni-k + Nk-1*Ni-k-1

Now we write a recursive function, where at each step we take k~=I/2.

static long N(long i)
{
    if (i < 2) return 1;
    long k=i/2;
    return N(k) * N(i - k) + N(k - 1) * N(i - k - 1);
}

Where is the fault?

shadowspawn
  • 3,039
  • 22
  • 26
Yellowfun
  • 418
  • 1
  • 9
  • 21

4 Answers4

4

You get a recursion formula for the effort: T(n) = 4T(n/2) + O(1). (disregarding the fact that the numbers get bigger, so the O(1) does not even hold). It's clear from this that T(n) is not in O(log(n)). Instead one gets by the master theorem T(n) is in O(n^2).

Btw, this is even slower than the trivial algorithm to calculate all Fibonacci numbers up to n.

Henry
  • 42,982
  • 7
  • 68
  • 84
2

The four N calls inside the function each have an argument of around i/2. So the length of the stack of N calls in total is roughly equal to log2N, but because each call generates four more, the bottom 'layer' of calls has 4^log2N = O(n2) Thus, the fault is that N calls itself four times. With only two calls, as in the conventional iterative method, it would be O(n). I don't know of any way to do this with only one call, which could be O(log n).

An O(n) version based on this formula would be:

static long N(long i) {
    if (i<2) {
        return 1;
    }
    long k = i/2;
    long val1;
    long val2;
    val1 = N(k-1);
    val2 = N(k);
    if (i%2==0) {
        return val2*val2+val1*val1;
    }
    return val2*(val2+val1)+val1*val2;
}

which makes 2 N calls per function, making it O(n).

2
public class fibonacci {
public static int count=0;
public static void main(String[] args) {

    Scanner scan = new Scanner(System.in);
    int i = scan.nextInt();
    System.out.println("value of i ="+ i);
    int result = fun(i);
    System.out.println("final result is " +result);

}
public static int fun(int i) {
    count++;
    System.out.println("fun is called and count is "+count);
    if(i < 2) {
        System.out.println("function returned");
        return 1;
    }
    int k = i/2;
    int part1 = fun(k);
    int part2 = fun(i-k);
    int part3 = fun(k-1);
    int part4 = fun(i-k-1);
    return ((part1*part2) + (part3*part4)); /*RESULT WILL BE SAME FOR BOTH METHODS*/
    //return ((fun(k)*fun(i-k))+(fun(k-1)*fun(i-k-1)));
}

}

I tried to code to problem defined by you in java. What i observed is that complexity of above code is not completely O(N^2) but less than that.But as per conventions and standards the worst case complexity is O(N^2) including some other factors like computation(division,multiplication) and comparison time analysis.

The output of above code gives me information about how many times the function fun(int i) computes and is being called.

OUTPUT enter image description here

So including the time taken for comparison and division, multiplication operations, the worst case time complexity is O(N^2) not O(LogN).

Ok if we use Analysis of the recursive Fibonacci program technique.Then we end up getting a simple equation

T(N) = 4* T(N/2) + O(1)

where O(1) is some constant time. So let's apply Master's method on this equation. According to Master's method

T(n) = aT(n/b) + f(n) where a >= 1 and b > 1

There are following three cases:

  1. If f(n) = Θ(nc) where c < Logba then T(n) = Θ(nLogba)
  2. If f(n) = Θ(nc) where c = Logba then T(n) = Θ(ncLog n)
  3. If f(n) = Θ(nc) where c > Logba then T(n) = Θ(f(n))

And in our equation a=4 , b=2 & c=0. As case 1 c < logba => 0 < 2 (which is log base 2 and equals to 2) is satisfied hence T(n) = O(n^2).

For more information about how master's algorithm works please visit: Analysis of Algorithms

Dinesh Kumar
  • 545
  • 4
  • 17
1

Your idea is correct, and it will perform in O(log n) provided you don't compute the same formula over and over again. The whole point of having N(k) * N(i-k) is to have (k = i - k) so you only have to compute one instead of two. But if you only call recursively, you are performing the computation twice.

What you need is called memoization. That is, store every value that you already have computed, and if it comes up again, then you get it in O(1).

Here's an example

const int MAX = 10000;

// memoization array
int f[MAX] = {0};

// Return nth fibonacci number using memoization
int fib(int n) {
    // Base case
    if (n == 0)
        return 0;
    if (n == 1 || n == 2)
        return (f[n] = 1);

    // If fib(n) is already computed
    if (f[n]) return f[n];

    // (n & 1) is 1 iff n is odd
    int k = n/2;

    // Applying your formula
    f[n] = fib(k) * fib(n - k) + fib(k - 1) * fib(n - k - 1);

    return f[n];
}
Robindar
  • 484
  • 2
  • 9