Joel提到将一个字节中的设置位数作为编程问题计算在Guerrilla Guide to Interviewing中,并讨论了利用查找表中出现的模式的方法。在我找到模式之后,我写了一篇关于它的文章。
总结:
Number of bits set in a byte in 16x16
0 1 1 2 1 2 2 3 1 2 2 3 2 3 3 4
1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5
1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5
2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5
2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
3 4 4 5 4 5 5 6 4 5 5 6 5 6 6 7
1 2 2 3 2 3 3 4 2 3 3 4 3 4 4 5
2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
3 4 4 5 4 5 5 6 4 5 5 6 5 6 6 7
2 3 3 4 3 4 4 5 3 4 4 5 4 5 5 6
3 4 4 5 4 5 5 6 4 5 5 6 5 6 6 7
3 4 4 5 4 5 5 6 4 5 5 6 5 6 6 7
4 5 5 6 5 6 6 7 5 6 6 7 6 7 7 8
第一行和第二行完全相同,可以通过在该位置的行和列中添加第一个值来计算网格中的每个位置。因此,您只需要一个包含16个条目的查找表,用于8位数字,并且只能使用前16个数字。然后,如果您想计算数字243中的设置位,例如,您只需执行:
a = [0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4]
x = 243 / 16 => 15 # (int)
y = 243 % 16 => 3
a[x] + a[y] => 6
# Are there six bits set in the number 243?
243 = 11110011 # yep
之后我注意到的下一个模式是,每当你将NxN网格的大小加倍时,每个象限可以通过分别为每个象限添加0,1,1和2来计算,如下所示:
# Make a 4x4 grid on the paper, and fill in the upper left quadrant with the values of the 2x2 grid.
# For each quadrant, add the value from that same quadrant in the 2x2 grid to the array.
# Upper left quad add 0 to each number from 2x2
0 1 * *
1 2 * *
* * * *
* * * *
# Upper right quad add 1 to each number from 2×2
0 1 1 2
1 2 2 3
* * * *
* * * *
# Lower left quad add 1 to each number from 2×2
0 1 1 2
1 2 2 3
1 2 * *
2 3 * *
# Lower right quad add 2 to each number from 2×2
0 1 1 2
1 2 2 3
1 2 2 3
2 3 3 4
重复这个过程两次,你将从上面得到16x16网格,所以我认为必须有某种四叉树算法可以让你从网格开始:
0 1
1 2
并给出数字N,动态生成查找表并计算出位数。所以我的问题/挑战是,你能算出一个算法吗?
答案 0 :(得分:0)
这是一个愚蠢的问题!在第一个示例中,您计算了使用16项表而不是256项设置的位数并不是什么神奇的!您所做的就是计算字节前四位(第一个半字节)中设置的位数,然后计算第二个半字节中的位数,将两者相加。 x / 16是第一个半字节,x%16是第二个半字节。
如果您重复此过程,现在您有一个两位的查找表,您只需执行四次,每对一次。在极端情况下,您可以逐个添加所有位,然后得到明显的答案。
查找表的重点是避免添加。
答案 1 :(得分:0)
基于罗伯特的代码here,它甚至可以在没有除法或模数的情况下完成,将它们替换为一个班次和一个AND,如下所示:
a = [0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4]
x = 243 >> 4 # 15 (same as dividing by 16)
y = 243 & 0x0f # 3 ( same as modding by 16)
result = a[x] + a[y] # 6 bits set
或者在C:
const unsigned char oneBits[] = {0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4};
unsigned char CountOnes(unsigned char x)
{
unsigned char results;
results = oneBits[x&0x0f];
results += oneBits[x>>4];
return results
}
对于任何大小的整数,你可以循环遍历字节并进行快速查找,如下所示:
def bits(n)
a = [0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4]
a[n >> 4] + a[n & 0x0f]
end
def setBits(n)
total = 0
while(n > 0)
total += bits(n&0xff)
n >>= 8
end
total
end
setBits(6432132132165432132132165436265465465653213213265465) # 78 bits set
我对这个答案很满意。我知道更复杂的东西,四叉树式的效率不高,我只是认为这是一个体面的思想实验。
答案 2 :(得分:0)
原谅迟到的帖子,但我刚刚找到了挑战。我的$ .02(蛮力)
Private Sub Button1_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles Button1.Click
For x As Integer = 0 To 255
Debug.WriteLine(bitsOn2(CByte(x)) & " " & Convert.ToString(x, 2).PadLeft(8, "0"c))
Next
End Sub
Private Function bitsOn(ByVal aByte As Byte) As Integer
Dim aBit As Byte = 1
For z As Integer = 0 To 7
If (aByte >> z And aBit) = aBit Then bitsOn += 1
Next
End Function
Dim aDict As New Dictionary(Of Integer, Integer)
Private Function bitsOn2(ByVal aByte As Byte) As Integer
If aDict.Count = 0 Then 'init dictionary
For x As Integer = 0 To 255
aDict.Add(x, bitsOn(CByte(x)))
Next
End If
Return aDict(aByte)
End Function