矩阵快速幂&斐波那契数列

矩阵快速幂&斐波那契数列

矩阵快速幂:

  1. 快速地求出斐波那契数列中的每一项
  2. 可以快速地求出斐波那契数列的前n项的和

首先我们来看如何快速地求出斐波那契数列的第n项

1.快速求斐波那契数列的某一项

F n = [ f n , f n + 1 ] F_n = [f_n,f_{n+1}] Fn=[fn,fn+1],构造这一个行向量,那么对于此,我们思考 F n F_n Fn乘一个什么样的矩阵可以得到 F n + 1 F_{n+1} Fn+1
显然:可以乘一个这样子的矩阵 哈哈
然后我们对这个递推的操作一直乘矩阵A,就可以推出最终 F n F_n Fn的表达式:
F n = F 0 ∗ A n F_n = F_0 * A^n Fn=F0An,求出 F n F_n Fn之后,自然就得到 f n f_n fn了!

2.快速求斐波那契数列的前n项和

法1

这个其实具体是一个推公式的过程,没有涉及到其他的东西,
S n = f 0 + f 1 + f 2 + . . . + f n S_n = f_0 + f_1 + f_2 +...+f_n Sn=f0+f1+f2+...+fn
具体的公式为:
S n = f n + 2 − f 1 S_n = f_{n+2} - f_1 Sn=fn+2f1 注意在我规定的斐波那契数列中,是从第0项开始的,公式具体的推导如下:
在这里插入图片描述
因此若要求斐波那契数列的前n项和,只需要求出 f n + 2 f_{n+2} fn+2即可,那么这个问题其实又回到了求斐波那契数列的第n项,这样来看就很简单啦!

法2

构造一个新的行向量,
F n = [ f n , f n + 1 , S n ] F_n = [f_n,f_{n+1},S_n] Fn=[fn,fn+1,Sn] ,这里的 S n S_n Sn就表示的是前n项的和,那么我们还是像开始一样思考一下如何构造矩阵,使得 F n F_n Fn可以通过递推来求得。很容易推导出矩阵A的形式应该为:
在这里插入图片描述
因此我们可以通过此递推得到: F n = F 0 ∗ A n F_n = F_0 * A^{n} Fn=F0An
这样只要我们把 F n F_n Fn求出来,就可以得到结果啦!

习题1:1303. 斐波那契前 n 项和

[1303. 斐波那契前 n 项和 - AcWing题库]:

附上AC代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
LL n,m;
const int N = 3;
int a[N][N] = {
    {0,1,0},
    {1,1,1},
    {0,0,1},
};
// 矩阵乘法:计算行向量 * 一个矩阵
void mul(int c[],int a[],int b[][N]){   // 计算 a[] * b[][]  -> 即:f1[] * a[][] 之后的值
    int tmp[N] = {0};    //  表示最后的答案数组
    
    for(int i = 0;i < N;i++){  // i表示答案的那一列
        // c[i] 表示的值应该等于 a中的每一个数 * b中第i列的那个数 的求和
        for(int j = 0;j < N;j++){  // j枚举的就是a中的每一个数
            tmp[i] = (tmp[i] + (LL)a[j] * b[j][i]) % m;
        }
    }
    
    memcpy(c,tmp,sizeof(tmp));
    
}
// 矩阵乘法:计算一个矩阵 * 一个矩阵
void mul(int c[][N],int a[][N],int b[][N]){
    int tmp[N][N] = {0};
    
    for(int i = 0;i < N;i++){
        for(int j = 0;j < N;j++){  // 枚举计算tmp[i][j]
            // 计算c[i][j]需要枚举的就是a[i][]的那一行的数 * b[][j]那一列的数
            for(int k = 0;k < N;k++){
                tmp[i][j] = (tmp[i][j] + (LL)a[i][k] * b[k][j]) % m;
            }
        }
    }
    
    memcpy(c,tmp,sizeof(tmp));
    
}

int main()
{
    cin >> n >> m;
    int f1[N] = {1,1,1};   // 表示的是  [f1,f2,S1]
    // Fn为向量
    // Fn = F1 * A^n-1  次幂
    LL k = n - 1;
    // 写快速幂
    while(k){
        if(k & 1) mul(f1,f1,a);   // f1[] = f1[] * a[][]
        k >>= 1;
        mul(a,a,a);        // a[][] = a[][] * a[][]  
    }
    
    cout << f1[2] << endl;
    return 0;
}

3.快速求斐波那契数列前n项的平方和

如何快速地求出 f 0 2 + f 1 2 + . . . + f n 2 f_0^2 + f_1^2 + ... + f_n^2 f02+f12+...+fn2 呢?
先给出结论: f 0 2 + f 1 2 + . . . + f n 2 = f i ∗ f i + 1 f_0^2 + f_1^2 + ... + f_n^2 = f_i * f_{i + 1} f02+f12+...+fn2=fifi+1
下面给出证明的过程:
在这里插入图片描述
其实只要知道了结论,具体做起题目来就简单多了!

习题2:斐波那契的秘密

http://csoj.scnu.edu.cn/problem/S0413

附上AC代码:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N = 2,mod = 1e9+7;
int a[N][N] = {
    {0,1},
    {1,1},
};
int f0[N];
int backup[N][N];
int t;
int n;
int l1,r1,l2,r2;
void mul(int c[][N],int a[][N],int b[][N]){
    int tmp[N][N] = {0};
    for(int i = 0;i < N;i++){
        for(int j = 0;j < N;j++){
            for(int k = 0;k < N;k++){
                tmp[i][j] = (tmp[i][j] + (LL)a[i][k] * b[k][j]) % mod;
            }
        }
    }
    memcpy(c,tmp,sizeof(tmp));
}
void mul(int c[],int a[],int b[][N]){
    int tmp[N] = {0};
    for(int i = 0;i < N;i++){
        for(int j = 0;j < N;j++){
            tmp[i] = (tmp[i] + (LL)a[j] * b[j][i]) % mod;
        }
    }
    memcpy(c,tmp,sizeof(tmp));
}
LL F(int x){  // 求Fx   Fn = F0 * A^n次幂
    memcpy(a,backup,sizeof(backup));
    f0[0] = f0[1] = n;
    while(x){
        if(x & 1) mul(f0,f0,a);
        x >>= 1;
        mul(a,a,a);
    }
    return (LL)f0[0];
}
LL qmi(LL a,LL k,LL p){
    LL res = 1;
    while(k){
        if(k & 1) res =  res * a % p;
        k >>= 1;
        a = a * a % p;
    }
    return res;
}
int main()
{
    cin >> n;
    int t;
    cin >> t;
    memcpy(backup,a,sizeof(a));
    while(t--){
        cin >> l1 >> r1 >> l2 >> r2;
        // cout << F(l1 - 1) << " " << F(l1) << " " << F(r1) << " "  << F(r1+1) << endl;
        // cout << F(r2 + 2) << " " << F(l2 + 1) << endl;
        LL fenzi = ((F(r1) * F(r1 + 1) % mod - F(l1 - 1) * F(l1) % mod) % mod + mod) % mod;
        // cout << "fenzi = " << fenzi << endl;
        LL fenmu = ((F(r2 + 2) - F(l2 + 1)) % mod + mod) % mod;
        // 然后求逆元
        LL fenmu2 = qmi(fenmu,mod-2,mod);
        // cout << "分母 = " << fenmu2 << endl;
        cout << fenzi * fenmu2 % mod << endl;
    }
    return 0;
}
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值