Fibonacci 2
本题目是一道由张子臻老师授课的15级移动算法设计课程中的通过矩阵快速幂求解斐波那契数列并取余的题目
在开始打代码解决本题之前,应该先懂得两个数学原理
- 两个整数的乘数取余 = 两个整数分别取余后再相乘再取余
- 两个整数的和取余 = 两个整数分别取余后再相加再取余
如何证明上面的两个数学原理,我们可以用对每个数的拆分进行分析:
假设分别有两个整数a、b,先要计算他们的乘数除以c的余数,与他们的和除以C的余数。则我们可以假设a = c*m1 + n1(其中m1、n1均是整数) b = c*m2 +n2(其中m2、n2均是整数),因此a*b = c*m1*c*m2+c*m1*n2+n1*c*m2+n1*n2,则a*b对c取余就等于n1*n2对c取余,又因为a对c取余就为n1,b对c取余就是n2,因此验证了上述的第一个数学原理。同理:a+b = c*(m1+m2)+n1+n2,则a+b对c取余就等于n1+n2对c取余。
利用此原理我们就可以算很大很大的数除以10^9+7的余数,而本题最大的考点也在于如何快速地求出一个很大的斐波那契数列并且不会使数字越界。
- 对于如何快速求解斐波那契数列,那当然是利用矩阵求解,令该满足求解条件的矩阵为A,则fib(k) = A^k-1;而在求解的过程还要用到快速幂的算法(快速幂即通过将幂的次数转换为二进制数省略0位的计算加快整个乘法的过程)
- 而为了令数字不越界,则要在每次相乘求幂的过程都让得出的矩阵的每一个元素对10^9+7进行取余,以保证矩阵的各个元素不会超出10^9+7的大小,从而不会造成数字越界。
总而言之,我们在求解斐波那契数列的时候应利用矩阵快速幂的方法进行求解。当我们要计算斐波那契数列对某个数进行取余时,正常情况我们想到的是先把斐波那契数列的那一项求出来再进行取余,但在这题中为了不让斐波那契数列的数字越界,应该边进行相乘幂运算的时候边取余。
以下则为提交的代码:
#include<iostream>
#include<cmath>
using namespace std;
class Matrix{
private:
long long m[2][2];
public:
void Init(){
m[0][0] = 1;
m[0][1] = 1;
m[1][0] = 1;
m[1][1] = 0;
}
void Unit(){
m[0][0] = 1;
m[0][1] = 0;
m[1][0] = 0;
m[1][1] = 1;
}
long long Get() const
{
return m[0][0];
}
friend Matrix operator*(const Matrix& A, const Matrix& B){
Matrix AB;
for(int i = 0; i < 2; i++){
for(int j = 0; j < 2; j++){
AB.m[i][j] = 0;
for(int k = 0; k < 2; k++){
//long long a = A.m[i][k] % (pow(10,9) + 7);
//long long b = B.m[k][j] % (pow(10,9) + 7);
AB.m[i][j] += (A.m[i][k] % 1000000007) * (B.m[k][j] % 1000000007);
AB.m[i][j] = AB.m[i][j] % (1000000007);
}
}
}
return AB;
}
};
long long powMod(Matrix A, long long e)
{
if(e == 0) return 1;
if(e == -1) return 0;
int ret = 1;
Matrix t;
t.Unit();
while(e){
if(e & 1){
t = t * A;
}
A = A * A;
e >>= 1;
}
return t.Get();
}
int main()
{
long long n;
while(cin >> n){
Matrix A;
A.Init();
cout << powMod(A, n-1) << endl;
}
}