python-3.x 代码评审:引发无限循环是否可接受?

c6ubokkw  于 2022-12-20  发布在  Python
关注(0)|答案(3)|浏览(124)

今天偶然发现了这个编码挑战,目标是检查n是否是2的幂。虽然我的解决方案似乎通过了所有测试,但大家都不太满意。
首先,它似乎与之前编写的伪代码不匹配,当尝试将n与大于测试中使用的数字进行比较时,即:while n < 10:我遇到了一个无限循环。
我很难理解这个问题!
我听说过有目的地引入一个不确定循环;这是不是某种抽象概念?

def is_power_of_two(n):
  # Check if the number can be divided by two without a remainder
  while n % 2 != n:
    n = n / 2
  # If after dividing by two the number is 1, it's a power of two
  if n == 1:
    return True
  return False
print(is_power_of_two(0)) # Should be False
print(is_power_of_two(1)) # Should be True
print(is_power_of_two(8)) # Should be True
print(is_power_of_two(9)) # Should be False
sbdsn5lh

sbdsn5lh1#

这个算法实际上可以完全不用循环来求解。
如果选择使用位移位,则算法如下所示:

def is_power_two(n):
    if n < 1:
        return False
    return n == (1 << n.bit_length() - 1)

给定一个数字n,您可以使用1 << number of bits - 1的位移位以及对n的相等性检查。如果该数字是2的幂,则返回零(True),否则返回非零值(False)。
示例:
数字8占用4位:0b1000。左移三位(1 << 3)并进行等于8的检查(0b1000 == 8)将返回True。但是,数字10也占用四位:然而,一位左移3(0b1000),用8(0b1010 == 8)的相等性检查返回假。
测试:

for i in range(65):
    print(i, is_power_two(i))

0 False
1 True  # 2**0
2 True
3 False
4 True
5 False
6 False
7 False
8 True
9 False
...
62 False
63 False
64 True
5vf7fwbs

5vf7fwbs2#

该代码似乎对许多输入都很有效,但它依赖于浮点数,通过应用(非整数)除以2。如果最后n为1,那么原始数字确实是2的幂。
然而,由于浮点数的限制,这将打破足够大的输入,你会得到假阳性。
例如:

is_power_of_two((1<<80) + 1)

这个函数应该返回False,因为这个数字中有两个1位,但是你的函数会返回True,就好像输入是1<<80一样。
要获得正确的实现,应该使用整数除法(//),并且只要余数为0就一直循环。
回到无限循环的主题:它不能无限循环,因为n通过除法变得更小,并且最终,当n % 2 == n时,它将变得低于值2。
即使n为负......在这种情况下,它将通过除法保持为负,但同样由于浮点数限制,除法最终将给出0,此时循环条件满足。
如果输入为0,则基于整数的版本可能会永远循环,并且需要保护。我们可以利用这个机会捕获负输入:

if n <= 0:
    return False
  while n % 2 == 0:
    n = n // 2

现在,上面的测试用例将返回正确的结果。
请注意,您可以使用一些按位操作符来完成此操作,而无需显式循环:

return n > 0 and (n & -n == n)

或者可能更可读:

return n > 0 and (1 << (n.bit_length() - 1) == n)
rkue9o1l

rkue9o1l3#

这适用于较小的数字:

def is_power_of_two(n):
    while n > 1:
        n /= 2
    return n == 1

print(is_power_of_two(0)) # False
print(is_power_of_two(1)) # True
print(is_power_of_two(8)) # True
print(is_power_of_two(9)) # False

但是对于更大的数字,它的精度会受到Python浮点精度的影响,所以,你可以使用以下代码:

def is_power_of_two(n):
    return bin(n).count('1') == 1

print(is_power_of_two(0)) # False
print(is_power_of_two(1)) # True
print(is_power_of_two(8)) # True
print(is_power_of_two(9)) # False

print(is_power_of_two(2**54))     # True
print(is_power_of_two(2**54 - 1)) # False
print(is_power_of_two(2**54 + 1)) # False

这是利用一个数字是2的幂的事实,如果在二进制中,它的前导数字之后没有其他的1(例如2 = 10,4 = 100,8 = 1000,等等)。

相关问题