HACKMEM имеет этот алгоритм в 3 операциях (грубо переводится в C):
bits = (c * 01001001001ULL & 042104210421ULL) % 017;
(ULL
- форсировать 64-битную арифметику. Это нужно, просто ... для вычисления требуется 33-битное целое число.)
На самом деле, вы можете заменить вторую константу на 042104210021ULL
, поскольку вы рассчитываете только 8 битов, но она не выглядит так хорошо симметрично.
Как это работает? Подумайте о c
побитовым образом и помните, что (a + b) % c = (a % c + b % c) % c
и (a | b) == a + b
тогда и только (a & b) == 0
.
(c * 01001001001ULL & 042104210421ULL) % 017
01 01001001001 01 1
02 02002002002 02000000000 1
04 04004004004 04000000 1
010 010010010010 010000 1
020 020020020020 020 1
040 040040040040 040000000000 1 # 040000000000 == 2 ** 32
0100 0100100100100 0100000000 1
0200 0200200200200 0200000 1
Если у вас нет 64-битной арифметики, вы можете разделить c
на кусочки и делать каждую половину, выполняя 9 операций. Для этого требуется всего 13 бит, поэтому сработает 16- или 32-битная арифметика.
bits = ((c & 017) * 0421 & 0111) % 7 + ((c >> 4) * 0421 & 0111) % 7;
(c * 0421 & 01111) % 7
1 0421 01 1
2 01042 01000 1
4 02104 0100 1
8 04210 010 1
Например, если c == 105 == 0b11001001
,
c == 0100
| 040
| 010
| 01 == 0151
* 01001001001001ULL == 0100100100100
| 040040040040
| 010010010010
| 01001001001 == 0151151151151
& 0421042104210421ULL == 0100000000
| 04000000000
| 010000
| 01 == 04100010001
% 017 == 4
c & 017 == 8 | 1 == 011
011 * 0421 == 8 * 0421 | 1 * 0421 == 04210 | 0421 == 04631
04631 & 0111 == 04210 & 0111 | 0421 & 0111 == 010 | 01 == 011
011 % 7 == 2
c >> 4 == 4 | 2 == 06
06 * 0421 == 4 * 0421 | 2 * 0421 == 02104 | 01042 == 03146
03146 & 0111 == 02104 & 0111 | 01042 & 0111 == 0100 | 01000 == 01100
01100 % 7 == 2
2 + 2 == 4