“上台阶问题”的分析

      这是一个对“上台阶问题”的分析,即有N个台阶,每次最多跨M个台阶,有多少种走法数。首先声明,作者非专业研究算法,之所以想这个问题,是因为02年左右初二的时候被拉去参加一个初中数学竞赛,最后一道题目就是N=10,M=2时候的“上台阶问题”,当时差点一个个去穷举了……所以幼小的心灵留下了深刻的印象,最近偶有所感,又仔细分析了一下,有些感想来和大家分享。


 

M=2时候特例分析

对于N个台阶的走法数F(N),考虑第一步的情况:第一步跨1个台阶,剩下N-1个台阶走法为F(N-1);第一步跨2个台阶,剩下N-2个台阶走法为F(N-2)。故F(N)=F(N-1) + F(N-2),即为斐波那契数列。

还可以考虑另一种方法:对于N个台阶,记M(N,k)表示N个台阶,其中有且仅有K次跨2个台阶的走法数。考虑扩展情况下,K~(0,N),当K>[n/2]时候,M(N,k)=0。针对跨2个台阶的数目,对N个台阶的走法数进行划分,有F(N) = M(N,0) + M(N,1) + M(N,2) + … + M(N,N)

在考虑M(N,k),即N个台阶有且仅有k次跨2个台阶的走法数。针对最后一次跨2个台阶的位置,对M(N,k)进行划分。有M(N,k) = M(N-2,k-1) + M(N-3,k-1) + M(N-4,k-1) + … + M(0,k-1),其中M(N-2,k-1)表示最后一次跨越台阶为N-1,N,此时前面N-2个台阶中必有K-1次跨2个台阶,即M(N-2,K-1)。

这样,当N为奇数时候,

     F(N+2) - F(N+1) = {M(N+2,0) – M(N+1,0)} + {M (N+2,1) – M(N+1,1)} + … + { M(N+2, (N+1)/2) – M(N+1, (N+1)/2)}

     其中M(N+2,K) – M(N+1,k) = {M(N,k-1) + M(N-1,k-1) + … + M(0,k-1)} – {M(N-1,k-1) + M(N-2,k-1) + … + M(0,k-1)} = M(N,k-1)

     故F(N+2) – F(N+1) = M(N,0) + M(N,1) + M(N,2) + … + M(N,(N-1)/2) = F(N)

同理,当N为偶数时候,注意到M(N,N/2)=1,即N为偶数个台阶,其中有N/2次跨2个台阶,显然只有一种情况。此时同样有 F(N+2) – F(N+1) = F(N)。

对于N<=10的情况,M(N,k)值如下,其中K~(0-5),F(N)= M(N,0) + M(N,1) … +(N,5),对应斐波那契数列。

        

N

M(N,0)

M(N,1)

M(N,2)

M(N,3)

M(N,4)

M(N,5)

F(N)

10

1

9

28

35

15

1

89

9

1

8

21

20

5

0

55

8

1

7

15

10

1

0

34

7

1

6

10

4

0

0

21

6

1

5

6

1

0

0

13

5

1

4

3

0

0

0

8

4

1

3

1

0

0

0

5

3

1

2

0

0

0

0

3

2

1

1

0

0

0

0

2

1

1

0

0

0

0

0

1

 


 

通式分析

进一步扩展,N个台阶,每一次最多跨M个台阶,记L(N,M)为对应的走法数,显然N>=M。可以看出,这个问题具有下面两个性质:

  1. 当M=N时候,除去一次跨N个台阶情况,有L(N,N) = 1+ L(N,N-1).
  2. 当M<N时候,同特例中M=2的情况一样分析,根据第一步跨的台阶数划分,有 L(N,M) = L(N-1,M) + L(N-2,M) + … + L(N-M,M)

有以上两个性质,加上初始值M(0,0) = 0,就可以递推出任何L(N-M,M)。其中,斐波那契数列为M=2时候的特例。


 

Python的实现

这只是一个实例代码,在Win7中用自带的IDLE写的,没有考虑复杂度,并且限定最大台阶数为20。因为使用格式化字符串,运行时候,要注意终端的大小。之所以使用Izanagi.py,是因为这个单词据说是“伊邪那岐”,比Fibonacci的斐波那契霸气些。遇到单词拼写和语法错误,请自动忽略……

