Bootstrap

动态规划--爬楼梯问题

1.问题分析

-- 如果想要到达第n阶台阶

-- 倒数一步有两种走法:从第n-1台阶跳上来或从第n-2台阶跳上来

-- 那么到n-1阶台阶的走法有f(n-1)个,到n-2阶台阶的走法有f(n-2)个

-- 两种走法的种数相加就是n阶台阶的情况下的所有种数

-- 即f(n)=f(n-1)+f(n-2)

2.代码(递归解法)

def f(n):
    if n==1:      # 递归结束条件
        return 1
    elif n==2:    # 递归结束条件
        return 2
    else:
        return f(n-1)+f(n-2)
print(f(20))
# 10946

 

时间复杂度:因为在递归过程中,会有大量的重复计算。例如,计算 f(n) 时会计算 f(n - 1) 和 f(n - 2),而计算 f(n - 1) 时又会计算 f(n - 2) 和 f(n - 3),其中 f(n - 2) 就被重复计算了。随着 n 的增大,重复计算的数量呈指数级增长。

3.代码优化

# 以爬5阶台阶为例
def f(n,m={}):
    # 建立一个空字典m

    if n<=2:
        return n
    if n not in m:
        m[n]=f(n-1,m)+f(n-2,m)
        # 如果数值不在字典中,将递归后的值保存到字典中
        # 作用:下次直接调用结果,不用重复计算

    return m[n]
print(f(5))
代码执行过程
调用 f(5) 时,5 不在字典 m 中,所以需要计算 f(4, m) + f(3, m)。
 计算 f(4, m) 时,4 也不在字典 m 中,需要计算 f(3, m) + f(2, m)。
 计算 f(3, m) 时,3 不在字典 m 中,需要计算 f(2, m) + f(1, m)。
  计算 f(2, m),由于 n <= 2,直接返回 2。
  计算 f(1, m),由于 n <= 2,直接返回 1。
所以 f(3, m) 为 2 + 1 = 3,并将 m[3] = 3 存入字典。
此时 f(4, m) 为 3 + 2 = 5,并将 m[4] = 5 存入字典。
计算 f(3, m) 时,由于之前已经计算过并存入字典,直接从字典中获取值 3。
最终 f(5) 为 5 + 3 = 8,并将 m[5] = 8 存入字典,最后打印输出 8。
def f(n,m={}):
    if n<=2:
        return n
    if n not in m:
        m[n]=f(n-1,m)+f(n-2,m)
        print(m)
    return m[n]
print(f(5))
# 输出
# {3: 3}
# {3: 3, 4: 5}
# {3: 3, 4: 5, 5: 8}
# 8

4.动态规划解法

随着台阶数的增加,走法数也不断增加,数值增长情况也斐波那契数列相同

斐波那契数列,又称为兔子数列,1,1,2,3,5,8,13,21.....下一个数等于前两个数之和

5.代码

# 动态规划
def f(n):
    if n<=2:
        return n
    a=1
    b=2
    sum=0
    for i in range(2,n):
        sum=a+b
        a=b
        b=sum
    return sum
print(f(5))

#第一次循环:sum=a+b=3
#           a=b=2
#           b=sum=3
#第二次循环:sum=a+b=5
#           a=b=3
#           b=sum=5
#第三次循环:sum=a+b=8

#即a和b依次代表前两个值,sum为前两个值之和

6.拓展:斐波那契数列

# 斐波那契数列,又称为兔子数列,1,1,2,3,5,8,13,21.....
# 下一个数等于前两个数之和
n=20
list=[1,1]
for i in range(2,n+1):
    a=list[i-2]
    b=list[i-1]
    list.append(a+b)
print(list)

#   [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,
#    377, 610, 987, 1597, 2584, 4181, 6765, 10946]
# 列表中下标为20的值,即为爬20个台阶的方法

;