我有两个长度相同的字节数组。我需要在每个字节之间执行XOR运算,然后计算位数之和。
例如:
11110000^01010101 = 10100101 -> so 1+1+1+1 = 4
我需要对字节数组中的每个元素执行相同的操作。
答案 0 :(得分:11)
使用查找表。 XORing后只有256个可能的值,所以它不会花费很长时间。与izb的解决方案不同,我不建议手动输入所有值 - 在启动时使用其中一个循环答案计算查找表一次。
例如:
public static class ByteArrayHelpers
{
private static readonly int[] LookupTable =
Enumerable.Range(0, 256).Select(CountBits).ToArray();
private static int CountBits(int value)
{
int count = 0;
for (int i=0; i < 8; i++)
{
count += (value >> i) & 1;
}
return count;
}
public static int CountBitsAfterXor(byte[] array)
{
int xor = 0;
foreach (byte b in array)
{
xor ^= b;
}
return LookupTable[xor];
}
}
(你可以使它成为一种扩展方法,如果你真的想...)
请注意byte[]
方法中CountBitsAfterXor
的使用 - 您可以使其成为IEnumerable<byte>
以获得更多的通用性,但迭代数组(这是在编译时已知是一个数组)会更快。可能只是在显微镜下更快,但是,嘿,你要求最快的方式:)
我几乎肯定实际将其表达为
public static int CountBitsAfterXor(IEnumerable<byte> data)
在现实生活中,但看哪哪个更适合你。
另请注意xor
变量的类型为int
。事实上,没有为byte
值定义XOR运算符,如果你使xor
成为byte
,由于复合赋值运算符的性质,它仍会编译,但它会执行在每次迭代中投射 - 至少在IL中。 JIT很可能会解决这个问题,但是没有必要甚至要求它:)
答案 1 :(得分:9)
最快的方式可能是256个元素的查找表...
int[] lut
{
/*0x00*/ 0,
/*0x01*/ 1,
/*0x02*/ 1,
/*0x03*/ 2
...
/*0xFE*/ 7,
/*0xFF*/ 8
}
e.g。
11110000^01010101 = 10100101 -> lut[165] == 4
答案 2 :(得分:6)
这通常被称为比特计数。实际上有几十种不同的算法。 Here是一个列出一些更为人熟知的方法的网站。甚至还有CPU特定的指令来执行此操作。
从理论上讲,Microsoft可以添加一个BitArray.CountSetBits
函数,该函数可以使用针对该CPU架构的最佳算法进行JITed。举个例子,我会欢迎这样的补充。
答案 3 :(得分:3)
据我所知,你想要对左右字节之间每个XOR的位进行求和。
for (int b = 0; b < left.Length; b++) {
int num = left[b] ^ right[b];
int sum = 0;
for (int i = 0; i < 8; i++) {
sum += (num >> i) & 1;
}
// do something with sum maybe?
}
答案 4 :(得分:2)
我不确定你的意思是总和字节还是比特。 要对一个字节内的位求和,这应该有效:
int nSum = 0;
for (int i=0; i<=7; i++)
{
nSum += (byte_val>>i) & 1;
}
然后你需要xoring,并且当然要围绕它进行数组循环。
答案 5 :(得分:1)
以下内容应该
int BitXorAndSum(byte[] left, byte[] right) {
int sum = 0;
for ( var i = 0; i < left.Length; i++) {
sum += SumBits((byte)(left[i] ^ right[i]));
}
return sum;
}
int SumBits(byte b) {
var sum = 0;
for (var i = 0; i < 8; i++) {
sum += (0x1) & (b >> i);
}
return sum;
}
答案 6 :(得分:1)
可以将其重写为ulong
并使用unsafe
指针,但byte
更容易理解:
static int BitCount(byte num)
{
// 0x5 = 0101 (bit) 0x55 = 01010101
// 0x3 = 0011 (bit) 0x33 = 00110011
// 0xF = 1111 (bit) 0x0F = 00001111
uint count = num;
count = ((count >> 1) & 0x55) + (count & 0x55);
count = ((count >> 2) & 0x33) + (count & 0x33);
count = ((count >> 4) & 0xF0) + (count & 0x0F);
return (int)count;
}
答案 7 :(得分:0)
计算位的一般函数可能如下所示:
int Count1(byte[] a)
{
int count = 0;
for (int i = 0; i < a.Length; i++)
{
byte b = a[i];
while (b != 0)
{
count++;
b = (byte)((int)b & (int)(b - 1));
}
}
return count;
}
1位越少,效果越快。它只是循环遍历每个字节,并切换该字节的最低1位,直到字节变为0.必须使用强制转换,以便编译器停止抱怨类型扩展和缩小。
然后可以使用以下方法解决您的问题:
int Count1Xor(byte[] a1, byte[] a2)
{
int count = 0;
for (int i = 0; i < Math.Min(a1.Length, a2.Length); i++)
{
byte b = (byte)((int)a1[i] ^ (int)a2[i]);
while (b != 0)
{
count++;
b = (byte)((int)b & (int)(b - 1));
}
}
return count;
}
答案 8 :(得分:0)
查找表应该是最快的,但是如果你想在没有查找表的情况下这样做,这将只适用于10个操作中的字节。
public static int BitCount(byte value) {
int v = value - ((value >> 1) & 0x55);
v = (v & 0x33) + ((v >> 2) & 0x33);
return ((v + (v >> 4) & 0x0F));
}
这是Sean Eron Anderson's bit fiddling site所述的通用位计数功能的字节版本。