JavaScript 实现斐波那契数列的多种方法详解

斐波那契数列是计算机科学和数学中的一个经典问题,也是面试中经常被问到的算法题目。本文将详细介绍 JavaScript 中实现斐波那契数列的 8 种不同方法,并对它们的性能特点进行分析比较。

什么是斐波那契数列?

斐波那契数列是一个无限序列,其定义如下:

  • F(0) = 0
  • F(1) = 1
  • F(n) = F(n-1) + F(n-2) (当 n ≥ 2 时)

数列的前几项为:0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144…

1. 递归实现(最基础版本)

function fibonacciRecursive(n) {
  if (n <= 1) return n;
  return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2);
}

// 测试
console.log(fibonacciRecursive(10)); // 55
console.log(fibonacciRecursive(20)); // 6765
// console.log(fibonacciRecursive(50)); // 非常慢,可能导致堆栈溢出

特点:

  • 代码简洁直观,直接反映数学定义
  • 时间复杂度:O(2^n) — 指数级增长,极其低效
  • 空间复杂度:O(n) — 由于递归调用栈
  • 不适用于大数计算

2. 递归实现(带备忘录优化)

function fibonacciMemoization(n, memo = {}) {
  if (n <= 1) return n;
  if (memo[n]) return memo[n];
  
  memo[n] = fibonacciMemoization(n - 1, memo) + fibonacciMemoization(n - 2, memo);
  return memo[n];
}

// 测试
console.log(fibonacciMemoization(10)); // 55
console.log(fibonacciMemoization(50)); // 12586269025
console.log(fibonacciMemoization(100)); // 354224848179262000000

特点:

  • 使用备忘录缓存已计算结果,避免重复计算
  • 时间复杂度:O(n) — 线性时间
  • 空间复杂度:O(n) — 存储备忘录和调用栈
  • 相比基础递归版本性能大幅提升

3. 迭代实现(循环)

function fibonacciIterative(n) {
  if (n <= 1) return n;
  
  let a = 0, b = 1, temp;
  for (let i = 2; i <= n; i++) {
    temp = a + b;
    a = b;
    b = temp;
  }
  return b;
}

// 测试
console.log(fibonacciIterative(10)); // 55
console.log(fibonacciIterative(50)); // 12586269025
console.log(fibonacciIterative(100)); // 354224848179262000000

特点:

  • 使用循环代替递归
  • 时间复杂度:O(n) — 线性时间
  • 空间复杂度:O(1) — 常数空间,只需存储几个变量
  • 性能优异,是最常用的实现方式之一

4. 动态规划实现

function fibonacciDP(n) {
  if (n <= 1) return n;
  
  const dp = [0, 1];
  for (let i = 2; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2];
  }
  return dp[n];
}

// 测试
console.log(fibonacciDP(10)); // 55
console.log(fibonacciDP(50)); // 12586269025
console.log(fibonacciDP(100)); // 354224848179262000000

特点:

  • 显式使用数组存储中间结果
  • 时间复杂度:O(n)
  • 空间复杂度:O(n) — 存储整个数组
  • 比迭代版本占用更多内存,但更直观展示动态规划思想

5. 矩阵幂实现(数学优化)

基于矩阵幂运算的数学公式:

[ F(n)   F(n-1)  ]   = [ 1 1 ] ^ (n-1)
[ F(n-1) F(n-2)  ]     [ 1 0 ]
function matrixMultiply(a, b) {
  return [
    [a[0][0] * b[0][0] + a[0][1] * b[1][0], 
    a[0][0] * b[0][1] + a[0][1] * b[1][1]],
    [a[1][0] * b[0][0] + a[1][1] * b[1][0], 
    a[1][0] * b[0][1] + a[1][1] * b[1][1]]
  ];
}

function matrixPower(mat, power) {
  let result = [[1, 0], [0, 1]]; // 单位矩阵
  while (power > 0) {
    if (power % 2 === 1) {
      result = matrixMultiply(result, mat);
    }
    mat = matrixMultiply(mat, mat);
    power = Math.floor(power / 2);
  }
  return result;
}

