我有点计数方法,我想尽快做出来。我想从Bit Twiddling Hacks尝试下面的算法,但我不知道C.什么是'type T',什么是python等价的(T)〜(T)0/3?
最佳位的概括 计算整数的方法 位宽高达128(参数化为 类型T)是这样的:
v = v - ((v >> 1) & (T)~(T)0/3); // temp
v = (v & (T)~(T)0/15*3) + ((v >> 2) & (T)~(T)0/15*3); // temp
v = (v + (v >> 4)) & (T)~(T)0/255*15; // temp
c = (T)(v * ((T)~(T)0/255)) >> (sizeof(v) - 1) * CHAR_BIT; // count
答案 0 :(得分:7)
T是整数类型,我假设它是无符号的。由于这是C,它将是固定宽度,可能(但不一定)是8,16,32,64或128中的一个。在该代码示例中重复出现的片段(T)~(T)0
仅给出值2 * * N-1,其中N是类型T的宽度。我怀疑代码可能要求N是8的倍数
为了正确操作。
这是将给定代码直接转换为Python,以N为参数化,以位为单位的宽度。
def count_set_bits(v, N=128):
mask = (1 << N) - 1
v = v - ((v >> 1) & mask//3)
v = (v & mask//15*3) + ((v >> 2) & mask//15*3)
v = (v + (v >> 4)) & mask//255*15
return (mask & v * (mask//255)) >> (N//8 - 1) * 8
注意事项:
(1)以上仅适用于最多2 ** 128的数字。不过,你可能可以将它推广到更大的数字。
(2)明显效率低下:例如,'mask // 15'计算两次。当然,这对于C来说并不重要,因为编译器几乎肯定会在编译时而不是运行时进行除法,但是Python的窥孔优化器可能不那么聪明。
(3)最快的C方法可能无法转换为最快的Python方法。对于Python速度,您可能应该寻找一种最小化Python按位运算次数的算法。正如Alexander Gessler所说:简介!
答案 1 :(得分:2)
您复制的内容是用于生成代码的模板。将该模板音译为另一种语言并期望它快速运行并不是一个好主意。我们来扩展模板。
(T)〜(T)0表示“与T型匹配的1位”。该算法需要4个掩码,我们将根据我们可能感兴趣的各种T大小计算这些掩码。
>>> for N in (8, 16, 32, 64, 128):
... all_ones = (1 << N) - 1
... constants = ' '.join([hex(x) for x in [
... all_ones // 3,
... all_ones // 15 * 3,
... all_ones // 255 * 15,
... all_ones // 255,
... ]])
... print N, constants
...
8 0x55 0x33 0xf 0x1
16 0x5555 0x3333 0xf0f 0x101
32 0x55555555L 0x33333333L 0xf0f0f0fL 0x1010101L
64 0x5555555555555555L 0x3333333333333333L 0xf0f0f0f0f0f0f0fL 0x101010101010101L
128 0x55555555555555555555555555555555L 0x33333333333333333333333333333333L 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0f0fL 0x1010101010101010101010101010101L
>>>
您会注意到为32位大小写生成的掩码与硬编码的32位C代码中的掩码相匹配。实现细节:从32位掩码(Python 2.x)中丢失L
后缀,并丢失Python 3.x的所有L
后缀。
正如你所看到的那样,整个模板和(T)〜(T)0 caper仅仅是混淆的诡辩。简而言之,对于k字节类型,您需要4个掩码:
k bytes each 0x55
k bytes each 0x33
k bytes each 0x0f
k bytes each 0x01
并且最终的移位仅仅是N-8(即8 *(k-1))个比特。旁白:我怀疑模板代码是否真的可以在CHAR_BIT不是8的机器上运行,但是现在这些机器的数量并不多。
更新:将此类算法从C语音转换为Python时,还有另一点会影响正确性和速度。 C算法通常假设无符号整数。在C中,对无符号整数的操作以模2 * N静默工作。换句话说,仅保留最低有效N位。没有溢出异常。许多有点笨拙的算法依赖于此。但是(a)Python的int
和long
已签名(b)旧的Python 2.X将引发异常,最近的Python 2.X将默默地将int
提升为long
和Python 3.x int
== Python 2.x long
。
在Python代码中,正确性问题通常至少需要register &= all_ones
次。通常需要仔细分析以确定最小的正确掩蔽。
使用long
代替int
并不能提高效率。您会注意到,即使输入long
,32位算法也会返回0
个答案,因为32位all_ones是long
。