首先是一个自定义的异常类,主要处理非法最大台阶数,即台阶数目不是整数、台阶数目不在限定范围(设定为1-20)。

 1 #!/usr/bin/env python
 2 # -*- coding: utf-8 -*-
 3 
 4 class CommonException(Exception):
 5     """
 6     Base Exception
 7     """
 8     message = "An exceptin occured."
 9 
10     def __init__(self, **kwargs):
11         try:
12             self._error_str = self.message % kwargs
13         except Exception:
14             self._error_str = self.message
15 
16     def __str__(self):
17         return self._error_str
18 
19 class IntegerError(CommonException):
20     """
21     Not an integer
22     """
23     message = "Exception: %(number)s is not an integer!"
24 
25 class RangeError(CommonException):
26     """
27     integer range error
28     """
29     message = "Exception: %(number)s is not in the range!"
30 
31 if __name__ == "__main__":
32     try:
33         raise IntegerError(number = -1)
34     except Exception,e:
35         print str(e)
36 
37     try:
38         raise RangeError(number = 10000)
39     except Exception,e:
40         print str(e)
自定义异常类

接下来是主程序,见代码。其中两个函数help和result直接使用字符串输出了。

  1 #!/usr/bin/env python
  2 # -*- coding: utf-8 -*-
  3 
  4 from __future__ import division
  5 from lzanagi_exception import IntegerError,RangeError
  6 
  7 class Lzanagi():
  8     def __init__(self, STAIR):
  9         self.STAIR = STAIR
 10         self.MAXSTEP = 20
 11         self.HEAD = 10
 12         self.LINE = 10
 13         self.MATRIX = []
 14         self.RATIO = []
 15         self.init_table()
 16 
 17     def init_table(self):
 18         """
 19         set the MATRIX[0][0] to get the MATRIX[1][1] by using the uniform format for loop
 20         """
 21         if type(self.STAIR) != type(1):
 22             raise IntegerError(number = self.STAIR)
 23         if self.STAIR < 0 or self.STAIR > self.MAXSTEP:
 24             raise RangeError(number = self.STAIR)
 25         self.MATRIX = []
 26         for i in range(self.STAIR + 1):
 27             matrix_array = []
 28             ratio_array = []
 29             for j in range(self.STAIR + 1):
 30                 matrix_array.append(0)
 31                 ratio_array.append(1)
 32             self.MATRIX.append(matrix_array)
 33             self.RATIO.append(ratio_array)
 34 
 35     def begin_lzanagi(self):
 36         """
 37         Note the the MATRIX is consistent of many lines,each line contain
 38         one stair and many step values.
 39         When calculate, begin with min step to max step.
 40         What you get is MATRIX[stair][step]
 41         """
 42         for step in range(1, self.STAIR+1):
 43             for stair in range(1, self.STAIR+1):
 44                 if stair < step:
 45                     self.MATRIX[stair][step] = self.MATRIX[stair][step-1]
 46                 elif step == stair:
 47                     self.MATRIX[stair][step] = self.MATRIX[stair][step-1] + 1
 48                 else:
 49                     #when step = 1, then self.MATRIX[stair][1]= self.MATRIX[stair-1][1]=1
 50                     #when step = 2, then self.MATRIX[stair][step] = self.MATRIX[stair-1][step] + self.MATRIX[stair-2][step]
 51                     #when step = 3, then self.MATRIX[stair][step] = self.MATRIX[stair-1][step] + self.MATRIX[stair-2][step] + self.MATRIX[stair-3][step]
 52                     #...
 53                     #until step = MAXSTEP, finish lzanagi!
 54                     for i in range(1, step+1):
 55                         self.MATRIX[stair][step] += self.MATRIX[stair-i][step]
 56 
 57     def calculate_ratio(self):
 58         for step in range(1, self.STAIR+1):
 59             for stair in range(2, self.STAIR+1):
 60                 self.RATIO[stair][step] = round(self.MATRIX[stair][step]/self.MATRIX[stair-1][step],4)
 61 
 62     def print_head(self):
 63         string = "{0:%d}" % self.HEAD
 64         string = string.format("STAIR\\STEP")
 65         line = "{0:>%d}" % self.LINE
 66         for i in range(self.STAIR):
 67             string += line.format(i+1)
 68         print string
 69 
 70     def print_line(self, table, index):
 71         string = "{0:^%d}" % self.HEAD
 72         string = string.format("STAIR_"+str(index))
 73         line = "{0:>%d}" % self.LINE
 74         for i in range(1,self.STAIR + 1):
 75             string += line.format(table[index][i] )
 76         print string
 77 
 78     def print_number(self):
 79         self.print_table(self.MATRIX)
 80 
 81     def print_ratio(self):
 82         self.print_table(self.RATIO)
 83 
 84     def get_one_ratio(self, index):
 85         """
 86         when index is 2, this is the Fibonacci
 87         """
 88         result = []
 89         for i in range(1, self.STAIR+1):
 90             result.append(self.RATIO[i][index])
 91         return result
 92         
 93     def print_table(self, table):
 94         self.print_head()
 95         for i in range(self.STAIR ,0 ,-1):
 96             self.print_line(table, i)
 97 
 98     @staticmethod
 99     def help():
