将 while 循环转换为递归

时间:2021-02-23 09:33:10

标签: python loops 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

3 个答案:

答案 0 :(得分:1)

这是原则性的方法:

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)

这里是愚蠢的,无原则的方式,基本上只是使用一个递归辅助函数,在它的闭包中改变变量,这只是为了演示递归如何基本上可以直接替换一个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

编辑:

如果不想使用辅助辅助函数,则需要额外的参数:

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)

答案 1 :(得分:0)

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 而不是二进制或 str。相应地更改了解决方案。

答案 2 :(得分:0)

递归是一种函数式遗产,因此将其与函数式风格结合使用会产生最佳效果。这意味着避免诸如突变、变量重新分配和其他副作用之类的事情。我们可以用inductive reasoning原则性地解决问题-

  1. 如果输入 n 只有一位,则已达到基本情况。如果 z 为 1 或返回 n 加 1 为最后的零,则返回连续零,z
  2. (inductive) n 有不止一位剩余。如果第一位是 1,则连续中断。产生当前的零条纹 z,并在子问题 n >> 1 上重复出现,新的零条纹为 0
  3. (归纳)n 有多个位且第一位为 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 函数已经接受了一个可迭代的输入,所以复制它几乎没有意义。

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,我们可以在我们的函数中嵌入一个嵌套的帮助器 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

# ...

相关问答