C ++有一组函数ffs(),ffsl()和ffsll(),它们返回在给定二进制整数中设置的最低有效位。
我想知道Python中是否已经存在等效函数。我没有看到一个用于bitarray的描述,但也许还有另一个。我希望通过循环遍历所有可能的位掩码来避免计算答案,当然这是最后的选择; ffs()只返回一个整数,我想知道Python中可比的东西。
答案 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