深入理解斐波那契数列的递归实现与优化

38 阅读3分钟

深入理解斐波那契数列的递归实现与优化

斐波那契数列是一个经典的数学序列,其定义为:从 0 和 1 开始,后续每一项都等于前两项之和,即f(0) = 0f(1) = 1f(n) = f(n-1) + f(n-2)。在编程中,实现斐波那契数列的计算是一个常见的练习,而递归是实现方式之一,同时也存在优化的空间。

基础递归实现

最直接的递归实现方式如下:

// 时间复杂度O(2^n)
function fib(n) {
    // 退出条件
    if(n <= 1){
        return n;
    }
    // 函数调用自己 递归
    // 递归的公式
    return fib(n-1) + fib(n-2)
}

console.log(fib(10));

这种实现的思路很清晰,符合斐波那契数列的数学定义。它将大的问题分解为多个类似的小问题,采用自顶向下的方式求解。然而,这种实现存在明显的缺陷:

  • 时间复杂度高:达到了 O (2^n),随着 n 的增大,计算时间会急剧增加
  • 重复计算严重:在递归过程中,大量的中间结果会被重复计算
  • 可能导致栈溢出:依赖函数调用栈来实现递归,当 n 较大时,容易出现栈内存爆栈的情况

利用缓存优化递归

为了解决重复计算的问题,我们可以引入缓存机制,将已经计算过的结果存储起来,当再次需要时直接从缓存中获取,避免重复计算。

全局缓存实现

第一种优化方式是使用全局缓存对象:

const cache = {}; // 用空间换时间
function fib(n) {
    if(n in cache){
        return cache[n];
    }
    if(n <= 1){
        cache[n] = n;
        return n;
    }
    const result = fib(n-1) + fib(n-2);
    cache[n] = result;
    return result;
}
console.log(fib(100));

这里通过全局变量cache存储已经计算的结果,当需要计算fib(n)时,先检查缓存中是否存在,如果存在则直接返回,否则再进行计算并将结果存入缓存。这种方式用空间换取了时间,将时间复杂度优化到了 O (n)。

闭包缓存实现

另一种更优雅的方式是利用闭包来封装缓存,避免全局变量的使用:

// cache 闭合到函数中
const fib = (function() {
    // 闭包 
    // 自由变量
    const cache = {};
    // IIFE
    return function(n) {
        if(n in cache){
            return cache[n];
        }
        if(n <= 1){
            cache[n] = n;
            return n;
        }
        cache[n] = fib(n-1) + fib(n-2);
        return cache[n];
    }
})()

console.log(fib(100));

这里使用了立即执行函数表达式(IIFE),它具有以下特点:

  • 立即执行
  • 非常适合创建闭包
  • 可以返回一个函数

通过这种方式,cache变量被封装在闭包中,不会污染全局作用域,同时又能在多次调用fib函数时保持缓存状态。

总结

递归是实现斐波那契数列的一种直观方式,尤其适合这种可以分解为树形结构的问题。但基础的递归实现存在效率问题,通过引入缓存机制(无论是全局缓存还是闭包缓存),可以有效解决重复计算的问题,大幅提高计算效率。