Python 不自己试试,还真猜不出递归函数的时间复杂度!

简介: Python 不自己试试,还真猜不出递归函数的时间复杂度!

如题,以斐波那契数列为例,写以下三种递归算法进行测试:

>>> def F1(n):
  if n<3: return 1
  return F1(n-1)+F1(n-2)
>>> def F2(n,n2=1,n1=1):
  if n<3: return 1
  if n==3: return n2+n1
  return F2(n-1,n1+n2,n2)
>>> def F3(n:int)->int:
  if n<3: return 1
  t=n//2
  if n%2: return F3(t)**2+F3(t+1)**2
  return F3(t)*(F3(t+1)+F3(t-1))
>>> for i in range(1,16):i,F1(i),F2(i),F3(i),F1(i)==F2(i)==F3(i)
(1, 1, 1, 1, True)
(2, 1, 1, 1, True)
(3, 2, 2, 2, True)
(4, 3, 3, 3, True)
(5, 5, 5, 5, True)
(6, 8, 8, 8, True)
(7, 13, 13, 13, True)
(8, 21, 21, 21, True)
(9, 34, 34, 34, True)
(10, 55, 55, 55, True)
(11, 89, 89, 89, True)
(12, 144, 144, 144, True)
(13, 233, 233, 233, True)
(14, 377, 377, 377, True)
(15, 610, 610, 610, True)
>>> 



F1 函数测试:

增加一个全局变量count用于计算调用次数。

>>> def F1(n):
  global count
  count += 1
  if n<3: return 1
  return F1(n-1)+F1(n-2)
>>> for i in range(1,16):
  count=0; F1(i),count
(1, 1)
(1, 1)
(2, 3)
(3, 5)
(5, 9)
(8, 15)
(13, 25)
(21, 41)
(34, 67)
(55, 109)
(89, 177)
(144, 287)
(233, 465)
(377, 753)
(610, 1219)
>>> 
>>> count=0; F1(20),count
(6765, 13529)
>>> count=0; F1(25),count
(75025, 150049)
>>> count=0; F1(30),count
(832040, 1664079)
>>> 6765*2,75025*2,832040*2
(13530, 150050, 1664080)
>>> 6765*2-1,75025*2-1,832040*2-1
(13529, 150049, 1664079)
>>> 


结果真的有点出乎意料,F(n)的运行次数是 2*F(n)-1。这么巨大的数字,怪不得此函数算 F(40) 时电脑基本上就动不了,要知道 2*F(40)-1 = 204668309,两亿次的调用计算量可想而知,不只是两亿次的整数运算。


所以这个函数的时间复杂度应在立方级O(n³)和指数级O(2ⁿ)之间吧:


20210911204255127.png



F2 函数测试:

>>> def F2(n,n2=1,n1=1):
  global count
  count += 1
  if n<3: return 1
  if n==3: return n2+n1
  return F2(n-1,n1+n2,n2)
>>> for i in range(1,16):
  count=0; F2(i),count
(1, 1)
(1, 1)
(2, 1)
(3, 2)
(5, 3)
(8, 4)
(13, 5)
(21, 6)
(34, 7)
(55, 8)
(89, 9)
(144, 10)
(233, 11)
(377, 12)
(610, 13)
>>> 
>>> count=0; F2(100),count
(354224848179261915075, 98)
>>> count=0; F2(1000),count
(4346655768693745643568852767504062580256466051737178040248172\
90895365554179490518904038798400792551692959225930803226347752\
09689623239873322471161642996440906533187938298969649928516003\
704476137795166849228875, 998)
>>> 

明显,经过尾递归优化后的时间复杂度为线性级O(n)。但是,它计算到 F(1027) 时就报错“超出了最大递归深度”,估计算法的空间复杂度已大到“内存占用”已到了堆栈限制上限了。

 



F3 函数测试:

>>> def F3(n:int)->int:
  global count
  count += 1
  if n<2: return n
  if n==2: return 1
  t=n//2
  if n%2: return F3(t)**2+F3(t+1)**2
  return F3(t)*(F3(t+1)+F3(t-1))
