一文解读斐波那契数列原理及生成器实现

1、斐波那契数列

斐波那契数列(意大利语:Successione di Fibonacci),又译为菲波拿契数列、菲波那西数列、斐氏数列、黄金分割数列。

在数学上,斐波那契数列是以递归的方法来定义:
在这里插入图片描述
用文字来说,就是斐波那契数列由0和1开始,之后的斐波那契数就是由之前的两数相加而得出。首几个斐波那契数是:

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233……(OEIS中的数列A000045)

特别指出:0 不是第一项,而是第零项,第一项是第一项!!

  • 以斐波那契数为边的正方形拼成的近似的黄金矩形(1:1.618)
    在这里插入图片描述
  • 以斐波那契数为半径的圆拼成的近似的黄金螺旋螺旋(1:1.618)
    在这里插入图片描述

2、斐波那契数列之源起

2.1 兔子学说

公元1150年印度数学家Gopala和金月在研究箱子包装对象长宽刚好为1和2的可行方法数目时,首先描述这个数列。在西方,最先研究这个数列的人是比萨的列奥那多(意大利人斐波那契Leonardo Fibonacci),他描述兔子生长的数目时用上了这数列:

  • 第一个月初有一对刚诞生(先得长大两月,第三月才能生)的兔子
  • 第二个月之后(第三个月初)它们可以生育
  • 每月每对可生育的兔子会诞生下一对新兔子
  • 兔子永不死去

如下图,兔子对 的数量 就是斐波那契数列。值得注意的是,假设兔子是永不会死去的,其中带点的兔子代表可以生小兔,反之不能。(兔兔在诞生的第三个月才能生小兔)
兔子对的数量就是斐波那契数列
假设在n月有兔子总共a对,n+1月总共有b对。在n+2月必定总共有a+b对:因为在n+2月的时候,前一月(n+1月)的b对兔子可以存留至第n+2月(在当月属于新诞生的兔子尚不能生育)。而新生育出的兔子对数等于所有在n月就已存在的a对

如下图,斐波纳契数是帕斯卡三角形的每一条红色对角线上数字的和
在这里插入图片描述

2.2 数列表达式

为求得斐波那契数列的一般表达式,可以借助线性代数的方法。高中的初等数学知识也能求出。
在这里插入图片描述

3、斐波那契数列之编程

需求:用三种方法实现斐波那契数列的第1-10项,注意这里不包括第0项。

3.1 方法一:循环实现 Loop

coding:

def fibonacci_loop(n):
    first, second, count, fib = 0, 1, 2, []
    if n == 1:
        fib.append(second)
    else:
        fib.append(second)
        while count <= n:
            tmp = second
            second = first + second
            first = tmp
            fib.append(second)
            # 更新值
            count += 1
    return fib


res_fib = fibonacci_loop(10)
print(res_fib)

输出:

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

3.2 方法二:生成器实现 Genarator

coding:

def fibonacci_genarator(n):
    n1, n2, count = 0, 1, 2
    yield n2
    while count <= n:
        tmp = n2
        n2 = n1 + tmp
        n1 = tmp
        yield n2
        count += 1


res = fibonacci_genarator(10)
print(list(res))

输出:

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

review

def fib_get(n):
    first, second, count = 0, 1, 2
    while count < n+2:
        yield second
        first, second = second, first + second
        count += 1


ret = fib_get(10)
print(list(ret))

输出:

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

3.3 方法三:递归实现 Recursion

coding:

def fibonacci_recursion(n):
    if n == 1 or n == 2:
        return 1
    else:
        return fibonacci_recursion(n-1) + fibonacci_recursion(n-2)


res = [fibonacci_recursion(n) for n in range(1, 11)]
print(res)

输出:

[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]

3.4 上述三种方法耗时PK

需求:分别用循环、生成器、递归来实现第1到第40项的斐波那契数列,对比耗时情况,猜猜看哪个耗时最久?

from functools import wraps
import time


# 计时装饰器
def timmer(func):
    @wraps(func)
    def inner(*args, **kwargs):
        start_time = time.time()
        res = func(*args, **kwargs)
        end_time = time.time()
        print(f'本次{func.__name__}函数执行共耗时{int(end_time-start_time)}s')
        return res
    return inner


@timmer
def fibonacci_loop(n):
    first, second, count, fib = 0, 1, 2, []
    if n == 1:
        fib.append(second)
    else:
        fib.append(second)
        while count <= n:
            tmp = second
            second = first + second
            first = tmp
            fib.append(second)
            # 更新值
            count += 1
    return fib


@timmer
def fibonacci_genarator(n):
    n1, n2, count = 0, 1, 2
    yield n2
    while count <= n:
        tmp = n2
        n2 = n1 + tmp
        n1 = tmp
        yield n2
        count += 1


def fibonacci_recursion(n):
    if n == 1 or n == 2:
        return 1
    else:
        return fibonacci_recursion(n-1) + fibonacci_recursion(n-2)


if __name__ == '__main__':
    # 循环
    res_fib = fibonacci_loop(40)
    print(res_fib)
    # 生成器
    res = fibonacci_genarator(40)
    print(list(res))
    # 递归
    start_time = time.time()
    res = [fibonacci_recursion(n) for n in range(1, 41)]
    print(res)
    end_time = time.time()
    print(f'本次{fibonacci_recursion.__name__}函数执行共耗时{int(end_time-start_time)}s')

如下,递归耗时最久,竟然耗时50秒,循环和生成器耗时不到1秒,所以说递归需慎用~
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Sunny_Future/article/details/108271920