[数据结构学习笔记15] 斐波那契数列(Fibonacci)

Fibonacci 和递归

Fibonacci在递归界有很重要的地位。它能很好的说明递归是怎么方便的解决问题的,也能很好的说明递归也不是万能的,它是有限制的,那么,怎么消除这些限制,我们将用Fibonacci的不同实现理解这些问题。

 

什么是Fibonacci数列

复制代码
fibonacci(0) = 0
fibonacci(1) = 1
fibonacci(2) = 1    // fibonacci(1) + fibonacci(0) 
fibonacci(3) = 2    // fibonacci(2) + fibonacci(1)
fibonacci(4) = 3    // fibonacci(3) + fibonacci(2)
.
.
.
fibonacci(n) = fibonacci(n - 1) + fibonacci(n - 2)
复制代码

这是个典型的递归,自己调用自己,并且有终止条件。

复制代码
function fibonacci(n) {
  if (n == 0) {
       return 0;
   } else if (n == 1) {
       return 1;
   } else {
       return fibonacci(n - 1) + fibonacci(n - 2);
   }
}

console.log('Result is', fibonacci(10));
复制代码

 来看看函数是怎么调用的

input: 3

fibonacci(3) -> return fibonacci(2) + fibonacci(1)

                    -> return fibonacci(1) + fibonacci(0) + return 1

                    -> return 1 + return 0 + return 1

                    -> 2

目前为止,看起来还不错,是我们想要的递归调用,并且似乎还比较简洁。我们继续。

input: 5

fibonacci(5) -> return fibonacci(4) + fibonacci(3)

                    -> return fibonacci(3) + fibonacci(2) + return fibonacci(2) + fibonacci(1)

                    -> return fibonacci(2) + fibonacci(1) + return fibonacci(1) + fibonacci(0) + return fibonacci(1) + fibonacci(0) + return 1

                    -> return fibonacci(1) + fibonacci(0) + return 1 + return 1 + return 0 + return 1 + return 0 + return 1

                    -> return 1 + return 0 + return 1 + return 1 + return 0 + return 1 + return 0 + return 1 

                    -> 5

这里我们开始看到一些重复计算了。随着input越来越大,重复计算会成指数级增长,这对时间和内存都有极大的消耗。统计得出,当input是20的时候,会有21,891次递归函数调用,当input是24的时候,这个数字会达到150,049次,所以这个增长速度是指数级的。(O(2^n))。我们必须要避免这样的情况。

 

在讨论解决方法之前,我们插一下,为什么函数调用是昂贵的?

函数调用,特别是长时间运行的递归调用,可能是会非常消耗时间和内存的:

1. 函数调用需要额外的内存,用来存储当前调用栈里的程序状态,包括所有变量,如果递归调用嵌套很深,可能会消耗大量内存

2. 函数调用会增加处理时间,每次函数调用都要把当前状态放到调用栈,函数返回的时候,又要从栈顶弹出,函数调用越多,这个时间也会越长

3. 无止境的函数递归调用会导致栈溢出

所以能减少函数调用,那就能提高程序运行的时间。

 

解决方法1:带有记忆的递归,就是把函数运行的结果保存起来,后面如果有一样的调用,那就不需要run了,直接拿结果就行。本质上就是减少函数调用。

复制代码
function fibonacci(index, cache=[]) {
  if (cache[index]) {
       return cache[index];
   } else {
       if (index == 0) {
           return 0;
       } else if (index == 1) {
           return 1;
       } else {
           cache[index] = fibonacci(index - 1, cache) + fibonacci(index - 2, cache);
       }
   } 
   return cache[index];
}

console.log(fibonacci(10));
复制代码

这个函数,当input是20时,函数调用从21,891降到37!

解决方法2:用循环的方式,对于fabonacci有几个好处,快,简介明了。

复制代码
function fibonacci(n) {
  if (n == 0) {
       return 0;
   } else if (n == 1) {
       return 1;
   } else {
       let a = 0;
       let b = 1;
       for (let i = 2; i <= n; i++) {
          let c = a + b;
          a = b;
          b = c;
       }
       return b;
   }
}

console.log(fibonacci(10));
复制代码

对比三种方法的速度:

常规递归最差,O(2^n),带记忆的递归其次,循环的方法最快!

 

posted @   Eagle6970  阅读(15)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示