I'm trying to turn this code ( that gives the maximum number of consecutive 0's in a binary) to a recursive code. I tried in many ways but the result is wrong. This is the code using a while loop:
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
Here is the principled way:
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)
Here is the dumb, unprincipled way, basically just use a recursive helper function that mutates variables in it's closure, this is just to demonstrate how recursion can basically replace a while-loop directly, but it's not very elegant:
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
EDIT:
If you don't want to use an auxilliary helper function, you need additional 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
EDIT: According to comment, input is int
instead of binary or str
. Changed solution accordingly.
Recursion is a functional heritage and so using it with functional style yields the best results. This means avoiding things like mutations, variable reassignments and other side effects. We can use inductive reasoning to solve the problem in a principled way -
n
, has only a single bit, the base case has been reached. Return the zero streak, z
, if n
is 1 or return z
plus 1 for the final zeron
has more than one bit remaining. If the first bit is a 1, the streak is broken. Yield the current zero streak, z
, and recur on the sub-problem n >> 1
with a fresh zero streak of 0n
has more than one bit and the first bit is a 0. Recur on the sub-problem n >> 1
with an incremented zero streak 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)
Notice the generator is disentangled from the max
logic. The built-in max
function already accepts an iterable input, so there's little sense in duplicating that.
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
prevent leaked parameters
If you don't want extra parameters like z
or expecting caller to require max
, we can embed a nested helper, loop
, in our function -
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
Now when you call it, max
is no longer required by the caller -
for x in range(20):
print(f"{x:b}", consecutive_zeroes(x)) # <- no need to call max
# ...
related Q&A
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.