Самый быстрый способ вычисления суммы битов в массиве байтов
У меня есть два байтовых массива одинаковой длины. Мне нужно выполнить операцию XOR между каждым байтом и после этого вычислить сумму битов.
например:
11110000^01010101 = 10100101 -> so 1+1+1+1 = 4
Мне нужно сделать ту же операцию для каждого элемента в массив байтов.
9 ответов
используйте таблицу подстановки. После 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)
в реальной жизни, но увидеть, что работает лучше для вас.
Также обратите внимание на тип the xor
переменная как int
. Фактически, нет оператора XOR, определенного для byte
значения, и если вы сделали xor
a byte
он все равно будет компилироваться из - за природы составных операторов присваивания, но он будет выполнять приведение на каждой итерации-по крайней мере, в IL. Вполне возможно, что JIT позаботится об этом, но нет необходимости даже просить его:)
самый быстрый способ, вероятно, будет 256-элементной таблицей поиска...
int[] lut
{
/*0x00*/ 0,
/*0x01*/ 1,
/*0x02*/ 1,
/*0x03*/ 2
...
/*0xFE*/ 7,
/*0xFF*/ 8
}
например
11110000^01010101 = 10100101 -> lut[165] == 4
это чаще называют подсчетом битов. Для этого существуют буквально десятки различных алгоритмов. здесь - Это один сайт, в котором перечислены несколько наиболее известных методов. Есть даже конкретные инструкции CPU для этого.
теоретически Microsoft может добавить BitArray.CountSetBits
функция, которая получает JITed с лучшим алгоритмом для этой архитектуры процессора. Я, например, был бы рад такому добавлению.
Как я понял, вы хотите суммировать биты каждого 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?
}
Я не уверен, имеете ли вы в виду сумму байтов или битов. Чтобы суммировать биты в байте, это должно работать:
int nSum = 0;
for (int i=0; i<=7; i++)
{
nSum += (byte_val>>i) & 1;
}
тогда вам понадобится xoring и массив, который, конечно же, будет вокруг этого.
следует сделать
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;
}
его можно переписать как 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;
}
общая функция для подсчета битов может выглядеть так:
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;
}
таблица поиска должна быть самой быстрой, но если вы хотите сделать это без таблицы поиска, это будет работать для байтов всего за 10 операций.
public static int BitCount(byte value) {
int v = value - ((value >> 1) & 0x55);
v = (v & 0x33) + ((v >> 2) & 0x33);
return ((v + (v >> 4) & 0x0F));
}
это байтовая версия общей функции подсчета битов, описанной в сайт Шона Эрона Андерсона.