Найдите все 2-битные значения, соответствующие другому двоичному шаблону, а затем просуммируйте их.

Первое значение:

У меня есть двоичное значение, которое на самом деле представляет собой компактную серию 2-битных значений. (То есть каждые 2 бита в двоичном значении представляют 0, 1, 2 или 3.) Таким образом, например, 0, 3, 1, 2 становится 00110110. В этой двоичной строке меня интересуют только 3 ( или, наоборот, я мог бы перевернуть биты и заботиться только о 0, если это облегчит ваш ответ). Все остальные числа не имеют значения (по причинам, которые мы рассмотрим чуть позже).

Второе значение:

У меня есть второе двоичное значение, которое также представляет собой сжатую серию 2-битных значений, представленных таким же образом. Он имеет ту же длину, что и первое значение.

Математика:

Мне нужна сумма 2-битных чисел во втором значении, которые имеют ту же позицию, что и 3 из первого значения. Другими словами, если у меня есть:

First:  11000011
Second: 01111101

Тогда мой ответ будет «2» (я добавил первое и последнее число из «Второго» вместе, потому что это были единственные, у которых было «11» в первом значении, которое им соответствовало.)

Я хочу сделать это за как можно меньшее количество тактов (либо на графическом процессоре, либо на архитектуре x86). Однако я обычно ищу алгоритм, а не решение на ассемблере. Есть ли способ быстрее, чем маскировать два бита за раз от каждого числа и запускать несколько циклов?


person Max Kanat-Alexander    schedule 12.05.2012    source источник
comment
Вы имели в виду «3» вместо «4» для 11?   -  person Julien Lebosquain    schedule 12.05.2012


Ответы (1)


Конечно.

 // the two numbers
 unsigned int a;
 unsigned int b;

Теперь создайте маску из a, которая содержит бит «1» в нечетной позиции, только если в a было «11», заканчивающееся в той же позиции.

 unsigned int mask = a & (a >> 1) & 0x55555555;

Разверните его, чтобы вернуть шаблон «11»:

 mask = mask | (mask << 1);

Так что теперь, если a был 1101100011, маска 1100000011.

Затем замаскируйте b маской:

 b = b & mask;

Затем вы можете выполнить сложение (замаскированных) чисел из b параллельно:

 b = (b & 0x33333333) + ((b & 0xcccccccc) >> 2);
 b = (b & 0x0f0f0f0f) + ((b & 0xf0f0f0f0) >> 4);
 b = (b & 0x00ff00ff) + ((b & 0xff00ff00) >> 8);
 b = (b & 0x0000ffff) + ((b & 0xffff0000) >> 16);

Для 32-битного числа сумма теперь находится в младших битах b. Это общеизвестный шаблон для параллельного добавления битовых полей. Для более чем 32-битных чисел вы должны добавить еще один раунд для 64-битных чисел и два раунда для 128-битных чисел.

person Antti Huima    schedule 12.05.2012
comment
Это фантастика, спасибо! :-) Я не был полностью уверен в первой части, в частности, в том, как сгенерировать маску, но это выглядит просто и быстро. :-) - person Max Kanat-Alexander; 13.05.2012