function fibonacciMatrix(n) {
  if (n <= 1) return n;
  
  const matrix = [[1, 1], [1, 0]];
  const resultMatrix = matrixPower(matrix, n - 1);
  return resultMatrix[0][0];
}

// 测试
console.log(fibonacciMatrix(10)); // 55
console.log(fibonacciMatrix(50)); // 12586269025
console.log(fibonacciMatrix(100)); // 354224848179262000000

特点:

  • 基于数学矩阵运算
  • 时间复杂度:O(log n) — 使用快速幂算法
  • 空间复杂度:O(1)
  • 理论上最优解,适合极大数计算
  • 实现较复杂,常数因子较大,在普通应用中可能不如迭代版本快

6. 闭包实现(生成器模式)

function createFibonacciGenerator() {
  let a = 0, b = 1;
  
  return function() {
    const temp = a;
    a = b;
    b = temp + b;
    return temp;
  };
}

// 测试
const generateFibonacci = createFibonacciGenerator();
console.log(generateFibonacci()); // 0
console.log(generateFibonacci()); // 1
console.log(generateFibonacci()); // 1
console.log(generateFibonacci()); // 2
console.log(generateFibonacci()); // 3
console.log(generateFibonacci()); // 5
// 可以无限调用生成数列

特点:

  • 使用闭包保存状态
  • 每次调用返回下一个斐波那契数
  • 适合需要逐个生成数列的场景
  • 时间复杂度:O(1) 每次调用
  • 空间复杂度:O(1)

7. 生成器函数实现(ES6)

function* fibonacciGenerator() {
  let a = 0, b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

// 测试
const fibGen = fibonacciGenerator();
console.log(fibGen.next().value); // 0
console.log(fibGen.next().value); // 1
console.log(fibGen.next().value); // 1
console.log(fibGen.next().value); // 2
console.log(fibGen.next().value); // 3
console.log(fibGen.next().value); // 5
// 可以无限调用

特点:

  • 使用 ES6 生成器函数
  • 惰性求值,按需生成
  • 代码简洁优雅
  • 适合需要逐个获取数列的场景

8. 通项公式实现(Binet公式)

斐波那契数列的通项公式(Binet公式):

F(n) = (φ^n - ψ^n) / √5
其中 φ = (1 + √5)/2 ≈ 1.618(黄金比例)
ψ = (1 - √5)/2 ≈ -0.618
function fibonacciFormula(n) {
  const sqrt5 = Math.sqrt(5);
  const phi = (1 + sqrt5) / 2;
  const psi = (1 - sqrt5) / 2;
  return Math.round((Math.pow(phi, n) - Math.pow(psi, n)) / sqrt5);
}

// 测试
console.log(fibonacciFormula(10)); // 55
console.log(fibonacciFormula(20)); // 6765
console.log(fibonacciFormula(50)); // 12586269025
console.log(fibonacciFormula(70)); // 190392490709135

特点:

  • 直接使用数学公式计算
  • 时间复杂度:O(1) — 理论上
  • 实际受限于浮点数精度,n较大时会有精度误差
  • JavaScript中大约在n=75左右开始出现精度问题
  • 适合中等规模n的计算

性能比较

下表比较了不同实现方式在计算F(40)时的性能表现(测试环境:Node.js 16.x):

方法时间复杂度空间复杂度计算F(40)时间(ms)
基础递归O(2^n)O(n)~1100ms
备忘录递归O(n)O(n)~0.05ms
迭代O(n)O(1)~0.02ms
动态规划O(n)O(n)~0.03ms
矩阵幂O(log n)O(1)~0.1ms
Binet公式O(1)O(1)~0.01ms

最佳实践建议

  1. 一般情况:使用迭代法,它在代码简洁性、性能和内存使用之间取得了良好平衡
  2. 需要多次计算:使用备忘录递归,可以缓存之前的结果
  3. 极大数计算:考虑矩阵幂方法,虽然实现复杂但时间复杂度最优
  4. 按需生成数列:使用生成器函数或闭包实现
  5. 避免使用:基础递归实现,性能极差

完整示例代码

下面是一个完整的实现,包含所有方法并添加了性能测试:

// 1. 基础递归
function fibonacciRecursive(n) {
  if (n <= 1) return n;
  return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2);
}

