简体   繁体   English

计算基数为 2 的尾随零数的 Pythonic 方法

[英]Pythonic way to count the number of trailing zeros in base 2

I'm looking for a Pythonic way to count the number of trailing zeros in the binary representation of a positive integer n (which will indicate the highest power of 2 which divides n without remainder).我正在寻找一种 Pythonic 方法来计算正整数n的二进制表示中尾随零的数量(这将表示2的最高幂,它将n整除而没有余数)。

A simple solution:一个简单的解决方案:

def CountZeros(n):
    c = 0
    while (n % 2) == 0:
        n /= 2
        c += 1
    return c

But in order to do it in a more Pythonic manner, I think that I can make use of:但为了以更 Pythonic 的方式做到这一点,我认为我可以利用:

  • bin(n)[2:] , which gives the binary representation of n bin(n)[2:] ,给出了n的二进制表示
  • bin(n)[:1:-1] , which gives the reversed binary representation of n bin(n)[:1:-1] ,它给出了n的反向二进制表示

So my question can be reduced to counting the number of trailing zeros in a string.所以我的问题可以简化为计算字符串中尾随零的数量。

Is there any single-statement way to do this?有没有任何单语句方法来做到这一点?

My ultimate goal is a Pythonic way for computing the highest power of 2 which divides n without remainder, so any ways to do this not by counting the trailing zeros in a string are also appreciated.我的最终目标是一种 Pythonic 计算2的最高幂的方法,它将n整除而没有余数,因此任何不通过计算字符串中的尾随零来实现此目的的方法也值得赞赏。

You could use str.rstrip :你可以使用str.rstrip

def trailing(s):
    return len(s) - len(s.rstrip('0'))

It's twice as fast to avoid converting to string with bin , instead using a modified bithack , since we already have an efficient log2 implementation.避免使用bin转换为 string 的速度是原来的两倍,而是使用修改后的bithack ,因为我们已经有了一个高效的log2实现。

def ctz(v):
    return (v & -v).bit_length() - 1

The above returns -1 if the input is 0.如果输入为 0,则上述返回 -1。

Using C makes it twice as fast again:使用 C 使其速度再次提高一倍:

from gmpy2 import bit_scan1 as ctz

This version returns None if input is zero.如果输入为零,则此版本返回 None。


As an example, consider the infinite binary expansion if the input is 20:例如,如果输入为 20,请考虑无限二进制展开:

v    000...00010100
~v   111...11101011 (not used directly, all bits opposite)
-v   111...11101100 (-v == ~v + 1; this causes all low 1 bits to overflow and carry)
v&-v 000...00000100 (has a single 1 bit, from the carry)

When the are and ed, all the leading zero and one bits are opposite, but the last 1 bit and all trailing zeros are the same both ways.当 are and ed 时,所有前导零和一位都是相反的,但最后一位和所有尾随零在两种情况下都相同。

Then .bit_length() tells us the integers is using 3 bits total, so just subtract 1 to only consider the zeros.然后.bit_length()告诉我们整数总共使用了 3 位,因此只需减去 1 即可仅考虑零。

This might do.这可能会。

def trailing_zeros(n):
    s = str(n)
    return len(s)-len(s.rstrip('0'))

I'm not sure if this is the fastest solution, but it look like the most logical to me:我不确定这是否是最快的解决方案,但对我来说它看起来最合乎逻辑:

def trailing_zeros(n):
    for i in range(20):
        if n % (2<<i) != 0:
            return i

Since you asked for a single-line statement, here's one, but I don't think it's very legible (and its efficiency is worse than the other one):既然你要求单行语句,这里是一个,但我认为它不是很清晰(而且它的效率比另一个更差):

max(i+1 for i in range(20) if n%(2<<i) == 0)

Definitely use bit-pushing operations, if you are concerned specifically with the underlying binary representation of the number.如果您特别关注数字的底层二进制表示,请务必使用位推送操作。 Divide and modulo-divide are the most costly operations, and relate to arithmetic not hardware bits.除法和模除法是成本最高的运算,涉及算术而不是硬件位。 So (untested code)所以(未经测试的代码)

def fnzb( n):
    " return position of first non-zero bit in n"
    if n==0:
        # edge case, there ARE no nonzero bits
        return None
    for po2 in range(0, 128) # or whatever larger upper limit is desired
        if a & ( 1 << po2) != 0: return po2
    # edge case, n too large
    raise ValueError,  "'impossibly' large integer argument encountered"

If these integers might often be extremely large with very many trailing zeros (for cryptographical values of "large") it might make an important difference to efficiency to initialize test=1 and right-shift it one place every trip around the loop, rather than starting with 1 and shifting it po2 places.如果这些整数可能通常非常大并且带有很多尾随零(对于“大”的加密值),那么初始化test=1并在每次循环中将其右移一个位置可能会对效率产生重要影响,而不是从 1 开始并将其移动到po2位置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM