python斐波那契数列之谜_python 斐波那契数列 算法_sherlock31415931的博客-CSDN博客

原文作者:sherlock31415931

原文标题:python 斐波那契数列 算法

发布时间:2021-01-18 15:43:56

斐波那契数,通常用F(n)表示,形成的序列称为 斐波那契数列 。该数列由 0 和 1 开始,后面的每一项数字都是前面两项数字的和。也就是:

F(0) = 0,F(1) = 1

F(n) = F(n - 1) + F(n - 2),其中 n > 1

给你 n ,请计算 F(n) 。

示例 1:

输入:2

输出:1

解释:F(2) = F(1) + F(0) = 1 + 0 = 1

纯递归

递归有两个基本要素:基例以及递归关系式。

基例:F(0) = 0,F(1) = 1,即递归结束的地方

递归关系式:F(n) = F(n-1) + F(n- 2)

def fib(n):

#base case

if n <= 1:

return n

elif n >= 2:

return fib(n-1) + fib(n-2)

然后根据python语法特性,可以缩写为一句话

def fib(n):

return fib(n-1) + fib(n-2) if n >= 1 else n

F(20) = F(19)+F(18)

F(19) = F(18)+F(17)

然后我们发现 F(18) 重复,即冗余结构。这样的运算时间会很大,

节点数 * 每个子问题所需要的时间 = 时间复杂度

显而易见,这种情况下为O(2^N)

纯递归的缺点就在于存在过多冗余结构。

递归加记忆,将指数级时间将为多项式级时间。

我们可以将已经出现计算好的存在一个备忘录里面,这样我们下一次计算之前,先去查一遍表就行,不需要再重新计算。

memo = {}

def fib(n):

#查表

if n in memo:

return memo[n]

#base case:

elif n <= 1:

return n

else:

ans = fib(n-1) + fib(n-2)

memo[n] = ans

return memo[n]

当然,上述的备忘录既可以采用数组,同时也可以用哈希表。

另外,如果追求完美一点,可以将基例存储到备忘录里面,这样可以减少一定程度的空间复杂度。

memo = {0:0,1:1}

这里介绍的是由上而下的方法,当然也可以是由下而上的。虽然比纯递归好,但是时间复杂度依然很高,leetcode上为1500-1600ms左右。

bottem-up-solution

def fib(n):

memo = {0:0,1:1}

for i in range(2,n+1):

memo[i] = memo[i-1] + memo[i-2]

return memo[n]

for循环的左边是因为备忘录里面已经有键0,1了,所以从2开始。然后我们最后要计算的是memo[n],i = n 所以要为n+1

这个的时间复杂度会大大降低,两者看似都是查表,但是由下而上的这种不会重新迭代函数,只会不断计算表。leetcode数据为36ms

算法方面的优化到此为止,但是关于空间复杂度可以更优化一点,

import sys

h = {0:0,1:1}

l = [0,1]

print(sys.getsizeof(h))

print(sys.getsizeof(l))

#232

#72

字典的空间复杂度远大于列表,所以可以更优化一点。单方面来讲,复杂度跟元素大小无关。跟元素个数有关。

def fib(n):

if n == 0:

return 0

memo[1 for i in range(n+1)]

memo[0],memo[1] = 0,1

for i in range(2,n+1):

memo[i] = memo[i-1] + memo[i-2]

return memo[n]

关于第一行生成len = n+1的原因,因为最后返回索引为n,而索引从0开始

而且除了列表生成器的表达,还可以

memo = [1] * (n+1)

之所以提取生成长度为n+1的列表,是为了防止列表索引不够,进行有序的替换。

因为是替换的,所以初始化列表的值是任取的,只要保证基例正确就行。

要小心的是,如果输入0没有开头的if语句是会报错的(for 循环)

还有就是力扣的很多时候判别的不准,大家只要往死里优化,看看几个最优解就行。不必苛求100%,大家可以复制几个100%,有些是运气100%,到你这就不是100%

最后,作为拓展,还有一种滚动变量的方法。

def fib(n):

if n == 0:

return 0

for i in range(n):

a,b = b,a+b

return a

这种方法显然不是最优解,但是值得一说的是,按照一些书中python的代码规范。这种结构可读性不好,建议少用。

大家觉得好的话,可以去github上给我点小星星呦

https://github.com/sherlcok314159/leetcode-python-3/blob/main/fibo.md

Post Views:

8

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值