基础算法(Python)-求第n个斐波那契数

求第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,原理很简单,可以类比自然数的快速求幂方法,所以这里就不多说了。

说明:最后两种解法因为自身都不是很理解就直接加引用了,引用原为链接为原文

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值