【问题标题】:Finding if a number is a power of 2 using recursion使用递归查找数字是否为 2 的幂
【发布时间】:2015-06-11 09:44:36
【问题描述】:

我正在尝试使用递归来确定一个数字是否是 2 的幂。但是,我似乎无法找出正确的解决方案。到目前为止,这是我尝试过的:

def is_power(n):
    n = n/2
    if n == 2:
        return True
    elif n > 2:
        is_power(n)
    else:
        return False


if is_power(32):
    print 'yes'
else:
    print 'no'

由于“32”是 2 的幂,我希望我的代码返回“是”作为输出。但是,代码改为输出“否”。我的代码似乎有什么问题?

【问题讨论】:

  • 提示:你已经(或多或少)实现了is_even,而不是is_power(至少,在Python 3中)。
  • 这里 1 不算作你的答案吗(即2**0)?

标签: python python-2.7 recursion


【解决方案1】:

由于我在这里已经有了一个公认的答案,我将用这个来解释一下为什么你的方法不好:

它在 python 中使用递归。 Python 需要为每次调用打开一个堆栈帧,因此对于非常大的数字,这将是一个不好的解决方法。对于非常大的整数,它甚至会失败。

我什至不认为 像 python 这样的非纯功能语言中的递归是在这里做的直观的事情。有一百万种更简单的方法可以做到这一点;例如,while 循环:

n = int(n)
while n>1:
    if n/2 != n/2.0: #compare integer division to float division
       return False
    n = n/2
return True

检查2的幂可以通过了解计算机上整数的存储结构是什么来巧妙地完成:它是二进制的。所以你可以在你的int的二进制表示中计算二进制1s:

return bin(n).count('1') == 1

也可以。当然,这意味着 python 在内部将整数转换为字符串,这会浪费大量内存。所以你也可以

power_of_2 = 1
while power_of_2 <= n:
    if power_of_2 == n:
        return True
    power_of_2 *= 2
return False

只需将您的数字与所有小于或等于 2 的幂进行比较。 (当然这可能需要更长的时间并使用更多的内存,因为你的 python 解释器必须解释 python 而不是仅仅将你的整数转换为 C 中的字符串,但这是关于算法原理的,对吧?)那样,你不需要不需要保留内存来计算二进制表示中 1 的出现次数。

当然,有一种方法可以解决您的问题,我从things I've learned 用 C/C++ 编写:

bool(n and not (n&(n-1)))

解释n and not (n&amp;(n-1))n 是 True iff n != 0,否则会被错误地限定为 2 的幂。

对于not (n&amp;(n-1))n&amp;(n-1) 检查某事物是否不是 2 的幂,因此我们必须将其反转。 &amp; 是按位“与”运算符。要理解n &amp; (n-1),假设n 是2 的幂,假设8。所以n-1 == 7,因此

8|0b1000
7|0b0111
--------
&|0b0000

如您所见,对于所有 2 的幂,n&amp;(n-1)0,其计算结果为 False。对于所有非 2 的幂,在减去 1 时不会反转所有位,因此 n&amp;(n-1) != 0 的计算结果为 True

【讨论】:

  • 这个if n/2 != n/2.0:在python 3中会失败,可以修改为if n // 2 != n / 2
【解决方案2】:
elif n > 2:
    is_power(n)

缺少return

def is_power(n):
    n = n/2
    if n == 2:
        return True
    elif n > 2:
        return is_power(n)
    else:
        return False

因此,is_power 的“第一”级别不返回任何内容(或 None,取决于您的检查方式),这会导致输出 no

@kaveman 正确指出is_power(2) 会产生错误的结果。 您可以通过将 elif 子句中的 2 减半来解决此问题:

def is_power(n):
    if not n == int(n):
        return False
    n = int(n)
    if n == 1:
        return True
    elif n > 2:
        return is_power(n/2.0)
    else:
        return False

编辑:@will 指出我将 python2 与 python3 部门混为一谈。使用/2.0 可以解决这个问题。此外,在对问题的 cmets 中,他指出 1 是 2 的幂。检查 ==1 而不是 ==2 可以解决该问题。另外,我添加了一个int 演员表,这对于 2 的幂检查不是必需的(因为 IEEE754 浮点毕竟是以 2 为底的,所以 2 的幂是完全可表示的),但对于非 2 的基础,这将使代码可移植。

【讨论】:

  • 虽然您对缺少的return 是正确的,但此实现仍然存在错误。运行 is_power(2) 时会发生什么
  • 为什么不直接修复你的实现,以免混淆 OP 和未来的读者?
  • 是的,我错过了“回报”。感谢您指出这一点!
  • 你的答案是错误的 - 见下面我的。在一些应该返回 false 的数字上尝试你的函数 - 即5,9,10,11,17,18,19,20,21,22,23,33,34,35,36,37,38,39,40,...
  • @will.,你是对的。我以为我看到了一个python3.4标签,但我错了。所以,是的,我会进行编辑,这将解决这个问题。
【解决方案3】:

上面提供的答案是错误

是的,它适用于 2 的幂,但它也会产生许多误报 - 因为您使用的是整数除法 - 因为您使用的是 python 2.7。

如果您使用 from __future__ import division,它会起作用,这会改变 / 的行为 - 但这会改变它在整个程序中的行为,这可能不是您想要的。

例如:

print 33/2 # 16
print 32/2 # 16

但是,使用from __future__ import division,输出会更改为正确(或至少更直观)的结果 - 要获得原始整数数学行为,您需要改用//(如在 python3 中)。

所以正确的答案会更像这样:

def is_power(n):
    n = n/2.0
    if n == 2:
        return True
    elif n > 2:
        return is_power(n)
    else:
        return False

注意开头的n= n/2.0

您需要测试您的代码是否适用于多个测试用例,而不仅仅是您想要特定结果的测试用例。

不过,我会选择这样的方式:

def is_power(n):
  if n == 2:
    return True
  elif n%2 != 0:
    return False
  else:
    return is_power(n/2.0)

【讨论】:

  • 优秀的答案,绝对是比 OP 更好的方法,非常值得支持 :) 唯一的问题是,当使用 % 运算符时,我们不妨只使用按位比较运算符,并且使用一步法:return not (n &amp; (n-1))。 python 的好处是它有一个“无限大整数”的工作实现,所以这甚至适用于非常大的值(例如2**100 -1)。
  • @MarcusMüller 我不同意将其全部放入 n&amp;(n-1) 构造中 - 除非您附上详细的评论,详细说明正在发生的事情。
【解决方案4】:

虽然这不能直接回答您的问题,但最快的实现方法是

def is_power(n):
    return ((n & (n - 1)) == 0) and n != 0

虽然这已在 Marcus 之前的一篇文章中介绍过,但“多一点”解释可能会对某些人有所帮助。每个数字和 (number-1) 的二进制表示至少共享一个 1 位,除非该数字是 2 的幂。此外,所有负数共享前导位,因此被此方法排除。

唯一的例外是数字 0,它与之前的数字 -1 不共享位,并且可能不会被视为 2 的幂。因此这需要显式检查。

数字的二进制表会清楚地说明这一点。

> Dcml  Binary 5bit
> -15   10001
> -14   10010
> -13   10011
> -12   10100
> -11   10101
> -10   10110
> -9    10111
> -8    11000
> -7    11001
> -6    11010
> -5    11011
> -4    11100
> -3    11101
> -2    11110
> -1    11111 
>  0    00000 
>  1    00001
>  2    00010
>  3    00011
>  4    00100
>  5    00101
>  6    00110
>  7    00111
>  8    01000
>  9    01001
>  10   01010
>  11   01011
>  12   01100
>  13   01101
>  14   01110
>  15   01111
>  16   10000

此有符号数字表示法中的负数不会满足条件,因为它们都共享最高有效位为 1。数字 0 将满足条件为 0&(any_other_number) == 0。如果您需要在非常大的情况下实现此条件使用 bitarrays 或更改 dtype 的 numpy 可能会更好。此外,this 的讨论可能有助于提高大型数组/数字的按位运算速度。

【讨论】:

    【解决方案5】:

    您可以使用 ma​​th 库的强大功能

    import math
    
    def isPowerOfTwo(n):
        if n <= 0:
            return False
        res = int(math.log(n) / math.log(2))
        return 2 ** res == n
    

    【讨论】:

      【解决方案6】:
          """
          Power check if a number is a power of 2
          Negative numbers are not allowed
          """
          import math
      
      
          def is_power_of_two(num):
              """
              :type num: integer
              """
              try:
                  x=0
                  x = math.log10(num)/math.log10(2)
                  return 2 ** x == num
              except ValueError:
                  exit()
      

      【讨论】:

        【解决方案7】:

        这是我对检查哪个数字是另一个数字的基数的函数的解决方案:

        def is_power_of(number, base):
          # when number is smaller than base.
          if base <= 1:
            return False
          elif number < base:
            return False
          elif  number > base:
            # keep dividing number by base.
            return is_power_of(number/base, base)
          else:
            return True
        

        【讨论】:

          【解决方案8】:

          这将提供一些信息,

          counter = 0
          def powoftwo(n):
              global counter
              counter+=1
              if n%2 != 0:
                return "Given Number %s is not Power of 2!"%g
              else:      
                if n==2:
                  return "yes give number %s = 2**%s is power of 2!"%(g, counter)
                return powoftwo(n/2)
          
          g = 1024
          print powoftwo(g)
          
          yes give number 1024 = 2**10 is power of 2!
          

          【讨论】:

            【解决方案9】:

            派对迟到了,不过,另一种方式:

            import math 
            
            def is_power_of_2(x):
                n = math.log(abs(x), 2)
                return n == int(n) 
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2022-01-03
              • 2020-11-14
              • 1970-01-01
              • 2021-06-27
              • 1970-01-01
              • 1970-01-01
              • 2010-10-10
              相关资源
              最近更新 更多