兔子从出生后第3 个月起每个月都会生一对兔子,小兔子成长到第三个月后每个月又会生一对兔子。初始有一对小兔子,假如兔子都不死,用户输入一个月份数,计算并在一行内输出从1 到n 月每个月的兔子数量。
(Fibonacci sequence)
image.png
1 1 2 3 5 8 13

斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”。
斐波那契数列指的是这样一个数列:
1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 ……
在现代物理、准晶体结构、化学等领域,斐波纳契数列都有直接的应用,为此,美国数学会从 1963 年起出版了以《斐波纳契数列季刊》为名的一份数学杂志,用于专门刊载这方面的研究成果。
例如,树木的生长,由于新生的枝条,往往需要一段“休息”时间,供自身生长,而后才能萌发新枝。所以,一株树苗在一段间隔,例如一年,以后长出一条新枝;第二年新枝“休息”,老枝依旧萌发;此后,老枝与“休息”过一年的枝同时萌发,当年生的新枝则次年“休息”。这样,一株树木各个年份的枝桠数,便构成斐波那契数列。这个规律,就是生物学上著名的“鲁德维格定律”。
另外,观察延龄草、野玫瑰、南美血根草、大波斯菊、金凤花、耧斗菜、百合花、蝴蝶花的花瓣,可以发现它们花瓣数目具有斐波那契数:3、5、8、13、21、…具有13条顺时针旋转和21条逆时针旋转的螺旋的蓟的头部……。
斐波那契数列数学上可以获得通项公式:
fib.png

有趣的是,这样一个完全是自然数的数列,通项公式却是用无理数来表达的。而且当 n 趋向于无穷大时,前一项与后一项的比值越来越逼近黄金分割0.618(或者说后一项与前一项的比值小数部分越来越逼近 0.618),又称黄金分割数列。
fib.png
在计算机中,尤其是在Python中,因会引入浮点数,一般不用这个方法,而广泛使用递推公式实现。

30 斐波那契数列 - 图4
在数学上,斐波那契数列以如下被以递推的方法定义:
F(1)=1,
F(2)=1,
F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)
image.png

  1. n = int(input()) # int函数将input()接收到的字符串转成整数
  2. f1 = 1
  3. f2 = 1 # 设定初始的前2项为1和1
  4. for i in range(n): # 循环n次,输出前n个月的数列
  5. print(f1, end=' ') # 输出当前项,不换行
  6. fn = f1 + f2 # f1 + f2为产生的新项
  7. f1 = f2 # 原来的第二项做为第1项
  8. f2 = fn # 新项做为新的第2项

可以简化一下

  1. n = int(input()) # int函数将input()接收到的字符串转成整数
  2. f1, f2 = 1, 1 # 设定初始的前2项为1和1
  3. for i in range(n): # 循环n次,输出前n个月的数列
  4. print(f1, end=' ') # 输出当前项,不换行
  5. f2 = f1 + f2 # f1 + f2为产生的新项
  6. f1 = f2 - f1 # 新的f2(新)-f1=f1+f2-f1=f2(旧)
  1. n = int(input()) # int函数将input()接收到的字符串转成整数
  2. f1, f2 = 1, 1 # 设定初始的前2项为1和1
  3. for i in range(n): # 循环n次,输出前n个月的数列
  4. print(f1, end=' ') # 输出当前项,不换行
  5. f1, f2 = f2, f1 + f2 # 同步赋值,同时把f1,f2标签贴到原f2和原f1+f2的结果上
  6. # 20
  7. # 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765

可以写成函数

  1. def fib(n):
  2. '''输出斐波那契数的前n项,首项从0开始'''
  3. f1, f2 = 1, 1 # 设定初始的前2项为1和1
  4. for i in range(n): # 循环n次,输出前n个月的数列
  5. print(f1, end=' ') # 输出当前项,不换行
  6. f1, f2 = f2, f1 + f2 # 同步赋值,同时把f1,f2标签贴到原f2和原f1+f2的结果上
  7. if __name__ == '__main__':
  8. n = int(input()) # int函数将input()接收到的字符串转成整数
  9. fib(n) # 调用fib()函数进行计算

可以用列表推导式方便 的获得斐波那契数列,可利用索引方法获取其中任一个数,或用ls[-1]获得最后一个数字。

  1. n = int(input()) # int函数将input()接收到的字符串转成整数
  2. ls = [1, 1] # 设定初始的前2项为1和1
  3. [ls.append(ls[i - 2] + ls[i - 1]) for i in range(2,n)]
  4. print(ls)
  5. # 20
  6. # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]

递归方法实现第n个月的兔子数量

  1. def fib(n):
  2. """输出斐波那契数的第n项,首项从1开始"""
  3. if n <= 2:
  4. v = 1 # 数列前两个数都是1
  5. return v # 返回结果,并结束函数
  6. v = fib(n - 1) + fib(n - 2) # 后一个数是前两个数之和
  7. return v # 返回结果,并结束函数
  8. if __name__ == '__main__':
  9. num = int(input()) # int函数将input()接收到的字符串转成整数
  10. print(fib(num)) # 调用fib()函数进行计算第num月兔子数量

斐波那契数列特性

平方与前后项

从第二项开始,每个偶数项的平方都比前后两项之积多1,每个奇数项的平方都比前后两项之积少1。
如:第二项 1 的平方比它的前一项 1 和它的后一项 2 的积 2 少 1,第三项 2 的平方比它的前一项 1 和它的后一项 3 的积 3 多 1。
[1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]
(注:奇数项和偶数项是指项数的奇偶,而并不是指数列的数字本身的奇偶)

奇数项求和

30 斐波那契数列 - 图6

偶数项求和

30 斐波那契数列 - 图7

  1. ls = [1, 1] # 设定初始的前2项为1和1
  2. [ls.append(ls[i - 2] + ls[i - 1]) for i in range(2,20)]
  3. print(ls)
  4. print(sum(ls[0:20:2])) # 6765 奇数项和
  5. print(sum(ls[1:20:2])) # 10945 = 4181 + 6765 - 1 偶数项和
  6. # [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765]

平方求和

30 斐波那契数列 - 图8

  1. ls = [1, 1] # 设定初始的前2项为1和1
  2. [ls.append(ls[i - 2] + ls[i - 1]) for i in range(2,20)]
  3. print(ls)
  4. print(sum(ls[i] **2 for i in range(20))) # 74049690
  5. print(ls[19] * (ls[19] + ls[18])) # 74049690

高效方法

一个为函数提供缓存功能的装饰器,缓存 maxsize 组传入参数,在下次以相同参数调用时直接返回上一次的结果。用以节约高开销或I/O函数的调用时间。
由于使用了字典存储缓存,所以该函数的固定参数和关键字参数必须是可哈希的。
不同模式的参数可能被视为不同从而产生多个缓存项,例如, f(a=1, b=2) 和 f(b=2, a=1) 因其参数顺序不同,可能会被缓存两次
如果指定了 user_function,它必须是一个可调用对象。 这允许 lru_cache 装饰器被直接应用于一个用户自定义函数,让 maxsize 保持其默认值 128:
如果 maxsize 设为 None,LRU 特性将被禁用且缓存可无限增长。

  1. from functools import lru_cache
  2. @lru_cache(maxsize=None)
  3. def fib(n):
  4. if n < 2:
  5. return n
  6. return fib(n - 1) + fib(n - 2)
  7. if __name__ == '__main__':
  8. for i in range(1000):
  9. print(fib(i))