假设第1个月有1对初生的兔子,第2个月进入成熟期,第3个月开始生育兔子,而1对成熟的兔子每月会生1对兔子,兔子永不死去……那么,由1对初生的兔子开始,12个月后会有多少对兔子呢?
  兔子数列即斐波那契数列,它的发明者是意大利数学家莱奥纳尔多·斐波那契(Leonardo Fibonacci,1170—1250)。1202年,莱奥纳尔多撰写了《算盘全书》(Liber Abaci),该书是一部较全面的初等数学著作。书中系统地介绍了印度—阿拉伯数码及其演算法则,以及中国的“盈不足术”;此外还引入了负数,并研究了一些简单的一次同余式组。

  根据分析第一、二个月没有兔子新增数量都是1;第三个月成熟期的兔子会生一只兔子,总数2;第四月成熟期的兔子会生兔子,上月生的兔子成熟,总数3;第五月两只成熟的兔子生小兔子,上月生的成熟,总数5;第五月三只成熟的兔子生小兔子,上月生的成熟,总数8;……兔子这样一直繁衍下去……
  由兔子繁殖规律可见,每月新生兔子数都是上上个月的总数。

神奇的兔子数列公式图

Python兔子数列循环结构 python兔子数列for_Python


  根据从兔子繁殖分析出的事件特性,可以用递归算法编写代码实现任意项的数列项推演。

递归代码

def resursive_rabbit(n):
    ''' 递归兔子数列 '''
    if n == 1 or n == 2:
        return 1
    else:
        return resursive_rabbit(n-1) + resursive_rabbit(n-2)

运行效果截图

Python兔子数列循环结构 python兔子数列for_算法_02


Python兔子数列循环结构 python兔子数列for_python_03


  这?效率有点儿低啊😱能不能优化下哩?这主要是在递推和回归在计算和存储上费时费力。那就用列表和循环来试试。😄

代码

def for_rabbit(n):
    ''' for循环优化兔子数列 '''
    fib = [1]*n
    
    for i in range(2, n):
        fib[i] = fib[i-1] + fib[i-2]

    return fib[-1]

运行效果截图

Python兔子数列循环结构 python兔子数列for_Python兔子数列循环结构_04


  这!🧐这么快?👀

只是用一个列表来存储前面的项而已哦。等等,来算个10000项。😎

Python兔子数列循环结构 python兔子数列for_Python兔子数列循环结构_05


  耗时居然远远小于0.1秒。先不定义整个数列长度的列表,就第一、二项,后面的计算出来append到列表中,会不会更快?

代码

def for_rabbit_append(n):
    ''' for+append循环优化兔子数列 '''
    fib = [1, 1]
    
    for i in range(2, n):
        fib.append(fib[i-1] + fib[i-2])

    return fib[-1]

效果截屏图片

Python兔子数列循环结构 python兔子数列for_python_06

  和直接先定义n个元素长度的列表,几无差异(还是计算第10000项)。还可以优化不?只计算第几项,用列表存储整个数列,是不是有点儿浪费?用两个变量暂存上一项、上上项何如?

代码

def for_rabbit_ab(n):
    ''' for + a,b循环优化兔子数列 '''
    a, b = 1, 1

    for i in range(2, n):
        fib_new = b + a
        b, a = fib_new, b

    return b

代码运行效果截屏图片

Python兔子数列循环结构 python兔子数列for_递归_07


  还是讲得第10000项,时间又少了一半!🤗这神奇的兔子数列,还没通项公式哩,用她来试试:

Python兔子数列循环结构 python兔子数列for_Python兔子数列循环结构_08

代码

def formula_rabbit(n):
    ''' 通项公式兔子数列 '''
    from math import sqrt
    a, b = ((1+sqrt(5))/2)**n, ((1-sqrt(5))/2)**n
    return 1/sqrt(5) * ( a - b )

运行效果截图

Python兔子数列循环结构 python兔子数列for_Python_09


计算第10000项数字表示溢出报错截屏图片

Python兔子数列循环结构 python兔子数列for_python_10


  由试炼可见,通项公式不但有浮点数计算误差,还可以算太多的项。速度也比不上列表+循环。所以最好不好用通项公式来整。😜试试用三个优化的循环都计算一遍第10000项:

Python兔子数列循环结构 python兔子数列for_Python兔子数列循环结构_11


