计算nᵗʰ斐波那契数的不同方法

269 阅读7分钟

在这篇文章中,我们将实现不同的算法来计算第n个斐波那契数。

目录

斐波那契数的介绍

在数学领域,斐波那契数(表示为Fn)是一个序列,其中每个数字都是前面两个数字之和。
这个序列被称为斐波那契数列。它通常从0和1开始,尽管我们可以从1和1或者1和2开始。从0和1开始,序列中接下来的几个值是。

0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, ...

斐波那契数列的递归公式是:-

Fn = Fn-1 + Fn-2

在OpenGenus的这篇文章中,我们将使用不同的方法来实现一种算法,以计算上述斐波那契数列中从0、1开始的第n个数字。

使用循环

我们可以使用循环来寻找第n个斐波那契数,方法是将前两个数字初始化为变量,并从3到n进行循环,其中每项是前两项的总和。

实现该算法

我们将定义一个接受整数n的函数。在函数内部,我们初始化两个变量a=0和b=1,然后我们将检查n=1或n=2的情况,如果n=1则返回a,如果n=2则返回b。如果n不是1或2,则使用从3到n的for循环,我们将计算出下一个斐波那契数,并将其存储起来,将a和b增加到斐波那契序列的下一个值。当循环结束时,第二个变量b将存储或指向第n个数值,现在我们只需返回b。

使用循环计算第n个斐波那契数的函数的Python代码

def Fib(n):
    a = 0
    b = 1
    if n == 1:
        return a
    elif n == 2:
        return b
    else:
        for i in range(3,n+1):
            c = a + b
            a = b
            b = c
        return b
    
n = 5
print(Fib(n))

输出。
3

在上面的算法中,函数 Fib 接受一个整数值 n 作为参数。如果n是1,它返回第一个初始化为0的数字a,如果n是2,它返回第二个1的数字b,否则,它遍历3到n(含),并通过添加a和b计算下一个项,使a和b成为第1次迭代的前两个项。该算法循环计算从1到n的每个数字,因此,该算法的时间复杂度为O(n)。由于该算法使用常数空间,因此该算法的空间复杂度为O(1)。

使用递归

我们可以通过使用斐波那契数列的递归公式Fn = Fn-1 + Fn-2,来使用递归来找到第n个斐波那契数。

实现该算法
我们将定义一个函数,该函数接收一个整数值n。在主体内部,我们将检查n=1或n=2的基本情况,如果n=1则返回0,如果n=2则返回1。如果n既不是1也不是2,那么使用递归方法,我们将通过两次调用函数来返回前两个项的和,一次是参数n-1,另一次是参数n-2。

使用递归计算第n个斐波那契数的Python函数代码

def Fib(n):
    if n == 1 :
        return 0
    if n == 2:
        return 1
    else:
        return Fib(n-1) + Fib(n-2)

n = 6
print(Fib(n))

输出:输出: 5

在上述算法中,函数 Fib 将一个整数值 n 作为参数,如果 n 是 1 则返回 0,如果 n 是 2 则返回 1,否则返回递归句 Fib(n-1) + F(n-2)。这个函数递归计算每一个条款,直到遇到一个基本情况,即n=1,n=2。因此,这也有一个线性时间复杂度,O(n)。这个算法在每一步都对前两个值运行两次函数。

因此,这个函数的时间复杂度是O(2n)。如果我们考虑到函数调用栈的大小,这个算法的空间复杂度为O(n),否则它的空间复杂度为O(1)。

使用记忆化

这种方法类似于递归方法,但在每个递归步骤中存储数值,以尽量减少重复计算相同参数的函数。

实现该算法

我们将定义一个函数,它需要一个整数值n和一个默认参数memoize来存储基本条件。在正文中,我们将检查n是否在memoize中,如果memoize包含n,那么将返回键n的值,否则将使用递归方法计算第n个键的值,并从memoize返回第n个键的值。

使用动态编程的memoization技术计算第n个斐波那契数的函数的Python代码

def Fib(n, memoize={1: 0, 2: 1}):
    if n in memoize:
		return memoize[n]
	else:
		memoize[n] = Fib(n-1, memoize) + Fib(n-2, memoize)
		return memoize[n]

n = 5
print(Fib(n))

输出:输出: 3

在上面的算法中,函数 Fib 需要一个整数 n 作为参数,以及一个默认的名为 memoize 的字典类型的参数,其中包含基数情况。在函数的主体中,我们检查整数n是否在memoize中,如果是,则返回映射到memoize中键n的值,否则,使用递归公式计算值,并将映射到键n的值存储在字典中,以便以后在递归步骤中使用,并返回计算的值。

这种算法减少了许多步骤,并以线性时间复杂度O(n)运行,与递归方法相比好得多。由于该算法存储了所有小于等于n的斐波那契数,所以它的空间复杂度为O(n)。

使用数学方法

我们可以使用数学方法来寻找第n个斐波那契数,方法是使用常数系数的二阶线性同质递归关系的概念。这里我们将使用Binet的斐波纳契数公式。

推导出Binet的公式

斐波那契递推关系Fn = Fn-1 + Fn-2的特征多项式是:

x2 = x + 1

特征方程的解是:

ϕ = (1+√5)/2
ψ = (1-√5)/2

所以斐波那契数列的封闭公式必须是这样的:

fn = ujn + vψn

现在我们使用递归的边界条件,即f0=0和f1=1,这意味着我们必须求解系统

0 = uj0 + vψ0
1 = uj1 + vψ1

第一个方程简化为u = -v,将其代入第二个方程,就可以得到:

1 = u((1+√5)/2)- u((1-√5)/2)
u = 1/√5
v = -1/√5

因此,第n个斐波那契公式为:

Fn = ( ϕn-1 - ψn-1 ) / √5

实现该算法

我们将定义一个接受整数值n的函数。在函数体中,我们将存储常数(1+√5)/2和(1-√5)/2,用于我们的公式表达。然后该函数将返回Binet公式的底线。在这里,我们使用底线来获得所需的结果,因为Binet公式返回的是近似值。

使用数学方法计算第n个斐波那契数的Python函数代码

def Fib(n):
	phi1 = (1 - 5 ** 0.5) / 2
	phi2 = (1 + 5 ** 0.5) / 2
	return (phi2**(n-1) - phi1**(n-1)) // (5**0.5)

n = 5
print(Fib(n))

输出: 3

在这个算法中,我们把递推关系Fn = Fn-1 + Fn-2的特征方程的解作为phi1和phi2。然后我们确定第n个斐波那契数的一般公式,即取phi1和phi2的(n-1)次幂之差的底限值,以及5的平方根。

公式如下:

Floor ( ( phi2n-1 - phi1n-1 ) / (50.5)
注意,这里我们使用了整数除法,Pythonic符号,这与计算除法后得到的数值的下限相似。

这个算法需要O( log(n))时间,因为指数化步骤需要log(n)时间。由于该算法只存储了两个常数phi1和phi2,因此其空间复杂度为O(1)。

通过OpenGenus的这篇文章,你一定对计算nᵗʰ斐波那契数的不同方法有了完整的了解。