Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/365.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如何跳过内置int中的尾随零位?_Python_Python 3.x_Performance - Fatal编程技术网

Python 如何跳过内置int中的尾随零位?

Python 如何跳过内置int中的尾随零位?,python,python-3.x,performance,Python,Python 3.x,Performance,有没有一些快速的方法来获取Python3内置整数中尾随的零的数量 例如,如果我想检查是否有4个尾随零,我可以执行以下操作: >>> some_integer & 15 或对于8个尾随零: >>> some_integer & 255 如果结果不是0,则在前4位或8位中有一个 有没有办法在一般情况下进行此操作?我的目标是通过>操作符跳过任意数量的尾随零 n = 12 # 0b1100 while n % 2 == 0: n //= 2

有没有一些快速的方法来获取Python3内置整数中尾随的零的数量

例如,如果我想检查是否有4个尾随零,我可以执行以下操作:

>>> some_integer & 15
或对于8个尾随零:

>>> some_integer & 255
如果结果不是
0
,则在前4位或8位中有一个

有没有办法在一般情况下进行此操作?我的目标是通过
>
操作符跳过任意数量的尾随零

n = 12 # 0b1100
while n % 2 == 0:
    n //= 2
# out: 3 i.e. 0b11
这将数字除以2直到不可分割,从而删除尾随的零

编辑:如果
n==0
,则实现上述代码无限循环。可更正:

n = 12 # 0b1100
if n != 0:
    while n % 2 == 0: n //= 2
# out: 3 i.e. 0b11
编辑:借鉴@MisterMiyagi的建议

n = 10 # 0b1010
if n != 0:
  while n % 2 == 0: n, r = divmod(n, 2)
# n = 5 i.e. 0b101
用于任意长数字

针对32位数字的无循环快速高效解决方案

改编自

您可以按尾随零的数量右移并去除它们(在
shift=n>>r
中表示)

测试

输出:

n: 1 bin: 0b1 zeroes: 0 shifted: 0b1
n: 2 bin: 0b10 zeroes: 1 shifted: 0b1
n: 3 bin: 0b11 zeroes: 0 shifted: 0b11
n: 4 bin: 0b100 zeroes: 2 shifted: 0b1
n: 5 bin: 0b101 zeroes: 0 shifted: 0b101
n: 6 bin: 0b110 zeroes: 1 shifted: 0b11
n: 7 bin: 0b111 zeroes: 0 shifted: 0b111
n: 8 bin: 0b1000 zeroes: 3 shifted: 0b1
n: 9 bin: 0b1001 zeroes: 0 shifted: 0b1001
n: 10 bin: 0b1010 zeroes: 1 shifted: 0b101
n: 11 bin: 0b1011 zeroes: 0 shifted: 0b1011
n: 12 bin: 0b1100 zeroes: 2 shifted: 0b11
n: 13 bin: 0b1101 zeroes: 0 shifted: 0b1101
n: 14 bin: 0b1110 zeroes: 1 shifted: 0b111
n: 15 bin: 0b1111 zeroes: 0 shifted: 0b1111
n: 16 bin: 0b10000 zeroes: 4 shifted: 0b1
n: 17 bin: 0b10001 zeroes: 0 shifted: 0b10001
n: 18 bin: 0b10010 zeroes: 1 shifted: 0b1001
n: 19 bin: 0b10011 zeroes: 0 shifted: 0b10011

您可以使用位算术(用于正值和负值,但不包括
0
,因为没有
1
可跟踪,因此未对其行为进行定义):

或:

或者,可能有更有趣的
0
行为(感谢@TomKarzes评论):

为了理解它们是如何工作的,让我们考虑一下最后一个表达式。当我们执行
x-1
时,如果
x
为奇数,则只有该位被翻转,然后当我们执行xor
^
时,该位是操作中唯一幸存的位。类似地,当
x
不是奇数时,每个尾随的零被翻转到
1
,被跟踪的
1
被翻转到
0
,所有其他位都不被触及,然后我们进行
xor
操作,直到被跟踪的
1
的所有位都被设置。然后,计算找到的有效位的数量,
-1
只是一个常量偏移量,我们需要得到“尾随零的数量”

为什么第一种方法也有效,是因为负数的两个补码表示。具体细节留给读者作为练习

为了证明它们有效:

for i in range(-15, 16): 
     print(
         f'{i:5b}',
         f'{i ^ -i:3d}',
         f'{(i & -i).bit_length() - 1:2d}',
         f'{(i ^ -i).bit_length() - 2:2d}',
         f'{(i ^ (i - 1)).bit_length() - 1:2d}') 
-1111-200
-1110  -4  1  1  1
-1101  -2  0  0  0
-1100  -8  2  2  2
-1011  -2  0  0  0
-1010  -4  1  1  1
-1001  -2  0  0  0
-1000 -16  3  3  3
-111  -2  0  0  0
-110  -4  1  1  1
-101  -2  0  0  0
-100  -8  2  2  2
-11  -2  0  0  0
-10  -4  1  1  1
-1  -2  0  0  0
0   0 -1 -2  0
1  -2  0  0  0
10  -4  1  1  1
11  -2  0  0  0
100  -8  2  2  2
101  -2  0  0  0
110  -4  1  1  1
111  -2  0  0  0
1000 -16  3  3  3
1001  -2  0  0  0
1010  -4  1  1  1
1011  -2  0  0  0
1100  -8  2  2  2
1101  -2  0  0  0
1110  -4  1  1  1
1111  -2  0  0  0
请注意
0
输入的两个表达式的不同行为。 特别是,
(x^(x-1)).bit_length()-1
可能会导致更简单的表达式,如果希望
0
输入生成
0
输出


