1.python的简单实现
菲波拉契数的python代码的简单实现:1 ,1,2,3,5,8,13,21,34...
可以看出,后面的数是前两个数的和。
# 菲波拉契数
# 委递归计算
# 时间复杂度为:2^n
# n从1开始
def fib(n):
if n < 3:
return 1
return fib(n-2) + fib(n-1)
print(fib(40))
菲波拉契数的时间复杂度的计算
我们可以看出,菲波拉契数的时间复杂度可以用下面的公式表示:
T(n) = T(n-1) + T(n+1)
我们可以先简单的写一个求第10个的数的递归过程:
我们先将其画到递归结束的程度,而我们每层的每一个f(n)都有一个加法的操作,所以每层的f(n)都需要一个时间复杂度(这样写好理解一些),所以:
所以我们求n个数时:O(),所以最终的复杂度是O().
2.空间复杂度计算(递归的空间复杂度计算)
所谓的空间复杂度,就是用于保存变量的空间。如:
def add():
a,b = 0,0
for i in range(100):
for j in range(100):
a += i
b += j
s = a + b
return s
上面的代码的空间复杂度为O(1),运行后,一共需要4个单位的空间复杂度 ,因为一共保存了4个变量。
上面的非常好看出来,那么递归代码的空间复杂度怎么计算呢?
比如:上面的菲波拉契数的空间复杂度计算?
先说说上下文切换吧,什么叫上下文的切换呢?在操作系统里面,在函数f1中,调用了f2,在f2中我们调用了f3,这种函数与函数的每次的调用关系,我们称之为上下文切换。而每一次的上下文切换,我们都需要有一个内存空间的使用,内存空间的使用有两种方法:
- 通过变量的方式使用
- 上下文切换的操作,也会占内存空间
上面两种的方法占用的内存空间是不一样的。 好了,我们回到空间复杂度的计算上:
其实递归的内存占用可以看作一个栈,进行一次上下文切换就进一次占,而到达递归的结束条件呢,则开始依次出栈,而这个时候,也是这个递归函数此次运行时所占的多少单位的空间复杂度了。
就好像我们计算f(8)=f(6)+f(7),这个时候栈中就有了3个单位的空间复杂度了,分别是f(8),f(6),f(7)。。。
依次写下去,你会发现菲波拉契数的空间复杂度其实是O(n)。
注:记得出栈,出栈后,空间自然释放,不是一共使用了多少空间,而是最大使用了多少空间。
下面我们对菲波拉契数的代码进行改进:
# 菲波拉契数
import time
# 委递归计算
# 时间复杂度为:2^n
def fib_1(n):
if n < 3:
return 1
return fib_1(n-2) + fib_1(n-1)
# 迭代法
def fib_2(n):
f1 = 1
f2 = 1
for i in range(3, n+1):
f3 = f1+f2
f1, f2 = f2, f3
return f3
statr_1 = time.time()
print(fib_1(40))
end_1 = time.time()
statr_2 = time.time()
print(fib_2(40))
end_2 = time.time()
print(f"fib_1: {end_1-statr_1}\nfib_2: {end_2-statr_2}")
通过对运行时间的比较,很容易看出:在30左右的时候还没有太大的区别,但n越大,fib_2的速度是更快的。
优化后:
- 时间复杂度:O(n)
- 空间复杂度:O(1)
可以说是有了本质的一个区别。
还可以套公式:使得时间复杂度为O(1)....
本文是学习时间-空间复杂度的笔记,如有错误,望指教。