nod51-1242斐波那契数列-矩阵快速幂


本文主要讲斐波那契数列结合矩阵快速幂求解的办法。

问题

先看下面的问题: [ nod51-1242]

题目描述:
斐波那契数列的定义如下:
F(0) = 0
F(1) = 1
F(n) = F(n - 1) + F(n - 2) (n >= 2)
(1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, …)
给出n,求F(n),由于结果很大,输出F(n) % 1000000009的结果即可。
Input
输入1个数n(1 <= n <= 10^18)。
Output:
输出F(n) % 1000000009的结果。
Input示例
11
Output示例
89

思路

矩阵二分求幂的思路之前讲过。主要说下斐波那契数列求解如何转化为矩阵的幂,见下面推导。
当n>=2时有:

[1110][Fn1Fn2]=[FnFn1](1)

由于(1)恒成立,所以当n>=2时有:
[1110]n1[F1F0]=[FnFn1](2)


[F1F0]=[10](3)

所以,用矩阵快速幂求解斐波那契数列的本质就是求解:
[1110]n1(4)

从而将斐波那契数列问题转化为矩阵乘法的问题,又可以进一步转化为矩阵快速幂。

代码


#include <iostream>
#include <cstring>
typedef long long ll;
const int maxn = 2;
const int MOD = 1000000009;

struct Matrix
{
    int size_;
    ll mat_[maxn][maxn];

    Matrix( int s = 0 ) : size_(s)
    {
        std::memset(mat_, 0, sizeof(mat_));
    }
    Matrix( const Matrix& rhs )
    {
        size_ = rhs.size_;
        *this = rhs;
    }

    int init()
    {
        mat_[0][0] = 1;
        mat_[0][1] = 1;
        mat_[1][0] = 1;
        mat_[1][1] = 0;

        return 0;
    }
    int set_diag( int val )
    {
        for( int i = 0; i < size_; ++i )
        {
            for( int j = 0; j < size_; ++j )
            {
                mat_[i][j] = (i==j)?val:0;
            }
        }
        return 0;
    }
    int print()
    {
        for( int i = 0; i < size_; ++i )
        {
            for( int j = 0; j < size_; ++j )
            {
                std::cout << mat_[i][j] < " ";
            }
            std::cout << std::endl;
        }
        return 0;
    }
    Matrix& operator=( const Matrix& rhs )
    {
        for( int i = 0; i < rhs.size_; ++i )
        {
            for( int j = 0; j < rhs.size_; ++j )
            {
                mat_[i][j] = rhs.mat_[i][j];
            }
        }
        return *this;
    }
    Matrix operator*( const Matrix& rhs )
    {
        Matrix ret(rhs.size_);
        for( int row = 0; row < size_; ++row )
        {
            for( int col = 0; col < size_; ++col )
            {
                for( int i = 0; i < size_; ++i )
                {
                    ret.mat_[row][col] = (ret.mat_[row][col] + (mat_[row][i]%MOD * rhs.mat_[i][col]%MOD)%MOD)%MOD;
                }
            }
        }
        return ret;
    }
    Matrix operator^( ll b )
    {
        Matrix ans(2);
        ans.set_diag(1);
        Matrix w(*this);
        while(b)
        {
            if(b%2)
                ans = ans*w;
            w = w*w;
            b /= 2;
        }
        return ans;
    }

};

int main(void)
{
    ll n;
    while(std::cin >> n)
    {
        Matrix a(2);
        a.init();

        Matrix ret = a^(n-1);
        int ans = ret.mat_[0][0];
        std::cout << ans << std::endl;
    }
    return 0;
}

bug

出了两个bug。分别是过测试点的时候。第一次只过了一个,第二次过了一部分。当然,错误都是很明显的。
其实,在整体思路肯定没有错误的时候。因为这个题目可以确定。
那么只能就是边界数据的问题。

这个题目由于取的MOD = 1000000009非常的大。所以矩阵做乘法,两个数据相乘的时候,即使取模了之后,对于数字来说任然是非常大的。
此时两个数字相乘可能会越界溢出。因为我没有意识到这个MOD取的非常大,所以取模之后的数字任然是非常大的。
所以,相乘导致溢出的问题。当然,在修改每个变量的类型之后。问题得到解决。这是第一个bug。

对于第二个bug,我发现是对输入数据的分析不够。因为对于n的取值范围非常大的。超过int。所以修改为ll。
问题搞定。其实MOD虽然取的非常大,但是取模之后不会超过int的范围,矩阵乘法两个数相乘之后不会超过long long的范围。
所以,long long是可以允许的。这是分析出来的。

总结

确定bug之后,大胆去试就好,不一定判断准确,但是不试试肯定不知道。本题的两个bug都是自己改出来的。有进步。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值