斐波那契数(快速幂计算的推理过程)

算法题目: 509. 斐波那契数
文章中存在些许公式,建议 PC 端打开观感更佳

思路

方阵(行列数相等的矩阵)快速幂

解题过程

  1. 解析斐波那契数列
    • 已知斐波那契数列:

      F ( n ) = { n , 当 n < 2 F ( n − 1 ) + F ( n − 2 ) , 当 n > = 2 F(n)= \begin{cases} \ n, &当\ n\ <\ 2\\2ex F(n-1)+F(n-2), &当\ n\ >=\ 2 \end{cases} F(n)=⎩ ⎨ ⎧ n,F(n−1)+F(n−2),当 n < 2当 n >= 2

    • 我们分析 F ( n ) = F ( n − 1 ) + F ( n − 2 ) F(n)=F(n-1)+F(n-2) F(n)=F(n−1)+F(n−2)部分,根据表达式我们可以得到:

      F ( n ) = 1 1 F ( n − 1 ) F ( n − 2 ) \begin{bmatrix} F(n) \\ \end{bmatrix} =\begin{bmatrix} 1 & 1 \\ \end{bmatrix} \begin{bmatrix} F(n-1) \\ F(n-2) \\ \end{bmatrix} F(n)=11F(n−1)F(n−2)

    • 通过上述的分析结果,如果想要实现递推,则需要将左右部分演化为相同的规则:

      F ( n ) F ( n − 1 ) F ( n − 1 ) F ( n − 2 ) 的关系 即 \begin{bmatrix} F(n) \\ F(n-1) \\ \end{bmatrix} 与 \begin{bmatrix} F(n-1) \\ F(n-2) \\ \end{bmatrix} 的关系 即F(n)F(n−1)F(n−1)F(n−2)的关系

    • 很简单的计算出: F ( n − 1 ) = 1 ∗ F ( n − 1 ) + 0 ∗ F ( n − 2 ) F(n-1)=1*F(n-1)+0*F(n-2) F(n−1)=1∗F(n−1)+0∗F(n−2)

      F ( n − 1 ) = 1 0 F ( n − 1 ) F ( n − 2 ) 即 \begin{bmatrix} F(n-1) \\ \end{bmatrix} =\begin{bmatrix} 1 & 0 \\ \end{bmatrix} \begin{bmatrix} F(n-1) \\ F(n-2) \\ \end{bmatrix} 即F(n−1)=10F(n−1)F(n−2)

    • 所以可以得出:

      F ( n ) F ( n − 1 ) = 1 1 1 0 F ( n − 1 ) F ( n − 2 ) \begin{bmatrix} F(n) \\ F(n-1) \\ \end{bmatrix} =\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} \begin{bmatrix} F(n-1) \\ F(n-2) \\ \end{bmatrix} F(n)F(n−1)=1110F(n−1)F(n−2)

    • 可以递推出公式:

      F ( n ) F ( n − 1 ) = 1 1 1 0 . . . 1 1 1 0 F ( 1 ) F ( 0 ) \begin{bmatrix} F(n) \\ F(n-1) \\ \end{bmatrix} =\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix}... \begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} \begin{bmatrix} F(1) \\ F(0) \\ \end{bmatrix} F(n)F(n−1)=1110...1110F(1)F(0)

    • 相当于需要计算 M n − 1 M^{n-1} Mn−1,其中方阵 M M M为:

      M = 1 1 1 0 M=\begin{bmatrix} 1 & 1 \\ 1 & 0 \\ \end{bmatrix} M=1110

  2. 快速幂的计算方式
    • 假设计算 a n a^n an,令 m m m为 n n n使用二进制表示的最高有效位
    • 已知 n > > k n>>k n>>k为右移操作,且 n & 1 = 1 n\ \&\ 1=1 n & 1=1时表示最低位为 1 1 1
    • 所以如果 ( n > > k ) & 1 = 1 (n>>k)\ \&\ 1=1 (n>>k) & 1=1则表示第 k + 1 k+1 k+1位为1,否则为 0 0 0
    • 则:
      n = ( n & 1 ) ∗ 2 0 + ( ( n > > 1 ) & 1 ) ∗ 2 1 + . . . + ( ( n > > m ) & 1 ) ∗ 2 m a n = a ( n & 1 ) ∗ 2 0 + ( ( n > > 1 ) & 1 ) ∗ 2 1 + . . . + ( ( n > > m ) & 1 ) ∗ 2 m a n = a ( n & 1 ) ∗ 2 0 ∗ a ( ( n > > 1 ) & 1 ) ∗ 2 1 ∗ . . . ∗ a ( ( n > > m ) & 1 ) ∗ 2 m \begin{align} n & =(n\ \&\ 1)*2^0+((n>>1)\ \&\ 1)*2^1+...+((n>>m)\ \&\ 1)*2^m\\ a^n & =a^{(n\ \&\ 1)*2^0+((n>>1)\ \&\ 1)*2^1+...+((n>>m)\ \&\ 1)*2^m} \\ a^n & =a^{(n\ \&\ 1)*2^0}*a^{((n>>1)\ \&\ 1)*2^1}*...*a^{((n>>m)\ \&\ 1)*2^m} \\ \end{align} nanan=(n & 1)∗20+((n>>1) & 1)∗21+...+((n>>m) & 1)∗2m=a(n & 1)∗20+((n>>1) & 1)∗21+...+((n>>m) & 1)∗2m=a(n & 1)∗20∗a((n>>1) & 1)∗21∗...∗a((n>>m) & 1)∗2m
    • 继续对计算进行拆分
      • 根据上述公式可以得出: a n = S m = ∏ i = 0 m a ( ( n > > i ) & 1 ) ∗ 2 i a^n=S_m=\mathop{\prod}\limits_{i=0}^m {a^{((n>>i)\ \&\ 1)*2^i}} an=Sm=i=0∏ma((n>>i) & 1)∗2i
      • ( n > > i ) & 1 = 0 (n>>i)\ \&\ 1=0 (n>>i) & 1=0,则 S i = S i − 1 S_i=S_{i-1} Si=Si−1
      • ( n > > i ) & 1 = 1 (n>>i)\ \&\ 1=1 (n>>i) & 1=1,则 S i = S i − 1 ∗ a 2 i S_i=S_{i-1}*a^{2^i} Si=Si−1∗a2i
      • 其中 a 2 i a^{2^i} a2i为当前项,所以只需要每次迭代完计算下一项即可:
        • 初始时第一项为 a 2 0 a^{2^{0}} a20,即 a a a自身,所以 A 0 = a A_0=a A0=a
        • 计算 a 2 i + 1 = a 2 i ∗ 2 = ( a 2 i ) 2 a^{2^{i+1}}=a^{2^{i}*2}=(a^{2^{i}})^2 a2i+1=a2i∗2=(a2i)2,即 A i + 1 = A i ∗ A i A_{i+1}=A_i*A_i Ai+1=Ai∗Ai
      • 定义迭代初始值:以本题为例,则为 2 ∗ 2 2*2 2∗2的单位矩阵(对角线全为 1 1 1其余全为 0 0 0的矩阵),因为单位矩阵乘以相同行列的方阵结果为对应的方阵,即:
        S − 1 = 1 0 0 1 S_{-1}= \begin{bmatrix} 1 & 0 \\ 0 & 1 \\ \end{bmatrix} S−1=1001
      • n > > i n>>i n>>i作为是否合法的条件进行迭代:
        • 计算当前迭代值:
          • ( ( n > > i ) & 1 = 1 ((n>>i)\ \&\ 1=1 ((n>>i) & 1=1,则 S i = S i − 1 ∗ A i S_i=S_{i-1}*A_i Si=Si−1∗Ai
        • 计算下一项的值: A i + 1 = A i ∗ A i A_{i+1}=A_i*A_i Ai+1=Ai∗Ai
        • i++,不过可以直接通过n >>= 1修改 n n n,同时将判断条件也换为 n n n

复杂度

  • 时间复杂度: O ( l o g n ) O(log\ n) O(log n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
JavaScript 复制代码
// 快速幂 Exponentiation by Squaring
class EBS {
  result = null;
  constructor() {
    if (new.target === EBS) {
      throw Error("请使用派生类");
    }
  }
  init() {
    throw Error("请重写init方法");
  }
  multiply() {
    throw Error("请重写multiply方法");
  }
  calculate(a, n) {
    // 获取单位值:数字为1,矩阵则为单位矩阵
    // 单位矩阵需要手动设置方阵的阶数
    let result = this.result === null ? this.init() : this.result;
    while (n) {
      if (n & 1) {
        result = this.multiply(result, a);
      }
      n >>= 1;
      a = this.multiply(a, a);
    }
    return (this.result = result);
  }
}

// 方阵(行列数相等的矩阵)快速幂
class MatrixEBS extends EBS {
  #max = 0;
  #initArray() {
    return new Array(this.#max).fill(0).map(() => new Array(this.#max).fill(0));
  }
  // 定义单位矩阵:即对角线全为1其余全为0的矩阵
  init(max) {
    this.#max = max;
    const result = this.#initArray();
    for (let n = 0; n < max; n++) result[n][n] = 1;
    return (this.result = result);
  }
  // 计算a*b:方阵a和b为相同的行列数
  multiply(a, b) {
    const c = this.#initArray();
    const max = this.#max;
    for (let i = 0; i < max; i++) {
      for (let j = 0; j < max; j++) {
        let sums = 0;
        for (let k = 0; k < max; k++) {
          sums += a[i][k] * b[k][j];
        }
        c[i][j] = sums;
      }
    }
    return c;
  }
}

/**
 * @param {number} n
 * @return {number}
 */
var fib = function (n) {
  if (n <= 1) return n;
  // 先计算 [[1, 1], [1, 0]] 的 n-1 次幂
  const ebs = new MatrixEBS();
  const M = [
    [1, 1],
    [1, 0],
  ];
  ebs.init(M.length);
  const result = ebs.calculate(M, n - 1);
  // 再乘以[[1], [0]],相当于直接取其第一行第一列的值
  return result[0][0];
};
相关推荐
QiLinkOS30 分钟前
【从实验室到商业战场:发明专利如何重塑科技与企业的共生生态】
大数据·c语言·数据结构·c++·人工智能·单片机·算法
小白兔奶糖ovo1 小时前
【Leetcode】231. 2的幂
linux·算法·leetcode
xiaoxiaoxiaolll1 小时前
《Light: Science & Applications》合并BIC实现80倍阈值单模运行:超紧凑光子晶体激光器新突破
人工智能·算法·机器学习
Peter·Pan爱编程2 小时前
14. Lambda 表达式:随手可写的函数对象
c++·算法·ai编程
-To be number.wan2 小时前
算法日记 | 暴力枚举
学习·算法
s_w.h2 小时前
【 linux 】动静态库的制作
linux·运维·服务器·算法·bash
过期动态2 小时前
【LeetCode 热题 100】接雨水
java·数据结构·算法·leetcode·职场和发展
春日见2 小时前
5分钟入门强化学习之动态规划算法与实现
大数据·人工智能·python·算法·机器学习·计算机视觉
scx_link3 小时前
线性回归的总结:
算法·机器学习·线性回归
郝亚军3 小时前
IEEE 754 单精度浮点的SEM表示
开发语言·c++·算法