Python3.6.3
跳台阶一步两步
1. 跳n(n∈N)阶台阶过程中最多有 n//2 步是跳了2阶,2阶步数固定的情况下可以用 C(总步数, 2阶的步数) 求出方法数,再把所有情况都加起来即可。
from math import factorial
from functools import reduce
from scipy.special import comb # C(N, k)
def f1(n): # sum(C(总步数, n2) for n2 in range(max2)) # max2:2步的数量最大值
return sum(comb(n-n2, n2, exact=True) for n2 in range(n//2 + 1)) # 速度比下一句快一个量级
# return sum(reduce(lambda a, b: a*b, range(n2+1, n-n2+1) or [1]) // factorial(n- 2*n2) for n2 in range(n//2 + 1))
2. 动态规划:假设n(n∈N)阶台阶的跳法数为F(n),最后一步要么跳了2阶(前面跳了n-2个台阶,方法数为F(n-2) (n>=2) ),要么跳了1阶(前面跳了n-1个台阶,方法数是F(n-1) ),所以
F(n) = F(n-1) + F(n-2) (n∈N,n>=2) ,
显然:
F(0) = 1,F(1) = 1 。
其实就是斐波那契数列(斐波那契数列通项公式中有无理数,用程序计算结果当n大于70时开始有误差,故不使用通项公式求法)。
关于F(0)=1: F(n)表示的是跳n阶台阶方法的数量,而不是跳了多少步。跳0阶,方法就是不跳,这是一个方法!(当然不同人不同看法,对程序影响其实不大)
斐波那契从前向后:
def f2(n): # 1st
if n < 2:
return 1
n0, n1 = 1, 1
for _ in range(n-1):
n0, n1 = n1, n0+n1
return n1
斐波那契从后向前 n<=1996(与递归深度限制有关):
def f3(n, cache=None): # 2st
cache = cache or {0: 1, 1: 1} # 默认值为可变类型的参数
t = cache.get(n)
if t is not None:
return t # 两个get不简化可以省空间,简化了省时间
cache[n-2] = f3(n-2, cache) # Python有递归深度限制,先算n-2比先算n-1能计算更大的n
cache[n-1] = f3(n-1, cache) # 两个赋值分开写比合着能计算的n可能更大(大1,也可能不大,因为最后一层可能是n-2,n-1都到边界也可能只有n-2到边界(与递归深度和边界有关))
return cache[n-1] + cache[n-2]
检验:
def _test():
for n in range(500):
funcs = (f1, f2, f3)
res = {f(n) for f in funcs}
if len(res) != 1:
print(n, [f(n) for f in funcs])
break
print(n, res.pop())
else:
print(True)
if __name__ == '__main__':
_test()
n阶台阶,每次可以跳1阶、2阶、3阶、...、n阶
同样考虑最后那一步有n种情况,得到:
F(n) = F(n-1) + F(n-2) + F(n-3) + ... + F(n-n)
= F(0) + F(1) + F(2) + ... + F(n-1) (n∈N*) ①
所以:
F(n-1) = F(0) + F(1) + F(2) + ... + F(n-2) (n∈N,n>=2) ②
① - ② 得:
F(n) = 2·F(n-1) (n∈N*,n>=2) ③
显然:
F(0) = F(1) = 1
所以:
不建议在③步就用递归或从前向后的解法,那属于数列没学好的做法,明明有更简单的通项公式为什么不用呢,如果这是高中求数列的题,到③就结束,保证你被数学老师一顿乱k
import math
f = lambda n: 2**((n or 1)-1)
# f = lambda n: 2**(n-1) if n else 1
# f = lambda n: math.ceil(2**(n-1)) # 一样快