斐波那契数列实现的几种写法---分析递归、迭代器、生成器、暴力解法的优劣

前面有篇文章,描述了斐波那契数列,如何用迭代器或生成器来生成。CSDNicon-default.png?t=O83Ahttps://mp.csdn.net/mp_blog/creation/editor/130422623

实际上,对于这种简单问题,这种实现是为了说明迭代器、生成器的基本使用方法。如果考虑到在编程中就事论事地解决问题。我们还有递归直接推算等方法。

#### 斐波那契数列的多种实现
import time

# 1, 使用Python生成器来实现
def fibonacci_generator(n):
    a, b = 1, 1
    for _ in range(n):
        yield a
        a, b = b, a+b

# 2, 使用暴力解法来实现
def fibonacci_noskill(n):
    if n <= 2:
        return None
    result = []
    result.append(1)
    result.append(1)
    for i in range(2,n):
        result.append(result[i-1] + result[i-2])
    return result

#  3, 使用辅助递归函数来实现
def fibonacci_recursion(n):
    result = []
    result.append(1)
    result.append(1)
    def helper(m, num):
        if m == 0:
            return
        num.append(num[-2]+num[-1])
        m -= 1
        helper(m, num)
    helper(n-2, result)
    return result

# 4, 用迭代器来实现
class Solution(object):
    def __init__(self):
        self.a, self.b = 1, 1
    def __iter__(self):
        return self
    def __next__(self):
        self.a, self.b = self.b, self.a + self.b
        return self.a

# 5, 另一种递归
def fib(n):
    if n <= 0:
        return 0
    elif n == 1 or n == 2:
        return 1
    else:
        return fib(n-1) + fib(n-2)

if __name__ == "__main__":

    start_time = time.perf_counter()
    print(list(fibonacci_generator(40)))
    end_time = time.perf_counter()
    execution_time = (end_time - start_time) * 1000
    print(f"使用生成器,执行时间为: {execution_time} 毫秒\n\n")

    start_time = time.perf_counter()
    print(fibonacci_noskill(40))
    end_time = time.perf_counter()
    execution_time = (end_time - start_time) * 1000
    print(f"使用暴力解法,执行时间为: {execution_time} 毫秒\n\n")

    start_time = time.perf_counter()
    print(fibonacci_recursion(40))
    end_time = time.perf_counter()
    execution_time = (end_time - start_time) * 1000
    print(f"使用一次递归(函数内建一个辅助递归函数),执行时间为: {execution_time} 毫秒\n\n")



    s = Solution()
    result = []
    result.append(1)
    start_time = time.perf_counter()
    for e in s:
        if e > 102334156:
            break
        else:
            result.append(e)
    print(result)
    end_time = time.perf_counter()
    execution_time = (end_time - start_time) * 1000
    print(f"使用迭代器执行时间为: {execution_time} 毫秒\n\n")



    start_time = time.perf_counter()
    print(fib(40))
    end_time = time.perf_counter()
    execution_time = (end_time - start_time) * 1000
    print(f"使用全递归,执行时间为: {execution_time} 毫秒\n\n")



结果是:

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]
使用生成器,执行时间为: 0.09039998985826969 毫秒


[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]
使用暴力解法,执行时间为: 0.05389994475990534 毫秒


[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]
使用一次递归(函数内建一个辅助递归函数),执行时间为: 0.058999983593821526 毫秒


[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]
,使用迭代器执行时间为: 0.03929995000362396 毫秒


102334155
使用全递归,执行时间为: 44740.48379994929 毫秒

分析_1:

运行是在一台比较老的Thinkpad PC上。

结果都是n为40的斐波那契数列的生成, [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155]。

可以看到,迭代器最快,暴力解法次之,生成器可能因为每次的yield等待稍慢。内建递归实现的一次递归,效率尚可。以上都在耗时0.1 毫秒的量级以内。但另外一种全递归方式,引起指数级别的压栈操作,用时高达44秒,比最快的迭代器,用时1,000,000倍。

实验2:

把n 设为100,结果为

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738, 19740274219868223167, 31940434634990099905, 51680708854858323072, 83621143489848422977, 135301852344706746049, 218922995834555169026, 354224848179261915075]
使用生成器,执行时间为: 0.0798000255599618 毫秒


[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738, 19740274219868223167, 31940434634990099905, 51680708854858323072, 83621143489848422977, 135301852344706746049, 218922995834555169026, 354224848179261915075]
使用暴力解法,执行时间为: 0.05619996227324009 毫秒


[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738, 19740274219868223167, 31940434634990099905, 51680708854858323072, 83621143489848422977, 135301852344706746049, 218922995834555169026, 354224848179261915075]
使用一次递归(函数内建一个辅助递归函数),执行时间为: 0.06530003156512976 毫秒


[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738, 19740274219868223167, 31940434634990099905, 51680708854858323072, 83621143489848422977, 135301852344706746049, 218922995834555169026, 354224848179261915075]
使用迭代器执行时间为: 4.075199947692454 毫秒


生成 这样的数列,[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711, 28657, 46368, 75025, 121393, 196418, 317811, 514229, 832040, 1346269, 2178309, 3524578, 5702887, 9227465, 14930352, 24157817, 39088169, 63245986, 102334155, 165580141, 267914296, 433494437, 701408733, 1134903170, 1836311903, 2971215073, 4807526976, 7778742049, 12586269025, 20365011074, 32951280099, 53316291173, 86267571272, 139583862445, 225851433717, 365435296162, 591286729879, 956722026041, 1548008755920, 2504730781961, 4052739537881, 6557470319842, 10610209857723, 17167680177565, 27777890035288, 44945570212853, 72723460248141, 117669030460994, 190392490709135, 308061521170129, 498454011879264, 806515533049393, 1304969544928657, 2111485077978050, 3416454622906707, 5527939700884757, 8944394323791464, 14472334024676221, 23416728348467685, 37889062373143906, 61305790721611591, 99194853094755497, 160500643816367088, 259695496911122585, 420196140727489673, 679891637638612258, 1100087778366101931, 1779979416004714189, 2880067194370816120, 4660046610375530309, 7540113804746346429, 12200160415121876738, 19740274219868223167, 31940434634990099905, 51680708854858323072, 83621143489848422977, 135301852344706746049, 218922995834555169026, 354224848179261915075]

分析_2:

这次看到,n从 40 -> 100,其他方法时间量级变化不大,但迭代器明显变慢。从0.0x 个毫秒,到几个毫秒的量级。这是什么原因呢?同时,指数级别的递归调用的危害作用更加明显,通过启停这个Python程序,可以看到CPU占到100%。

总结:

所以从效率而言,如果n 很大,递归方法最要谨慎,尤其是可能递归函数中调用超过一次的递归情况,因为占用大量栈空间,一次算完,可能引起指数增长的递归调用。其次是直接暴力解法,暴力解法并不是不可以用,具体情况具体分析。而生成器、迭代器是基于单值, 从时间、空间角度考虑。可能更优,其中生成器是基于函数的单值生成,更轻量快速;而迭代器,在处理复杂数据结构的时候,可能优势就显现出来了。

BTW,关于递归,写过一篇小文,可以参考。 CSDNicon-default.png?t=O83Ahttps://mp.csdn.net/mp_blog/creation/editor/142356275

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值