100         string = '''
101 You must input an integer, and in the range 1~ MAXSTAIR.
102 Note that I doesn't consider the time complexity. So I just limit the MAXSTAIR to 20
103 
104 The is the "step stair question".
105 for example:
106     10 stairs,you can take one or two step(s) at once,so you get the Fibonacci!
107     More over, 10 stairs,but you can take one or two or three step(s) at once,what will happen?
108 Generally, N stairs,you can take at most M steps at once (Of course N>=M). Now what will happen?
109 When M=2,you get Fibonacci. So more generally, I call this method Lzanagi!
110 This program aims at this question. I can prove this algorithm,that is say,this problem can be resoved by this method.
111 '''
112         print string
113 
114     @staticmethod
115     def result():
116         string = '''
117 You can see that S(N,N), which means that N stairs,you can take at most N step once, S(N,N) = 2^(N-1).
118 This is an interesting thing!
119 Proving: S(N,N) = 1 + S(N,N-1) = 1 + S(N-1,N-1) + S(N-2,N-1) + ... + S(1,N-1).
120 Note that S(1,N-1) = S(1,1); S(2,N-1) = S(2,2); ...;S(N-2,N-1) = S(N-2,N-2).
121 Then you can use the matheimatical induction to prove that S(N,N) = 1 + 2^(N-2) + 2^(N-3) + ... + 1 = 2^(N-1)
122 '''
123         print string
124         
125 
126 if __name__ == "__main__":
127     try:
128         lzanagi = Lzanagi(10)
129         print "Init Lzanagi!"
130         lzanagi.print_number()
131         print "Begin Lzanagi!"
132         lzanagi.begin_lzanagi()
133         print "The result is as follow:"
134         lzanagi.print_number()
135         print "Calculate the ration!"
136         lzanagi.calculate_ratio()
137         print "The ration is as follow:"
138         lzanagi.print_ratio()
139         #lzanagi.help()
140         #lzanagi.result()
141     except Exception,e:
142         print str(e)
143         Lzanagi.help()
主程序

结果简单分析

Init Lzanagi!
STAIR\STEP         1         2         3         4         5         6         7         8         9        10
 STAIR_10          0         0         0         0         0         0         0         0         0         0
 STAIR_9           0         0         0         0         0         0         0         0         0         0
 STAIR_8           0         0         0         0         0         0         0         0         0         0
 STAIR_7           0         0         0         0         0         0         0         0         0         0
 STAIR_6           0         0         0         0         0         0         0         0         0         0
 STAIR_5           0         0         0         0         0         0         0         0         0         0
 STAIR_4           0         0         0         0         0         0         0         0         0         0
 STAIR_3           0         0         0         0         0         0         0         0         0         0
 STAIR_2           0         0         0         0         0         0         0         0         0         0
 STAIR_1           0         0         0         0         0         0         0         0         0         0
Begin Lzanagi!
The result is as follow:
STAIR\STEP         1         2         3         4         5         6         7         8         9        10
 STAIR_10          1        89       274       401       464       492       504       509       511       512
 STAIR_9           1        55       149       208       236       248       253       255       256       256
 STAIR_8           1        34        81       108       120       125       127       128       128       128
 STAIR_7           1        21        44        56        61        63        64        64        64        64
 STAIR_6           1        13        24        29        31        32        32        32        32        32
 STAIR_5           1         8        13        15        16        16        16        16        16        16
 STAIR_4           1         5         7         8         8         8         8         8         8         8
 STAIR_3           1         3         4         4         4         4         4         4         4         4
 STAIR_2           1         2         2         2         2         2         2         2         2         2
 STAIR_1           1         1         1         1         1         1         1         1         1         1
