一维动态规划-求解斐波那契数列

1.💖利用记忆自顶向下实现斐波那契数💖

1.1 🌸斐波那契数列由以下递归式定义🌸:

f ( n ) = { n , n = 0 , 1 f ( n − 1 ) + f ( n − 2 ) , 其 他 \begin{aligned} f(n)= \begin{cases} n,&n=0,1\\ f(n-1)+f(n-2),&其他 \end{cases} \end{aligned} f(n)={n,f(n1)+f(n2),n=0,1

1.2 当n=5时的斐波那契数列递归树🌲

为了便于理解,将利用时间复杂度 O(2n) 递归算法计算n=5时的 fib(5) 斐波那契数列执行过程表示为一棵递归树(如下图所示)。树中每一个结点表示一次函数调用,不难发现为了计算fib(5),需要深度优先依次遍历树中每一个结点,然而这棵树存在许多重复结点,如2个fib(3)结点和3个fib(2)结点。这些重复的结点在求解过程中都会被重复的展开进行计算,这是导致递归算法求解斐波那契数列效率低下的主要原因。
斐波那契数列递归树

fib5
fib4
fib3
fib3
fib2
fib2
fib1
fib2
fib1
fib1
fib0
fib1
fib0
fib1
fib0

1.3 🌸记忆优化重复递归子问题🌸

既然算法的实现过程存在诸多重复的函数调用,那么为了提高算法执行效率,应该考虑优化这些重复的函数调用。我们采用的方法非常简单,记忆。也就是说,在计算出一个输入参数为n的斐波那契数fib(n)后,就把fib(n)用表的形式存储下来。在函数递归调用前,首先在表中查找函数对应参数的值是否在表中。如果表中没有对应的值,说明该参数对应的函数还未被调用,那么就调用该参数对应的递归函数;否则,说明该参数的斐波那契数已经计算出来,这时就不用调用该参数对应的递归函数,而是直接将表中存储的斐波那契数返回即可。

1.4 🌸Python自顶向下实现斐波那契数🌸

代码1.4

def fib_top_bottom(nth):
    """
    自顶向下求解斐波那契数列第nth项
    :param nth: 第n项
    :return: the nth item of fibonacci sequence
    """
    if nth in memo:  # 判断第n项是否已经求出,若是则直接返回
        return memo[nth]
    else:
        if nth <= 2:  # boundary conditions
            fib = 1
        else:
            fib = fib_top_bottom(nth - 1) + fib_top_bottom(nth - 2)
        memo[nth] = fib
    return memo[nth]

2.🌹自底向上实现斐波那契数🌹

2.1 🎆自底向上实现递归🎆

除了利用记忆实现递归外,还可以用自底向上的方法来实现递归,如代码2.3所示。代码直接采用循环来代替递归函数调用。fib(0)和fib(1)是边界条件,有了它们就可以求出fib(2)。有了fib(1)和fib(2),则可以求fib(3)。因此,索引i从2依次递增到n,根据递归式仅仅使用循环,而非递归函数实现求解斐波那契数。
代码2.3的实现可看作如图2.2所示的执行过程。图中每一结点代表一个参数对应的斐波那契数。任意一个结点求值所需要的信息,都是已经求出的结点值。也就是说,当前结点的值只与该结点左边的结点有关,与该结点右边结点无关,而当前结点左边结点的值均已经算出。具体而言,如果要求fib(5)这个结点的值,需要知道这个结点左边结点的值,而该结点左边结点的值在求结点fib(5)之前便已经得到。

2.2 🎇斐波那契数列有向无环图🎇

如果将代码1.4看作是自顶向下的求解斐波那契数,那么代码2.3就是自底向上求解斐波那契数。之所以称为自底向上,是因为在求解fib(n)的值时,我们从fib(0),fib(1),fib(2)开始直到fib(n)。自底向上的实现递归,总是利用已知的信息去求未知的信息,这相当于对图2.2进行拓扑排序后得到的结点顺序。

图2.2 求解斐波那契数列的有向无环图

拓扑排序
fib(1) fib(2) fib(3) fib(4) fib(5) - - -

2.3 🎉Python自底向上实现斐波那契数🎉

代码2.3

# 定义记忆字典
memo = {0: 0}


def fib_bottom_top(nth):
    """
    自底向上求解斐波那契数列第nth项
    :param nth: 第n项
    :return: the nth item of fibonacci sequence
    """
    fib = {0: 0}
    for i in range(1, nth + 1):
        if i <= 2:  # boundary conditions
            fib_i = 1
        else:
            fib_i = fib[i - 1] + fib[i - 2]
        fib[i] = fib_i
    return fib[nth]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值