[算法]有趣的《Bit Twiddling Hacks》字节位反转
给定一个字节input
,定义其二进制为abcd efgh
,将其比特位进行反转。
1. 乘+与+取模
公式:
r
e
v
e
r
t
e
d
=
(
i
n
p
u
t
∗
0
x
0202020202
U
L
L
&
0
x
010884422010
U
L
L
)
%
1023
reverted = (input \ * \ 0x0202020202ULL \ \& \ 0x010884422010ULL) \ \% \ 1023
reverted=(input ∗ 0x0202020202ULL & 0x010884422010ULL) % 1023
将input
的二进制abcd efgh
代入展开如下:
abcd efgh (-> hgfe dcba)
* 10 0000 0010 0000 0010 0000 0010 0000 0010 (0x0202020202)
-------------------------------------------------------
a bcde fgha bcde fgha bcde fgha bcde fgha bcde fgh0
& 1 0000 1000 1000 0100 0100 0010 0010 0000 0001 0000 (0x010884422010)
-------------------------------------------------------
a 0000 f000 b000 0g00 0c00 00h0 00d0 0000 000e 0000
% 1111 1111 (1023)
-------------------------------------------------------
00 000e 0000
00 h000 d000
00 0g00 0c00
00 00f0 00b0
00 0000 000a
-------------------------------------------------------
hgfe dcba
1.1 第一步:input * 0x0202020202ULL
将abcd efgh
复制5份,并依次铺开在不同位置。得到000a bcde fgha bcde fgha bcde fgha bcde fgha bcde fgh0
。
1.2 第二步:第一步结果 & 0x010884422010ULL
将 a bcde fgha bcde fgha bcde fgha bcde fgha bcde fgh0
分为10bit一组为 000000000a bcdefghabc defghabcde fghabcdefg habcdefgh0
,
找出各组从第1位到第8位以次对应abcdefgh的位置:
000000000a bcdefghabc defghabcde fghabcdefg habcdefgh0
1 6 2 8 3 7 4 5
------------------------------------------------------
0000000001 0000100010 0001000100 0010001000 0000010000
0000000001 0000100010 0001000100 0010001000 0000010000
即为0x010884422010ULL,通过&运算就将abcdefgh
各bit分离出来了。
1.3 第三部:第二步结果 % 1023
首先,了解两个公式:
-
x n ≡ 1 ( m o d x − 1 ) x^n ≡ 1 (mod\ x-1) xn≡1(mod x−1)
比如100 ≡ 1 (mod 9),1000 ≡ 1 (mod 9)。
-
y ≡ s u m ( a ∗ x n ) ≡ s u m ( a ) ( m o d x − 1 ) y ≡ sum(a*x^n) ≡ sum (a) (mod\ x-1) y≡sum(a∗xn)≡sum(a)(mod x−1)。(sum:求和)
比如300 ≡ (100 + 200) ≡ 1 + 2 (mod 9) ≡ 3 (mod 9)。
所以对1023取模,可以这样理解:
y ≡ 0000000000a 0000f000b0 000g000c00 00h000d000 00000e0000
≡ a*x^4 + 0000f000b0*x^3 + 000g000c00*x^2 + 00h000d000*x +00000e0000
≡ a + 0000f000b0 + 000g000c00 + 00h000d000 + 00000e0000 (mod x - 1)
≡ 000000000a
+ 0000f000b0
+ 000g000c00
+ 00h000d000
+ 00000e0000 (mod x - 1)
≡ 00hgfedcba (mod x - 1)
其中x为1024,即 2 10 2^{10} 210,所以x-1即为1023。通过对1023取模,将第二步按照10bit分组出来的数刚好得到了000000000a、0000f000b0、000g000c00、00h000d000、00000e0000五个数,五个分数相加,刚好等效于位或的效果!
2. 乘+与+乘+移位
公式:
r
e
v
e
r
t
e
d
=
(
(
i
n
p
u
t
∗
0
x
80200802
U
L
L
)
&
0
x
0884422110
U
L
L
)
∗
0
x
0101010101
U
L
L
>
>
32
reverted = ((input\ * \ 0x80200802ULL)\ \& \ 0x0884422110ULL) \ *\ 0x0101010101ULL >> 32
reverted=((input ∗ 0x80200802ULL) & 0x0884422110ULL) ∗ 0x0101010101ULL>>32
原理看这里:Reverse the bits in a byte with 4 operations (64-bit multiply, no division):