返回Python中最低有效位的索引

时间:2011-04-02 02:10:41

标签: python bit-manipulation

C ++有一组函数ffs(),ffsl()和ffsll(),它们返回在给定二进制整数中设置的最低有效位。

我想知道Python中是否已经存在等效函数。我没有看到一个用于bitarray的描述,但也许还有另一个。我希望通过循环遍历所有可能的位掩码来避免计算答案,当然这是最后的选择; ffs()只返回一个整数,我想知道Python中可比的东西。

9 个答案:

答案 0 :(得分:18)

仅限于Pythons 2.7和3.1及以上版本:

def ffs(x):
    """Returns the index, counting from 0, of the
    least significant set bit in `x`.
    """
    return (x&-x).bit_length()-1

示例:

>>> ffs(136)
3

答案 1 :(得分:6)

它可以在GNU Multi-Precision库的gmpy包装器中找到。在我的系统上,它比ctypes解决方案快4倍。

>>> import gmpy
>>> gmpy.scan1(136)
3
>>> bin(136)
'0b10001000'

答案 2 :(得分:5)

可以使用ctypes模块从共享库(Windows用户的DLL)加载函数。我能够在Ubuntu 10.10上的ffs()中包含的C标准库中加载libc.so.6函数:

>>> import ctypes
>>> libc = ctypes.cdll.LoadLibrary('libc.so.6')
>>> libc.ffs(136)
4

(请注意,这使用基于1的索引)。显然,这不是原始的跨平台兼容;您需要根据您正在运行的系统(从sys.platform或类似情况检测到)更改要加载的库的文件名。我甚至不会100%确定它在不同的Linux发行版上是一样的。

还值得做一些适当的基准测试,看看它是否真的值得。如果经常调用它,但如果它只是偶尔使用,那么与维护相比,Python实现的性能优势可能会微不足道,以确保它能够在不同的平台上运行。

另一种方法是在C中编写自己的函数实现,并提出一个Python包装器。然后,您必须为您想要的每个平台编译它,但是您在找到正确的库名称的同时保留速度优势的麻烦。

答案 3 :(得分:4)

所有这些答案都与外部模块,定义函数等有关...按位操作???

(1 + (x ^ (x-1))) >> 1

将在x中返回2的最小有效幂。

例如,x = 136时,答案为2 ^ 3 = 8。

Trick记住x-1与x的位数相同,除了所有最不重要的1和所有后续的零;然后执行按位XOR bitwin X和X + 1提取这些数字。

然后,您可以使用bit_length()方法提取索引。

答案 4 :(得分:3)

为了充实S.Lott的评论,如果设置了LSB,则该值将为奇数,如果清除,则该值将为偶数。因此,我们可以继续将价值向右移动,直到它变得奇怪,跟踪发生这种情况需要多少班次。只记得检查先设置一个位,否则当给定值为0时,循环将变为无穷无尽...

>>> def ffs(num):
...     # Check there is at least one bit set.
...     if num == 0:
...         return None
...     # Right-shift until we have the first set bit in the LSB position.
...     i = 0
...     while (num % 2) == 0:
...         i += 1
...         num = num >> 1
...     return i
...
>>> num = 136
>>> bin(num)
'0b10001000'
>>> ffs(num)
3
>>> ffs(0) is None
True

注意,这会将LSB视为位0;如果你想要基于1的索引,只需将i初始化为1。

答案 5 :(得分:1)

您可以实现此处标识的任何算法: http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightLinear

我不知道有任何本地方法可以这样做。 (你也可以编写一个扩展来将C函数导出到Python,但这可能不值得麻烦: - )

答案 6 :(得分:1)

尝试积极优化Python代码有点傻,所以一个带有计数器和右移的简单for循环应该没问题。如果你想要更快(在C,Java或汇编中更有意义),你可以二进制搜索最右边的1位甚至使用查找表来帮助你。

假设x是64位,你想要LSB。掩盖低32位。假设x非零:

if x & 0xffffffff == 0:
  if x & 0xffff00000000 == 0:
    # the LSB is in the highest two bytes
  else:
    # the LSB is in the 5th or 6th byte
else:
  if x & 0xffff0000:
    # the LSB is in the 3rd or 4th byte
  else:
    # the LSB is in the 1st or 2nd byte

您如何处理上面评论的部分取决于您想要的积极程度:您可以进行类似于我们所拥有的进一步二进制搜索,或者您可以使用查找表。目前,我们有16位的不确定性,因此我们的表将是65,536个条目。我实际上已经为这个极其性能敏感的代码创建了这样的表,但这是一个玩Chess的C程序(64位字符串有一个二进制表示的板)。

答案 7 :(得分:1)

我知道已经有一个选定的答案,但我自己也解决了这个问题,因为我可以。该解决方案基于以下思想:如果值为2的幂,则可以使用对数基数2来查找其位置。实现的其余部分围绕转换值,以便我们可以简单地记录日志。

我没有对它进行基准测试,但转换是O(1),其中n是位数(这个视图有点不公平地忽略了log()引入的复杂性,尽管它可能在O(log(n)附近))?1)。实施基于2的'递减和比较'两种权力方法:

import math

def ffs(value):
    """Find the first set bit in an integer, returning its index (from zero)"""
    if 0 > value:
        raise ValueError("No negative values!")
    if 0 == value:
        # No bits are set in value
        return None
    if (value & (value - 1)) != 0:
        # Multiple bits are set in value. Transform value such that only the
        # lowest bit remains set.
        value &= ~ (value - 1)
    # Only one bit is set in value, find its index from zero
    return int(math.log(value, 2))

答案 8 :(得分:0)

from math import log2
def bit_pos(n):
    """Returns the index, counting from 0, of the
    least significant set bit in `n`. If no bit is set, returns None.
    """   
    return int(log2(n^(n&(n-1)))) if n else None

bit_pos(32)
# returns 5