Calculate the ration!
The ration is as follow:
STAIR\STEP         1         2         3         4         5         6         7         8         9        10
 STAIR_10        1.0    1.6182    1.8389    1.9279    1.9661    1.9839    1.9921    1.9961    1.9961       2.0
 STAIR_9         1.0    1.6176    1.8395    1.9259    1.9667     1.984    1.9921    1.9922       2.0       2.0
 STAIR_8         1.0     1.619    1.8409    1.9286    1.9672    1.9841    1.9844       2.0       2.0       2.0
 STAIR_7         1.0    1.6154    1.8333     1.931    1.9677    1.9688       2.0       2.0       2.0       2.0
 STAIR_6         1.0     1.625    1.8462    1.9333    1.9375       2.0       2.0       2.0       2.0       2.0
 STAIR_5         1.0       1.6    1.8571     1.875       2.0       2.0       2.0       2.0       2.0       2.0
 STAIR_4         1.0    1.6667      1.75       2.0       2.0       2.0       2.0       2.0       2.0       2.0
 STAIR_3         1.0       1.5       2.0       2.0       2.0       2.0       2.0       2.0       2.0       2.0
 STAIR_2         1.0       2.0       2.0       2.0       2.0       2.0       2.0       2.0       2.0       2.0
 STAIR_1           1         1         1         1         1         1         1         1         1         1
运行结果

当N取值为10时候的输出如上,第一个数组为初始数组,全为0;第二个是计算出的所有L(N,M),每一行表示限定台阶数的情况下,不同最大步数对应的走法数。其中可以看出,STEP=2时候,第二列为斐波那契数列。值得注意的是,当有N个台阶且每一步最多跨N个台阶时候,有L(N,N) = 2^(N-1)。这是一个很有意思的发现,主程序代码(见函数result的注释)中对这个进行了一个形式化的证明。

同时,代码还对N=10的情况,计算了每一列相邻的数据的比例。可以看出,STEP=2时候,很熟悉的斐波那契数列的黄金分割比例1.6182。


进一步扩展

这个问题和另一个经典的“正整数分割问题”有些类似,想请自行Google之。值得注意的是,正整数分割中,如果考虑分割的顺序,即(1+2+1),(1+1+2),(2+1+1)是整数4的不同分割的话,此时的情况与N个台阶,每次最多跨N个台阶的走法数问题完全一致。其总数为

L(N,N) = 2^(N-1)

转载于:https://www.cnblogs.com/lycc316/p/3388690.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,针对您的问题,我来讲一下动态规划爬楼梯问题5个台阶分析方法。 在动态规划中,我们通常会使用一个数组来存储子问题的解,以便后续的子问题可以直接使用已解决的子问题的解,从而避免重复计算。对于爬楼梯问题,我们可以使用一个一维数组来存储到达每个台阶的不同路径数量。 以下是对于5个台阶分析方法: 1. 递推法:根据题目可知,到达第n个台阶的方法数为到达第n-1和第n-2个台阶的方法数之和,因此可以使用递推的方式来求解。具体实现可以使用一个一维数组dp来存储到达每个台阶的方法数,从dp[0]开始逐个计算,最终返回dp[n]即可。 2. 递归法:类似于递推法,也是通过递归的方式来求解。递归终止条件为到达第0个台阶和第1个台阶的方法数分别为1和1,递归求解到达n-1和n-2个台阶的方法数,最终返回它们之和即可。 3. 记忆化搜索法:在递归法的基础上,加入了记忆化的思想,即在求解每个子问题时,先查看该子问题是否已经计算过,如果已经计算过,则直接返回已有的解。如果没有计算过,则递归求解,并将结果存储到一个数组中,以便后续的子问题可以直接使用已解决的子问题的解。 4. 斐波那契数列法:利用斐波那契数列的递推公式,即f(n)=f(n-1)+f(n-2),来求解爬楼梯问题。具体实现可以使用两个变量f1和f2来存储f(n-1)和f(n-2)的值,然后依次更新它们的值,最终返回f(n)即可。 5. 矩阵快速幂法:在斐波那契数列法的基础上,利用矩阵快速幂的思想,可以将时间复杂度从O(n)降低到O(logn)。具体实现可以将斐波那契数列的递推公式转化为矩阵的形式,然后使用矩阵快速幂的方式来求解。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值