📜  斐波那契模(1)

📅  最后修改于: 2023-12-03 14:55:03.599000             🧑  作者: Mango

斐波那契模

简介

斐波那契数列是指这样一个数列:0、1、1、2、3、5、8、13、21、34、…… 在数学上,斐波那契数列以如下被以递推的方法定义:

F(0) = 0,F(1) = 1,
F(n) = F(n – 1) + F(n – 2)(n ≥ 2,n ∈ N*)。

而斐波那契模是指在求斐波那契数列中的某个数时,取模的结果。即:

F(n) % m = ?

为了方便表示,我们用 Fm(n) 表示 F(n) % m。

求解
暴力法

直接按照斐波那契数列的定义,求出数列中的每个数,然后对其取模,即可得到斐波那契模。

def fib_mod_v1(n: int, m: int) -> int:
    if n < 2:
        return n % m
    else:
        return (fib_mod_v1(n - 1, m) + fib_mod_v1(n - 2, m)) % m

但是,该方法时间复杂度非常高,无法处理较大的 n 值。

数学法

我们可以利用数学知识,推导出斐波那契模的求解公式。

由于斐波那契数列的递推公式为:F(n) = F(n – 1) + F(n – 2),我们可以整理出以下矩阵:

[ F(n) ]   [ 1  1 ] [ F(n-1) ]
[        ] = [      ] [        ]
[ F(n-1) ]   [ 1  0 ] [ F(n-2) ]

使用矩阵快速幂,可以将时间复杂度优化到 O(logn)。

from typing import List

def matrix_mul(A: List[List[int]], B: List[List[int]]) -> List[List[int]]:
    """
    矩阵乘法
    """
    m, n, s = len(A), len(B[0]), len(A[0])
    res = [[0] * n for _ in range(m)]
    for i in range(m):
        for j in range(n):
            for k in range(s):
                res[i][j] += A[i][k] * B[k][j]
                res[i][j] %= mod
    return res

def matrix_pow(A: List[List[int]], n: int) -> List[List[int]]:
    """
    矩阵快速幂
    """
    res = [[1, 0], [0, 1]]
    while n:
        if n & 1:
            res = matrix_mul(res, A)
        A = matrix_mul(A, A)
        n >>= 1
    return res

def fib_mod_v2(n: int, m: int) -> int:
    if n < 2:
        return n % m
    else:
        # 矩阵快速幂
        A = [[1, 1], [1, 0]]
        A_pow = matrix_pow(A, n - 1)
        return A_pow[0][0] % m
鸽笼原理

根据鸽笼原理,当 m 足够大时,必定会出现两个相邻的数 F(i) 和 F(i+1) 他们在模 m 意义下相等,即:F(i) % m == F(i+1) % m。

因此当我们计算 F(n) % m 时,我们可以计算出所有小于 m² 的 F(i) % m 的值,找到其中连续相等的数对 F(i) 和 F(i+1),即可计算出 F(n) % m。

def fib_mod_v3(n: int, m: int) -> int:
    if n < 2:
        return n % m
    else:
        # 计算所有小于 m² 的斐波那契数对 m 取模的余数
        fib_mod_list = [0, 1]
        while True:
            fib_mod_list.append((fib_mod_list[-1] + fib_mod_list[-2]) % m)
            if fib_mod_list[-2:] == [0, 1]:
                fib_mod_list.pop()
                break
        
        i = n % len(fib_mod_list)
        return fib_mod_list[i]
总结
  • 暴力法时间复杂度高,无法处理较大的 n 值。
  • 数学法利用矩阵快速幂可以将时间复杂度优化到 O(logn)。
  • 鸽笼原理利用了模 m 的特点,不需要计算所有的斐波那契数,而是计算小于 m² 的数就可以得到 F(n) % m。

以上为斐波那契模的几种解法。具体使用哪种方法,需要根据数据量大小和时间限制等因素进行选择。