// 2. 备忘录递归
function fibonacciMemoization(n, memo = {}) {
  if (n <= 1) return n;
  if (memo[n]) return memo[n];
  
  memo[n] = fibonacciMemoization(n - 1, memo) + fibonacciMemoization(n - 2, memo);
  return memo[n];
}

// 3. 迭代
function fibonacciIterative(n) {
  if (n <= 1) return n;
  
  let a = 0, b = 1, temp;
  for (let i = 2; i <= n; i++) {
    temp = a + b;
    a = b;
    b = temp;
  }
  return b;
}

// 4. 动态规划
function fibonacciDP(n) {
  if (n <= 1) return n;
  
  const dp = [0, 1];
  for (let i = 2; i <= n; i++) {
    dp[i] = dp[i - 1] + dp[i - 2];
  }
  return dp[n];
}

// 5. 矩阵幂
function matrixMultiply(a, b) {
  return [
    [a[0][0] * b[0][0] + a[0][1] * b[1][0], a[0][0] * b[0][1] + a[0][1] * b[1][1]],
    [a[1][0] * b[0][0] + a[1][1] * b[1][0], a[1][0] * b[0][1] + a[1][1] * b[1][1]]
  ];
}

function matrixPower(mat, power) {
  let result = [[1, 0], [0, 1]];
  while (power > 0) {
    if (power % 2 === 1) {
      result = matrixMultiply(result, mat);
    }
    mat = matrixMultiply(mat, mat);
    power = Math.floor(power / 2);
  }
  return result;
}

function fibonacciMatrix(n) {
  if (n <= 1) return n;
  const matrix = [[1, 1], [1, 0]];
  const resultMatrix = matrixPower(matrix, n - 1);
  return resultMatrix[0][0];
}

// 6. 闭包生成器
function createFibonacciGenerator() {
  let a = 0, b = 1;
  return function() {
    const temp = a;
    a = b;
    b = temp + b;
    return temp;
  };
}

// 7. ES6生成器
function* fibonacciGenerator() {
  let a = 0, b = 1;
  while (true) {
    yield a;
    [a, b] = [b, a + b];
  }
}

// 8. Binet公式
function fibonacciFormula(n) {
  const sqrt5 = Math.sqrt(5);
  const phi = (1 + sqrt5) / 2;
  const psi = (1 - sqrt5) / 2;
  return Math.round((Math.pow(phi, n) - Math.pow(psi, n)) / sqrt5);
}

// 性能测试函数
function testPerformance(fn, n, name) {
  const start = process.hrtime.bigint();
  const result = fn(n);
  const end = process.hrtime.bigint();
  console.log(`${name}(${n}) = ${result}, 耗时: ${(end - start) / 1000000n}ms`);
}

// 测试
const testNum = 40;

console.log('--- 性能测试 ---');
testPerformance(fibonacciRecursive, testNum, '基础递归');
testPerformance(fibonacciMemoization, testNum, '备忘录递归');
testPerformance(fibonacciIterative, testNum, '迭代');
testPerformance(fibonacciDP, testNum, '动态规划');
testPerformance(fibonacciMatrix, testNum, '矩阵幂');
testPerformance(fibonacciFormula, testNum, 'Binet公式');

console.log('\n--- 生成器测试 ---');
const generateFibonacci = createFibonacciGenerator();
console.log('闭包生成器前10项:');
for (let i = 0; i < 10; i++) {
  console.log(generateFibonacci());
}

const fibGen = fibonacciGenerator();
console.log('\nES6生成器前10项:');
for (let i = 0; i < 10; i++) {
  console.log(fibGen.next().value);
}

总结

JavaScript 实现斐波那契数列有多种方法,每种方法都有其适用场景:

  1. 学习递归:基础递归实现(但不要用于生产环境)
  2. 一般用途:迭代法或备忘录递归
  3. 高性能需求:矩阵幂方法
  4. 数列生成:生成器函数或闭包实现
  5. 数学探索:Binet公式实现

在实际开发中,迭代法通常是最佳选择,因为它具有优秀的性能(O(n)时间,O(1)空间)和代码简洁性。对于需要多次计算不同斐波那契数的场景,备忘录递归可以进一步提高效率。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北辰alk

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值