耗时还是远远小于0.1秒。😎😎😎


  一般来讲,同一需求都可以设计几种算法。当重复太多,递归费时费力,就要找出一个更优算法。我从优化算法的过程中,真切地体会到算法之美!


回页首



mypycolor完整源码(源码较长,点此跳过源码)

#!/sur/bin/nve python
# coding: utf-8

'''

神奇的兔子数列——裴波拉契数列

filename = rabbit_fib.py

Author:梦幻精灵_cq

date:2022-10-21

'''


from  time import localtime, time


# 打印颜色设置字符。
text = '    假设第1个月有1对初生的兔子,第2个月进入成熟期,第3个月开始生育兔子,而1对成熟的兔子每月会生1对兔子,兔子永不死去……那么,由1对初生的兔子开始,12个月后会有多少对兔子呢?\n    兔子数列即斐波那契数列,它的发明者是意大利数学家莱奥纳尔多·斐波那契(Leonardo Fibonacci,1170—1250)。1202年,莱奥纳尔多撰写了《算盘全书》(Liber Abaci),该书是一部较全面的初等数学著作。书中系统地介绍了印度—阿拉伯数码及其演算法则,以及中国的“盈不足术”;此外还引入了负数,并研究了一些简单的一次同余式组。'
blue = '\033[34m'
red = '\033[91m'
green = '\033[92;5m'
gray = '\033[90m'
offall = '\033[0m'

def resursive_rabbit(n):
    ''' 递归兔子数列 '''
    if n == 1 or n == 2:
        return 1
    else:
        return resursive_rabbit(n-1) + resursive_rabbit(n-2)

def for_rabbit(n):
    ''' for循环优化兔子数列 '''
    fib = [1]*n
    
    for i in range(2, n):
        fib[i] = fib[i-1] + fib[i-2]

    return fib[-1]

def for_rabbit_append(n):
    ''' for+append循环优化兔子数列 '''
    fib = [1, 1]
    
    for i in range(2, n):
        fib.append(fib[i-1] + fib[i-2])

    return fib[-1]

def for_rabbit_ab(n):
    ''' for + a,b循环优化兔子数列 '''
    a, b = 1, 1

    for i in range(2, n):
        fib_new = b + a
        b, a = fib_new, b

    return b
    
def formula_rabbit(n):
    ''' 通项公式兔子数列 '''
    from math import sqrt
    a, b = ((1+sqrt(5))/2)**n, ((1-sqrt(5))/2)**n
    return 1/sqrt(5) * ( a - b )




if __name__ == '__main__':
    print(f"\n{green}{' 神奇的兔子数列 '.center(43, '~')}\n\n{gray}{text}{offall}")

    while True:
        try:
            n = int(input(f"\n{'':>8}第{blue} n {offall}项({blue}如 28{offall}):"))
            break
        except Exception as error:
            print(f"{red}{' 请输入整数! '.center(44, '~')}{offall}", end='\r')

    print(f"\n{gray}{' 机器正在运算…… '.center(44, '~')}{offall}", end='\r')
    t0 = time()

    #print(f"{' '*50}{gray}{resursive_rabbit.__doc__},{offall}第{blue} {n} {offall}项:{green}{resursive_rabbit(n)}\n\n{green}{'~'*50}{offall}")
    print(f"{' '*50}{gray}{for_rabbit.__doc__},{offall}第{blue} {n} {offall}项:{green}{for_rabbit(n)}\n\n{green}{'~'*50}{offall}")
    print(f"{' '*50}{gray}{for_rabbit_append.__doc__},{offall}第{blue} {n} {offall}项:{green}{for_rabbit_append(n)}\n\n{green}{'~'*50}{offall}")
    print(f"{' '*50}{gray}{for_rabbit_ab.__doc__},{offall}第{blue} {n} {offall}项:{green}{for_rabbit_ab(n)}\n\n{green}{'~'*50}{offall}")
    #print(f"{' '*50}{gray}{formula_rabbit.__doc__},{offall}第{blue} {n} {offall}项:{green}{formula_rabbit(n)}\n\n{green}{'~'*50}{offall}")

    print(f"\n{'':>14}程序用时{green}{time()-t0:10.6f}{offall} 秒。")
    y, M, d, h, m, s = localtime()[:6]
    input(f"{'~'*14} {blue}{y}-{M:02}-{d:02}{offall} {green}{h:02}:{m:02}:{s:02}{offall} {'~'*15}")