题目链接

PEEK149 斐波那契数列

题目描述

经典的斐波那契数列 定义如下:

给定一个非常大的整数 ,计算 在模 意义下的值。

解题思路

的值非常大时(例如 ),直接使用循环递推计算 的方法(时间复杂度为 )将会超时。这是一个典型的可以使用矩阵快速幂进行优化的线性递推问题。

1. 构造递推矩阵

斐波那契数列的核心递推关系是 。为了将其转化为矩阵形式,我们需要一个状态向量。我们选择向量 。我们的目标是找到一个 的转移矩阵 ,使得:

根据递推公式,我们可以写出:

将这个线性方程组写成矩阵形式,我们得到:

这个 就是我们需要的转移矩阵,我们称之为

2. 矩阵快速幂

利用上述关系,我们可以进行递推:

为了求 ,我们可以从一个已知的初始状态开始。最方便的初始状态是

根据题目定义,。所以我们的任务变成了计算矩阵 ,然后与初始向量 相乘。

对于非常大的指数(如 ),计算 可以使用快速幂算法。其原理与整数的快速幂完全相同:

  • 若指数 是偶数,则
  • 若指数 是奇数,则

通过这种方式,我们可以在 次矩阵乘法内计算出 。由于矩阵大小是常数(),每次矩阵乘法的时间复杂度也是常数。

3. 算法流程

  1. 处理特殊情况:如果 ,根据定义直接返回
  2. 定义转移矩阵
  3. 使用矩阵快速幂算法计算
  4. 最终结果 这个结果向量的第一个分量。
  5. 所有计算都在模 的意义下进行。

代码

#include <iostream>
#include <vector>

using namespace std;

const int MOD = 1000000007;

// 定义矩阵结构
struct Matrix {
    long long mat[2][2];

    Matrix() {
        mat[0][0] = mat[0][1] = mat[1][0] = mat[1][1] = 0;
    }
};

// 矩阵乘法
Matrix multiply(Matrix A, Matrix B) {
    Matrix C;
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 2; j++) {
            for (int k = 0; k < 2; k++) {
                C.mat[i][j] = (C.mat[i][j] + A.mat[i][k] * B.mat[k][j]) % MOD;
            }
        }
    }
    return C;
}

// 矩阵快速幂
Matrix power(Matrix A, long long p) {
    Matrix result;
    result.mat[0][0] = result.mat[1][1] = 1; // 单位矩阵
    while (p > 0) {
        if (p % 2 == 1) {
            result = multiply(result, A);
        }
        A = multiply(A, A);
        p /= 2;
    }
    return result;
}

int main() {
    long long n;
    while (cin >> n) {
        if (n <= 2) {
            cout << 1 << endl;
            continue;
        }

        Matrix T;
        T.mat[0][0] = 1; T.mat[0][1] = 1;
        T.mat[1][0] = 1; T.mat[1][1] = 0;

        T = power(T, n - 2);

        long long ans = (T.mat[0][0] * 1 + T.mat[0][1] * 1) % MOD;
        cout << ans << endl;
    }
    return 0;
}
import java.util.Scanner;

public class Main {
    static int MOD = 1000000007;

    // 矩阵乘法
    static long[][] multiply(long[][] a, long[][] b) {
        long[][] c = new long[2][2];
        for (int i = 0; i < 2; i++) {
            for (int j = 0; j < 2; j++) {
                for (int k = 0; k < 2; k++) {
                    c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % MOD;
                }
            }
        }
        return c;
    }

    // 矩阵快速幂
    static long[][] power(long[][] a, long p) {
        long[][] res = new long[2][2];
        res[0][0] = res[1][1] = 1; // 单位矩阵
        while (p > 0) {
            if ((p & 1) == 1) {
                res = multiply(res, a);
            }
            a = multiply(a, a);
            p >>= 1;
        }
        return res;
    }

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        while (sc.hasNextLong()) {
            long n = sc.nextLong();
            if (n <= 2) {
                System.out.println(1);
                continue;
            }

            long[][] T = new long[2][2];
            T[0][0] = 1; T[0][1] = 1;
            T[1][0] = 1; T[1][1] = 0;

            T = power(T, n - 2);

            long ans = (T[0][0] * 1 + T[0][1] * 1) % MOD;
            System.out.println(ans);
        }
    }
}
import sys

MOD = 1000000007

# 矩阵乘法
def multiply(a, b):
    c = [[0, 0], [0, 0]]
    for i in range(2):
        for j in range(2):
            for k in range(2):
                c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % MOD
    return c

# 矩阵快速幂
def power(a, p):
    res = [[1, 0], [0, 1]]  # 单位矩阵
    while p > 0:
        if p % 2 == 1:
            res = multiply(res, a)
        a = multiply(a, a)
        p //= 2
    return res

for line in sys.stdin:
    if not line.strip():
        continue
    n = int(line)
    
    if n <= 2:
        print(1)
        continue
        
    T = [[1, 1], [1, 0]]
    
    T = power(T, n - 2)
    
    # 结果是 T * [F(2), F(1)]^T 的第一个元素
    # F(n) = T[0][0]*F(2) + T[0][1]*F(1)
    ans = (T[0][0] * 1 + T[0][1] * 1) % MOD
    print(ans)

算法及复杂度

  • 算法: 矩阵快速幂
  • 时间复杂度: 矩阵的维度是 ,为常数。矩阵快速幂需要进行 次矩阵乘法,每次乘法的时间复杂度是 。因此,总时间复杂度为
  • 空间复杂度: 算法需要存储几个 的矩阵用于计算,所需空间为常数。因此,空间复杂度为