python上台阶问题_“上台阶问题”的分析

这是一个对“上台阶问题”的分析,即有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。可以看出,这个问题具有下面两个性质:

当M=N时候,除去一次跨N个台阶情况,有L(N,N) = 1+ L(N,N-1).

当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 classCommonException(Exception):5 """

6 Base Exception7 """

8 message = "An exceptin occured."

9

10 def __init__(self, **kwargs):11 try:12 self._error_str = self.message %kwargs13 exceptException:14 self._error_str =self.message15

16 def __str__(self):17 returnself._error_str18

19 classIntegerError(CommonException):20 """

21 Not an integer22 """

23 message = "Exception: %(number)s is not an integer!"

24

25 classRangeError(CommonException):26 """

27 integer range error28 """

29 message = "Exception: %(number)s is not in the range!"

30

31 if __name__ == "__main__":32 try:33 raise IntegerError(number = -1)34 exceptException,e:35 printstr(e)36

37 try:38 raise RangeError(number = 10000)39 exceptException,e:40 print str(e)

自定义异常类

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

1 #!/usr/bin/env python

2 #-*- coding: utf-8 -*-

3

4 from __future__ importdivision5 from lzanagi_exception importIntegerError,RangeError6

7 classLzanagi():8 def __init__(self, STAIR):9 self.STAIR =STAIR10 self.MAXSTEP = 20

11 self.HEAD = 10

12 self.LINE = 10

13 self.MATRIX =[]14 self.RATIO =[]15 self.init_table()16

17 definit_table(self):18 """

19 set the MATRIX[0][0] to get the MATRIX[1][1] by using the uniform format for loop20 """

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 defbegin_lzanagi(self):36 """

37 Note the the MATRIX is consistent of many lines,each line contain38 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 <45 self.matrix elif step="=stair:47">

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 defcalculate_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 defprint_head(self):63 string = "{0:%d}" %self.HEAD64 string = string.format("STAIR\\STEP")65 line = "{0:>%d}" %self.LINE66 for i inrange(self.STAIR):67 string += line.format(i+1)68 printstring69

70 defprint_line(self, table, index):71 string = "{0:^%d}" %self.HEAD72 string = string.format("STAIR_"+str(index))73 line = "{0:>%d}" %self.LINE74 for i in range(1,self.STAIR + 1):75 string +=line.format(table[index][i] )76 printstring77

78 defprint_number(self):79 self.print_table(self.MATRIX)80

81 defprint_ratio(self):82 self.print_table(self.RATIO)83

84 defget_one_ratio(self, index):85 """

86 when index is 2, this is the Fibonacci87 """

88 result =[]89 for i in range(1, self.STAIR+1):90 result.append(self.RATIO[i][index])91 returnresult92

93 defprint_table(self, table):94 self.print_head()95 for i in range(self.STAIR ,0 ,-1):96 self.print_line(table, i)97

98 @staticmethod99 defhelp():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 20103

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 printstring113

114 @staticmethod115 defresult():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 printstring124

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 exceptException,e:142 printstr(e)143 Lzanagi.help()

主程序

结果简单分析

Init Lzanagi!

STAIR\STEP1 2 3 4 5 6 7 8 9 10STAIR_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 resultisas follow:

STAIR\STEP1 2 3 4 5 6 7 8 9 10STAIR_101 89 274 401 464 492 504 509 511 512STAIR_91 55 149 208 236 248 253 255 256 256STAIR_81 34 81 108 120 125 127 128 128 128STAIR_71 21 44 56 61 63 64 64 64 64STAIR_61 13 24 29 31 32 32 32 32 32STAIR_51 8 13 15 16 16 16 16 16 16STAIR_41 5 7 8 8 8 8 8 8 8STAIR_31 3 4 4 4 4 4 4 4 4STAIR_21 2 2 2 2 2 2 2 2 2STAIR_11 1 1 1 1 1 1 1 1 1Calculate the ration!

The rationisas follow:

STAIR\STEP1 2 3 4 5 6 7 8 9 10STAIR_101.0 1.6182 1.8389 1.9279 1.9661 1.9839 1.9921 1.9961 1.9961 2.0STAIR_91.0 1.6176 1.8395 1.9259 1.9667 1.984 1.9921 1.9922 2.0 2.0STAIR_81.0 1.619 1.8409 1.9286 1.9672 1.9841 1.9844 2.0 2.0 2.0STAIR_71.0 1.6154 1.8333 1.931 1.9677 1.9688 2.0 2.0 2.0 2.0STAIR_61.0 1.625 1.8462 1.9333 1.9375 2.0 2.0 2.0 2.0 2.0STAIR_51.0 1.6 1.8571 1.875 2.0 2.0 2.0 2.0 2.0 2.0STAIR_41.0 1.6667 1.75 2.0 2.0 2.0 2.0 2.0 2.0 2.0STAIR_31.0 1.5 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0STAIR_21.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0 2.0STAIR_11 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)

45>
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值