[英]Turning a while loop to a recursion
我正在尝试将此代码(给出二进制中连续 0 的最大数量)转换为递归代码。 我尝试了很多方法,但结果是错误的。 这是使用 while 循环的代码:
def consecutivezeros(N):
if N==0 :
return 1
# variable to store the length
# of longest consecutive 0's
maxm = -1
# to temporary store the
# consecutive 0's
cnt = 0
while (N):
if (not (N & 1)):
cnt += 1
N >>= 1
maxm = max(maxm, cnt)
else:
maxm = max(maxm, cnt)
cnt = 0
N >>= 1
return maxm
以下是原则方法:
def consecutivezeros(N):
if N == 0:
return 1
def _consecutivezeros(N, maxm, cnt):
if not N:
return maxm
elif (not (N & 1)):
new_cnt = cnt + 1
return _consecutivezeros(N>>1, max(maxm, new_cnt), new_cnt)
else:
return _consecutivezeros(N>>1, max(maxm, cnt), 0)
return _consecutivezeros(N, -1, 0)
这是愚蠢的,没有原则的方式,基本上只是使用递归帮助器 function 在它的闭包中改变变量,这只是为了演示递归如何基本上可以直接替换while循环,但它不是很优雅:
def consecutivezeros(N):
if N == 0:
return 1
maxm = -1
cnt = 0
def _looper():
nonlocal maxm, cnt, N
if not N:
return
if (not (N & 1)):
cnt += 1
maxm = max(maxm, cnt)
else:
maxm = max(maxm, cnt)
cnt = 0
N >>= 1
_looper(N)
return maxm
编辑:
如果您不想使用辅助助手 function,则需要额外的 arguments:
def consecutivezeros(N, _maxm=-1, _cnt=0, _first=True):
if N == 0 and _first:
return 1
elif not N:
return _maxm
elif (not (N & 1)):
incr = _cnt + 1
return consecutivezeros(N>>1, max(_maxm, incr), incr, False)
else:
return consecutivezeros(N>>1, max(_maxm, _cnt), 0, False)
def rec_count(s: str, c: int = 0, m: int = 0):
if s:
if s[0] == "0":
if (c := c+1) > m:
m = c
return rec_count(s[1:], m=m, c=c)
else:
return rec_count(s[1:], m=m)
else:
return m
N = 100000100101
print(rec_count(s=str(N)))
# 5
编辑:根据评论,输入是int
而不是 binary 或str
。 相应地更改了解决方案。
递归是一种函数式遗产,因此将其与函数式风格一起使用会产生最佳结果。 这意味着避免诸如突变、变量重新分配和其他副作用之类的事情。 我们可以用归纳推理有原则地解决问题——
n
只有一位,则已达到基本情况。 如果n
为 1,则返回零条纹z
或返回z
加 1 作为最后一个零n
剩余多于一位。 如果第一位是 1,则连续中断。 产生当前的零条纹z
,并在子问题n >> 1
上重复出现,新的零条纹为 0n
有多个位,并且第一位是 0。在子问题n >> 1
上递归,并带有递增的零条纹def consecutive_zeroes(n, z = 0):
if n < 2: # 1
yield z if n == 1 else z + 1
elif n & 1: # 2
yield z
yield from consecutive_zeroes(n >> 1, 0)
else: # 3
yield from consecutive_zeroes(n >> 1, z + 1)
请注意,生成器与max
逻辑分离。 内置的max
function 已经接受了一个可迭代的输入,所以复制它没有什么意义。
for x in range(20):
print(f"{x:b}", max(consecutive_zeroes(x)))
0 1
1 0
10 1
11 0
100 2
101 1
110 1
111 0
1000 3
1001 2
1010 1
1011 1
1100 2
1101 1
1110 1
1111 0
10000 4
10001 3
10010 2
10011 2
for x in range(60, 80):
print(f"{x:b}", max(consecutive_zeroes(x)))
111100 2
111101 1
111110 1
111111 0
1000000 6
1000001 5
1000010 4
1000011 4
1000100 3
1000101 3
1000110 3
1000111 3
1001000 3
1001001 2
1001010 2
1001011 2
1001100 2
1001101 2
1001110 2
1001111 2
for x in range(500, 520):
print(f"{x:b}", max(consecutive_zeroes(x)))
111110100 2
111110101 1
111110110 1
111110111 1
111111000 3
111111001 2
111111010 1
111111011 1
111111100 2
111111101 1
111111110 1
111111111 0
1000000000 9
1000000001 8
1000000010 7
1000000011 7
1000000100 6
1000000101 6
1000000110 6
1000000111 6
防止参数泄露
如果您不希望像z
这样的额外参数或期望调用者需要max
,我们可以在我们的 function 中嵌入一个嵌套帮助器loop
——
def consecutive_zeroes(n): # <- new wrapper, with no leaked parameters
def loop(n, z): # <- our function above, renamed to loop
if n < 2:
yield z if n == 1 else z + 1
elif n & 1:
yield z
yield from loop(n >> 1, 0)
else:
yield from loop(n >> 1, z + 1)
return max(loop(n, 0)) # <- max
现在,当您调用它时,调用者不再需要max
-
for x in range(20):
print(f"{x:b}", consecutive_zeroes(x)) # <- no need to call max
# ...
相关问答
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.