斐波那契数列/兔子数列/台阶问题/变态台阶问题(关键词:斐波那契数列/兔子数列/台阶问题/变态台阶问题/动态规划)

版权声明:本文为博主原创文章,可以转载,但转载前请联系博主。 https://blog.csdn.net/qq_33528613/article/details/84886988

台阶问题

台阶问题描述

一只青蛙一次可以跳上1级台阶,也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。

斐波那契数列

0, 1, 1, 2, 3, 5, 8, 13......

递归算法:

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

递归算法 - pythonic(不考虑 n 等于 0 的情况):

def fib_recur1(n):
        return n if 0<= n <= 1 else fib_recur2(n-1) + fib_recur2(n-2)

递归算法改进 - 使用备忘录缓存

def fib_recur2(n):
        global cache
        cache = {0:0, 1:1}

        if n not in cache.keys():
                cache[n] = fib_recur2(n-2) + fib_recur2(n-1)

        return cache[n]

递归算法改进 - 使用备忘录缓存 - 使用装饰器

def memo1(func):
        cache = {}
        def wrapper(arg):
                if arg not in cache.keys():
                        cache[arg] = func(arg)
                return cache[arg]
        return wrapper

fib_recur1 = memo1(fib_recur1)

递归算法改进 - 使用备忘录缓存 - 使用装饰器 - 改进装饰器

上面的算法中,用装饰器装饰完函数以后,无法正确地获取到原函数的函数名称和帮助信息:

>>> fib_recur1 = memo1(fib_recur1)
>>> fib_recur1.__name__
'wrapper'

为了获取这些信息,我们需要使用@functool.wraps:

from functools import wraps

def memo2(func):
        cache = {}
        @wraps(func)
        def wrapper(arg):
                if arg not in cache.keys():
                        cache[arg] = func(arg)
                return cache[arg]
        return wrapper

fib_recur1 = memo(fib_recur1)

非递归算法

def fib_iter(n):
        first, second = 0, 1

        for _ in range(n):
                first, second = second, first+second

        return first

图解:

在这里插入图片描述

测试

def test(func):
        lyst = []
        for index in range(0, 8):
                lyst.append(func(index))

        assert lyst == [0,1,1,2,3,5,8,13]
        print(func.__name__, 'pass!')


if __name__ == '__main__':
        funcs = [fib_recur0, fib_recur1, fib_recur2, memo1(fib_recur1), \
                memo2(fib_recur1), fib_iter]

        for func in funcs:
                test(func)

结果:

$ python3 t.py
fib_recur0 pass!
fib_recur1 pass!
fib_recur2 pass!
wrapper pass!
fib_recur1 pass!
fib_iter pass!

变态台阶问题

变态台阶问题描述

一只青蛙一次可以跳上 1 级、2 级… n 级台阶。求该青蛙跳上一个 n 级的台阶总共有多少种跳法。

问题分析(数学描述)

将 n 级台阶的跳法记为 f(n)。

n = 1 时,跳法只有 1 种,记为 f(1);
n = 2 时,可以先跳 1 级,然后有 f(1) 种跳法;或者直接跳 2 级,这是 1 种跳法;共计 f(2) = f(1)+1;
n = 3 时,可以先跳 1 级,然后有 f(3-1) 种跳法,即 f(2) 种;或者先跳 2 级,然后有 f(3-2) 种跳法,即 f(1) 种;或者直接跳 3 级;共计 f(3) = f(2) + f(1) + 1 种;

n 级台阶,共有
f(n) = f(n-1) + f(n-2) + … + f(1) + 1 种;
n-1 级台阶,共有
f(n-1) = f(n-2) + … + f(1) + 1 种;

两个等式相减,得到:
f(n) - f(n-1) = f(n-1)


f(n) = 2*f(n-1)

递归算法

def f(n):
        return n if n <= 1 else 2*f(n-1)

递归算法改进 - 使用备忘录缓存

f = memo1(f)
f = memo2(f)

非递归算法(动态规划)

def g(n):
        global d
        d = {0:0, 1:1}

        for i in range(1, n):
                d[i+1] = 2*d[i]

        return d[n]

测试

def test(func, arg):
        lyst = {}
        for i in range(arg+1):
                lyst[i] = func(i)

        assert lyst == {0:0, 1:1, 2:2, 3:4, 4:8, 5:16}

        print('Function ' + func.__name__ + '\'s testcases pass!')


if __name__ == '__main__':
        funcs = [f, g, memo1(f), memo2(f)]

        for func in funcs:
                test(func, arg=5)
        assert f(5) == 16
        assert g(5) == 16

结果:

$ python3 t.py
Function f's testcases pass!
Function g's testcases pass!
Function wrapper's testcases pass!
Function f's testcases pass!

参考文献:

  1. 1 台阶问题/斐波那契
  2. 跳台阶问题 + 变态跳台阶问题 解法(动态规划递归 + 非递归)
  3. 程序员面试100题之二:跳台阶问题(变态跳台阶)

猜你喜欢

转载自blog.csdn.net/qq_33528613/article/details/84886988
今日推荐