速度方面,这应该比任何基于循环的解决方案都要快,并且与基于查找的解决方案相当(但不受大小限制,也不使用额外内存):


最终附加的位移位操作只需要几纳秒,因此在这里应该是不相关的。

对于您的用例来说,循环是可以接受的吗?
(number&2**number\u of_training\u zeres-1)
将给您一个布尔值,告诉您的数字中是否有一定数量的尾随零。@AravindSuresh无法右移。此外,它生成的是一个
int
,而不是
bool
。您是被限制为正整数还是也可以为负整数?@norok2我被限制为正整数请注意,内置的
divmod
等于
(x//y,x%y)
。利用这一点可能会大大提高性能,因为
%
/
都使用了divmod,但放弃了部分结果。我考虑的是
中的一些东西,而True:div,mod=divmod(n,2)
,嵌套分支
如果不是mod:n=div
其他:break
。即使使用赋值表达式,也无法保持这种简洁。由于这种方法无论如何都要比比特魔法慢,所以使用可读性更高的
%
/
操作实际上可能是最好的。@MisterMiyagi
divmod
可能会慢。@juanpa.arrivillaga确实如此。:/在Python中解压结果的额外支架是一个杀手。@mistermiagi它太糟糕了,速度太慢了。我一直认为divmod的全部目的是防止两次分割,从而节省一些CPU周期。@interjay这是我的用例的问题,因为我使用任意长精度,thanks@mikulatomas根据新要求添加了解决方案
(x^(x-1))。位长度()-1
避免了
0
的奇怪结果@TomKarzes我实际上发现0的大小写是特别的,因为它是严格未定义的(应该是0?无穷大?没有1可以跟踪…),但是你也可以使用你的表达式,我认为它实际上应该更快。@norok2对,但是使用
0
product
0
的优点是,您可以通过它进行右移,而不会产生错误。如果移位量为负数,则会出现错误。@TomKarzes我在代码中避免
0
,但谢谢。
for n in range(1, 20):
    r = get_number_of_trailing_zeroes(n)
    shifted = n >> r
    print(f'n: {n} bin: {bin(n)} zeroes: {r} shifted: {bin(shifted)}')
    assert 0 == get_number_of_trailing_zeroes(shifted)
n: 1 bin: 0b1 zeroes: 0 shifted: 0b1
n: 2 bin: 0b10 zeroes: 1 shifted: 0b1
n: 3 bin: 0b11 zeroes: 0 shifted: 0b11
n: 4 bin: 0b100 zeroes: 2 shifted: 0b1
n: 5 bin: 0b101 zeroes: 0 shifted: 0b101
n: 6 bin: 0b110 zeroes: 1 shifted: 0b11
n: 7 bin: 0b111 zeroes: 0 shifted: 0b111
n: 8 bin: 0b1000 zeroes: 3 shifted: 0b1
n: 9 bin: 0b1001 zeroes: 0 shifted: 0b1001
n: 10 bin: 0b1010 zeroes: 1 shifted: 0b101
n: 11 bin: 0b1011 zeroes: 0 shifted: 0b1011
n: 12 bin: 0b1100 zeroes: 2 shifted: 0b11
n: 13 bin: 0b1101 zeroes: 0 shifted: 0b1101
n: 14 bin: 0b1110 zeroes: 1 shifted: 0b111
n: 15 bin: 0b1111 zeroes: 0 shifted: 0b1111
n: 16 bin: 0b10000 zeroes: 4 shifted: 0b1
n: 17 bin: 0b10001 zeroes: 0 shifted: 0b10001
n: 18 bin: 0b10010 zeroes: 1 shifted: 0b1001
n: 19 bin: 0b10011 zeroes: 0 shifted: 0b10011
(x & -x).bit_length() - 1
(x ^ -x).bit_lenght() - 2
(x ^ (x - 1)).bit_length() - 1
for i in range(-15, 16): 
     print(
         f'{i:5b}',
         f'{i ^ -i:3d}',
         f'{(i & -i).bit_length() - 1:2d}',
         f'{(i ^ -i).bit_length() - 2:2d}',
         f'{(i ^ (i - 1)).bit_length() - 1:2d}') 
def trail_zero_loop(x):
    if x != 0:
        k = -1
        r = 0
        while not r:
            # x, r = divmod(x, 2)  # ~15% slower
            r = x % 2
            x = x // 2
            k += 1
        return k
    else:
        return -1
def trail_zero_and(x):
    return (x & -x).bit_length() - 1
def trail_zero_xor(x):
    if x != 0:
        # return (x ^ -x).bit_length() - 2  # ~1% slower
        return (x ^ (x - 1)).bit_length() - 1
    else:
        return -1
_LOOKUP =[ 32, 0, 1, 26, 2, 23, 27, 0, 3, 16, 24, 30, 28, 11, 0, 13, 4, 7, 17, 0, 25, 22, 31, 15, 29, 10, 12, 6, 0, 21, 14, 9, 5, 20, 8, 19, 18 ]


def trail_zero_lookup(x):
    if x != 0:
        return _LOOKUP[(x & -x) % 37]
    else:
        return -1
n = 2 ** 30
%timeit trail_zero_loop(n)
# 3.15 µs ± 25.4 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each) 
%timeit trail_zero_and(n)
# 228 ns ± 9.87 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit trail_zero_xor(n)
# 253 ns ± 7.25 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)
%timeit trail_zero_lookup(n)
# 294 ns ± 1.81 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)