在O(logn)中找到第n个fib数

时间:2015-08-28 13:56:41

标签: c fibonacci

我想解决这个问题: SPOJ problem

经过一些研究,我发现它归结为第n个纤维数的简单计算,但是n可以变得非常大,因此O(n)解决方案不会有任何好处。谷歌搜索我发现你可以计算O(logn)中的第n个fib数,还有一个完全符合的代码样本:

long long fibonacci(int n)
{
    long long fib[2][2]= {{1,1},{1,0}},ret[2][2]= {{1,0},{0,1}},tmp[2][2]= {{0,0},{0,0}};
    int i,j,k;
    while(n)
    {
        if(n&1)
        {
            memset(tmp,0,sizeof tmp);
            for(i=0; i<2; i++) for(j=0; j<2; j++) for(k=0; k<2; k++)
                        tmp[i][j]=(tmp[i][j]+ret[i][k]*fib[k][j]);
            for(i=0; i<2; i++) for(j=0; j<2; j++) ret[i][j]=tmp[i][j];
        }
        memset(tmp,0,sizeof tmp);
        for(i=0; i<2; i++) for(j=0; j<2; j++) for(k=0; k<2; k++)
                    tmp[i][j]=(tmp[i][j]+fib[i][k]*fib[k][j]);
        for(i=0; i<2; i++) for(j=0; j<2; j++) fib[i][j]=tmp[i][j];
        n/=2;
    }
    return (ret[0][1]);
}

我试图针对这个问题修改它,但仍然得到WA:http://ideone.com/3TtE5m

我计算模运算错了吗?或者是其他问题?

3 个答案:

答案 0 :(得分:5)

你的意思是我希望第n个斐波那契。

为了做到这一点,你需要对here所描述的斐波那契数字进行矩阵分解。

基本思路是你将Donald E. Knuth矩阵身份表格用于斐波那契数字:

fib matrix equation

而不是以传统的方式计算斐波纳西数,你将尝试找到(k)的幂的矩阵,其中k被要求给出数字。

所以这解决了k矩阵乘法中的问题,因为我们可以更容易地做到这一点,所以没有用。

但是等等!我们可以优化矩阵乘法。我们不是先进行k乘法,而是先对它进行平方,然后进行乘法的一半。我们可以继续这样做。因此,如果给定的数字是2 ^ a,那么我们可以分步进行。通过保持矩阵的平方。

如果矩阵不是正方形,我们可以对数字进行二进制分解,看看是否将给定的平方矩阵转化为最终乘积。

在每次乘法后的情况下,您还需要将模运算符123456应用于每个矩阵编号。

希望我的解释有助于如果没有看到更清晰和更长的链接。

实际上还有一个任务的警告,因为要求你提供一些给定数量模数的斐波纳契数。您还应该证明采用每个矩阵元素的提醒不会改变操作。换句话说,如果我们乘以矩阵并提醒我们实际上仍然得到斐波那契数字提醒。但是由于提醒操作是分布式的并且在乘法中它实际上产生了正确的结果。

答案 1 :(得分:3)

斐波纳契数的出现是enter image description here的连续分数的连续收敛的比率,并且由任何连续分数的连续收敛的矩阵形成的行列式为fscanf(stdin,...)+1

enter image description here

矩阵表示形式给出了斐波那契数的以下闭合形式表达式,即

enter image description here

矩阵乘以−1次,因为只有这样我们才能得到n斐波那契数作为结果矩阵中行(n+1)th的行和列的元素。

如果我们应用上述方法而不使用(0, 0)Time Complexity: O(n)以外的矩阵递归乘法。

但是我们想要Space Complexity: O(1),因此我们必须优化上述方法,并且可以通过对矩阵进行递归乘法来获得Time Complexity: O(log n)的幂。

上述规则的执行情况可以在下面找到。

nth

答案 2 :(得分:2)

有一个非常简单的算法,只使用整数:

long long fib(int n) {
    long long a, b, p, q;
    a = q = 1;
    b = p = 0;
    while (n > 0) {
        if (n % 2 == 0) {
            long long qq = q*q;
            q = 2*p*q + qq;
            p = p*p + qq;
            n /= 2;
        } else {
            long long aq = a*q;
            a = b*q + aq + a*p;
            b = b*p + aq;
            n -= 1;
        }
    }
    return b;
}

这基于identities of the Lucas sequence