本文主要讲斐波那契数列结合矩阵快速幂求解的办法。
问题
先看下面的问题: [ 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时有:
由于(1)恒成立,所以当n>=2时有:
又
所以,用矩阵快速幂求解斐波那契数列的本质就是求解:
从而将斐波那契数列问题转化为矩阵乘法的问题,又可以进一步转化为矩阵快速幂。
代码
#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都是自己改出来的。有进步。