为什么这个Fibonacci数函数不能在O(log N)中起作用?

时间:2018-03-07 21:23:34

标签: fibonacci

因此log(N)的Fibonacci数 - 没有矩阵。

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

现在我们编写一个递归函数,在每一步我们取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);
}
&#13;
&#13;
&#13;

错误在哪里?

4 个答案:

答案 0 :(得分:4)

你得到了一个努力的递归公式:T(n)= 4T(n / 2)+ O(1)。 (无视数字变大的事实,所以O(1)甚至不成立)。由此可以清楚地看出T(n)不在O(log(n))中。取而代之的是主定理T(n)在O(n ^ 2)中。

顺便说一句,这比计算所有斐波纳契数最多为n的普通算法要慢。

答案 1 :(得分:2)

函数内部的四个N调用各自具有大约i / 2的参数。因此,总共N个调用的堆栈长度大致等于log 2 N,但是因为每个调用再生成四个,所以调用的底部“层”有4 ^ log 2 < / sub> N = O(n 2 )因此,错误是N自称四次。只有两次调用,如在传统的迭代方法中,它将是O(n)。我不知道有任何方法只使用一次调用,可能是O(log n)。

基于此公式的O(n)版本将是:

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;
}

每个函数调用2个N,使其成为O(n)。

答案 2 :(得分: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)));
}

}

我尝试编写java中定义的问题。我观察到的是上面代码的复杂性并不完全是O(N ^ 2)而是小于那个。但是根据惯例和标准,最坏情况复杂度是O(N ^ 2),包括一些其他因素,如计算(除法,乘法) )和比较时间分析。

上面代码的输出给了我关于函数多少次的信息 fun(int i)计算并被调用。

<强>输出 enter image description here

因此包括比较和除法,乘法运算所花费的时间,最坏情况下的时间复杂度是O(N ^ 2)而不是O(LogN)。

好的,如果我们使用Analysis of the recursive Fibonacci program技术。然后我们最终得到一个简单的等式

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

其中O(1)是一些恒定时间。 那么让我们在这个等式上应用 Master's方法。 根据师父的方法

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

以下三种情况:

  1. 如果f(n)=Θ(nc),其中c
  2. 如果f(n)=Θ(nc)其中c = Logba则T(n)=Θ(ncLog n)
  3. 如果f(n)=Θ(nc),其中c> Logba然后T(n)=Θ(f(n))
  4. 在我们的等式中 a = 4 b = 2 &amp;的 C = 0 即可。 由于案例1 c < logba => 0 < 2(对数基数为2且等于2)已满足 因此T(n) = O(n^2)

    有关master算法如何工作的更多信息,请访问:Analysis of Algorithms

答案 3 :(得分:1)

你的想法是正确的,只要你不计算相同的公式,它就会在O(log n)中执行 一遍又一遍地。具有N(k)* N(i-k)的整个点是(k = i-k),因此您只需要计算一个而不是两个。但是如果你只是递归调用,那么你正在执行两次计算。

您需要的是 memoization 。也就是说,存储您已经计算过的每个值,以及 如果它再次出现,那么你可以在O(1)中得到它。

这是一个例子

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];
}