>>> count=0; F3(100),count
(354224848179261915075, 462)
>>> count=0; F3(101),count
(573147844013817084101, 344)
>>> count=0; F3(200),count
(280571172992510140037611932413038677189525, 1131)
>>> count=0; F3(201),count
(453973694165307953197296969697410619233826, 807)
>>> 


这个函数的时间复杂应该接近线性对数级 O(nlogn) 的,并且当n是奇数时比偶数时计算的快。



函数的时间测试

>>> def Fib(n):
    if n<3: return 1
    if n%2: return Fib(n//2)**2+Fib(n//2+1)**2
    return Fib(n//2)*(Fib(n//2+1)+Fib(n//2-1))
>>> from time import time
>>> t=time();n=Fib(2000000);print(time()-t)
111.7971076965332



斐波那契数列的递归改进


综合以上几个函数的经验作以下改进,改进后计算第500万项只需80秒,比不作改进的计算200万项还要快半分钟。

>>> def Fib(n:int,n1=34,n2=55):
    if n<11: return [0,1,1,2,3,5,8,13,21,34,55][n]
    if n==11: return n1+n2
    if n<1001:return Fib(n-1,n2,n1+n2)
    t=n//2
    if n%2: return Fib(t)**2+Fib(t+1)**2
    return Fib(t)*(Fib(t+1)+Fib(t-1))
>>> from time import time
>>> t=time();n=Fib(1000000);print(time()-t)
7.492823362350464
>>> t=time();n=Fib(2000000);print(time()-t)
18.6656014919281
>>> t=time();n=Fib(3000000);print(time()-t)
35.3787145614624
>>> t=time();n=Fib(4000000);print(time()-t)
46.64482545852661
>>> t=time();n=Fib(5000000);print(time()-t)
80.72290563583374
>>> 


--All done!

目录
相关文章
|
1月前
|
算法 Python
Python 数据结构和算法:解释什么是 Big O 表示法?举例说明几种常见的时间复杂度。
Python 数据结构和算法:解释什么是 Big O 表示法?举例说明几种常见的时间复杂度。
|
算法 Python 容器
Python常见操作的时间复杂度
本文整理了Python中常见数据结构操作的时间复杂度,旨在帮助大家了解Python操作的性能,协助运行更快的代码。
180 0
Python常见操作的时间复杂度
|
7月前
|
Python
33 python - 递归函数
33 python - 递归函数
23 0
|
8月前
|
机器学习/深度学习 编译器 Python
Python-递归函数-L
Python-递归函数-L
|
10月前
|
机器学习/深度学习 Python
【从零学习python 】30.深入理解递归函数和匿名函数
【从零学习python 】30.深入理解递归函数和匿名函数
37 0
|
机器学习/深度学习 算法 C++
浅析算法的时间复杂度和空间复杂度 (C++/python双语实例)
浅析算法的时间复杂度和空间复杂度 (C++/python双语实例)
111 0
|
算法 Python
算法与python:使用高斯消元法计算行列式的值,并分析时间复杂度
算法与python:使用高斯消元法计算行列式的值,并分析时间复杂度
168 0
|
Python
【Python基础之函数:多层语法糖、装饰器和装饰器修复技术及递归函数】
【Python基础之函数:多层语法糖、装饰器和装饰器修复技术及递归函数】
109 0
【Python基础之函数:多层语法糖、装饰器和装饰器修复技术及递归函数】
|
机器学习/深度学习 存储 人工智能
Python 递归函数
它能够把一个大型复杂的问题转化为一个与原问题相似的较小规模的问题来求解,用非常简洁的方法来解决重要问题。就像一个人站在装满镜子的房间中,看到的影像就是递归的结果。以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……程序设计可以让你的工作由几天节约至几个小时,好的算法可能可以让你的程序运行时间从几个小时节约至几秒钟。被计算了无数次,如果我们能在第一次计算出来后就存储下来,以供后面使用,会不会快些?,基例不需要再次递归,它是确定的表达式;
112 0
Python 递归函数
|
机器学习/深度学习