求第n个斐波那契数
斐波那契数列(Fibonacci sequence),又称黄金分割数列,因数学家莱昂纳多·斐波那契(Leonardo Fibonacci)以兔子繁殖为例子而引入,故又称“兔子数列”,其数值为:1、1、2、3、5、8、13、21、34……在数学上,这一数列以如下递推的方法定z义:F(1)=1,F(2)=1,F(n)=F(n - 1)+F(n - 2)(n ≥ 2,n ∈ N*)。
题目:
给出一个整数n,输出第n个斐波那契数。
输入:
输入一个整数n,代表斐波那契数索引。
输出:
输出一行,包含一个实数,表示索引为n的斐波那契数。
例子:
输入:
5
输出:
5
Python代码
方法一:递归
# 方法一:递归
def fib(n):
if n <= 2:
return 1
else:
return fib(n-2) + fib(n-1)
#递推方法:fib(1)=1,fib(2)=1,fib(n)=fib(n-1)+fib(n-2)(n≥2,n∈N*)。
n = int(input())
print(fib(n))
方法二:使用lambda匿名函数(和递归思想一样,只不过看着更简单)
# 方法二:使用lambda匿名函数
fib = lambda n: 1 if n <= 2 else fib(n-2) + fib(n-1)
n = int(input())
print(fib(n))
方法三:迭代
# 方法三:迭代
def fib(n):
x, y = 0, 1
while n: # 非零即真,非空即真
x, y, n = y, x + y, n-1
return x
n = int(input())
print(fib(n))
方法四:使用迭代器
# 方法四:迭代器生成斐波那契数
from itertools import islice
class Fib:
def __init__(self, n):
self.prev = 0
self.curr = 1
self.index = 0
self.n = n # 记录需要打印斐波那契数的索引
def __iter__(self):
# 重写迭代器函数,使self就具有迭代属性
return self
def __next__(self):
# 重写next函数
if self.index < self.n:
value = self.curr
self.curr += self.prev
self.prev = value
self.index += 1
return value
else:
raise StopIteration
n = int(input())
fib = Fib(n)
# 打印第n位斐波那契数,索引为n-1
# (1)使用islice索引:
print(list(islice(fib,n))[n-1])
# (2)使用判断索引位置:
for index, value in enumerate(fib):
if index == n-1:
print(value)
方法五:使用生成器
# 方法五:生成器生成斐波那契数
from itertools import islice
def fib(n):
x, y = 0, 1
while n:
x, y, n = y, x + y, n - 1
yield x
n = int(input())
# (1)使用islice索引:
print(list(islice(fib(n), n))[n - 1])
# (2)使用判断索引位置:
for index, value in enumerate(fib(n)):
if index == n - 1:
print(value)
方法六:矩阵方法
# 方法六:矩阵求解
from functools import reduce
def fib(n):
def m1(a, b):
m = [[], []]
m[0].append(a[0][0] * b[0][0] + a[0][1] * b[1][0])
m[0].append(a[0][0] * b[0][1] + a[0][1] * b[1][1])
m[1].append(a[1][0] * b[0][0] + a[1][1] * b[1][0])
m[1].append(a[1][0] * b[1][0] + a[1][1] * b[1][1])
return m
def m2(a, b):
m = []
m.append(a[0][0] * b[0][0] + a[0][1] * b[1][0])
m.append(a[1][0] * b[0][0] + a[1][1] * b[1][0])
return m
return m2(reduce(m1, [[[0, 1], [1, 1]] for i in range(n)]), [[0], [1]])[0]
n = int(input())
print(fib(n))
这段代码就不像之前的代码那样清晰了,所以先介绍下原理(需要一点线性代数知识):首先看一下之前的迭代版本的Fibonacci函数,很容易可以发现存在一个变换:y->x, x+y->y。换一个角度,就是[x,y]->[y,x+y]。在这里,我声明一个二元向量[x,y]T,它通过一个变换得到[y,x+y]T,可以很容易得到变换矩阵是[[1,0],[1,1]],也就是说:[[1,0],[1,1]]*[x,y]T=[y,x+y]T。令二元矩阵A=[[1,0],[1,1]],二元向量x=[0,1]T,容易知道Ax的结果就是下一个Fibonacci数值,即:Ax=[fib(1),fib(2)]T,亦有:Ax=[fib(2),fib(3)]T………………以此类推,可以得到:Aⁿx=[fib(n),fib(n-1)]T,也就是说可以通过对二元向量[0,1]T进行n次A变换,从而得到[fib(n),fib(n+1)]T,从而得到fib(n)。在这里我定义了一个二元矩阵的相乘函数m1,以及一个在二元向量上的变换m2,然后利用reduce操作完成一个连乘操作得到Aⁿx,最后得到fib(n)。
方法七:ACM比赛使用的矩阵方法
def fib(n):
lhm = [[0, 1], [1, 1]]
rhm = [[0], [1]]
em = [[1, 0], [0, 1]]
# multiply two matrixes
def matrix_mul(lhm, rhm):
# initialize an empty matrix filled with zero
result = [[0 for i in range(len(rhm[0]))] for j in range(len(rhm))]
# multiply loop
for i in range(len(lhm)):
for j in range(len(rhm[0])):
for k in range(len(rhm)):
result[i][j] += lhm[i][k] * rhm[k][j]
return result
def matrix_square(mat):
return matrix_mul(mat, mat)
# quick transform
def fib_iter(mat, n):
if not n:
return em
elif (n % 2):
return matrix_mul(mat, fib_iter(mat, n - 1))
else:
return matrix_square(fib_iter(mat, n / 2))
return matrix_mul(fib_iter(lhm, n), rhm)[0][0]
n = int(input())
print(fib(n))
看过上一个fib函数就比较容易理解这一个版本了,这个版本同样采用了二元变换的方式求fib(n)。不过区别在于这个版本的复杂度是lgn,而上一个版本则是线性的。
这个版本的不同之处在于,它定义了一个矩阵的快速求幂操作fib_iter,原理很简单,可以类比自然数的快速求幂方法,所以这里就不多说了。
说明:最后两种解法因为自身都不是很理解就直接加引用了,引用原为链接为原文