详解一种高效位反转算法
位反转
这里的位反转(Bit Reversal),指的是一个数的所有bit位依照中点对换位置,例如0b0101 0111 => 0b1110 1010。也可以叫二进制逆序,按位逆序,位翻转等等。
算法原理
高效位反转算法原理:算法运用了分治法(divide and conquer),以两个bit位一组,对调相邻的bit位;然后再以4个bit位为一组,分成左边两个bit位一段和右边两个bit位一段,然后这两段相互对调;然后再以8个bit位为一组,以此类推,最后完成位反转。
32位数的高效位反转算法实现
下面举例一个32位数的高效位反转算法代码:
unsigned int reverse(unsigned int x)
{
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
return((x >> 16) | (x << 16));
}
下面进行逐行代码分析:
首先,我们要进行位反转的是一个32位数,如下图所示
分析第一行代码:
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
-
0xaaaaaaaa
0xaaaaaaaa 是从第0位开始,所有奇数位为1
0xaaaaaaaa = 0b10101010101010101010101010101010
-
x & 0xaaaaaaaa
x & 0xaaaaaaaa的结果如下图所示:
(x & 0xaaaaaaaa) >> 1
右移一位后结果如下图所示:
-
0x55555555
0x55555555是从第0位开始,所有偶数位为1
0x55555555 = 0b01010101010101010101010101010101
-
x & 0x55555555
x & 0x55555555的结果如下图所示:
-
(x & 0x55555555) << 1
左移一位的结果如下图所示:
x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
然后两个数或运算,结果如下图所示:
第一行代码运算完成。
总结,x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
这行的代码就是以两个bit位一组,对调相邻的bit位。
图解就是将下图
转换成
分析第二行代码:
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
目前x的数值为:
-
0xcccccccc
0xcccccccc是从第0位开始,0和1每隔两位交替出现0
xcccccccc = 0b11001100110011001100110011001100 -
x & 0xcccccccc
x & 0xcccccccc的结果如下图所示:
-
(x & 0xcccccccc) >> 2
右移两位后结果如下图所示:
-
0x33333333
0x33333333是从第0位开始,1和0每隔两位交替出现
0x33333333 = 0b00110011001100110011001100110011 -
x & 0x33333333
x & 0x33333333的结果如下图所示:
(x & 0x33333333) << 2
左移两位后结果如下图所示:
x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
然后两个数或运算,结果如下图所示:
第二行代码运算完成。
总结,x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
这行的代码就是以4个bit位为一组,分成左边两个bit位一段和右边两个bit位一段,然后这两段相互对调。
图解就是将下图
转换成
分析第三行代码:
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
目前x的数值为:
-
0xf0f0f0f0
0xf0f0f0f0是从第0位开始,0和1每隔四位交替出现
0xf0f0f0f0= 0b11110000111100001111000011110000 -
x & 0xf0f0f0f0
x & 0xf0f0f0f0的结果如下图所示:
-
(x & 0xf0f0f0f0) >> 4
右移四位后结果如下图所示:
-
0x0f0f0f0f
0x0f0f0f0f是从第0位开始,1和0每隔四位交替出现
0x0f0f0f0f= 0b00001111000011110000111100001111
-
x & 0x0f0f0f0f
x & 0x0f0f0f0f的结果如下图所示:
-
(x & 0x0f0f0f0f) << 4
左移四位后结果如下图所示:
-
x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
然后两个数或运算,结果如下图所示:
第三行代码运算完成。
总结,x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
这行的代码就是以8个bit位为一组,分成左边四个bit位一段和右边四个bit位一段,然后这两段相互对调。
图解就是将下图
转换成
分析第四行代码:
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
目前x的数值为:
-
0xff00ff00
0xff00ff00是从第0位开始,0和1每隔八位交替出现
0xff00ff00= 0b11111111000000001111111100000000
-
x & 0xff00ff00
x & 0xff00ff00的结果如下图所示:
-
(x & 0xff00ff00) >> 8
右移八位后结果如下图所示:
-
0x00ff00ff
0x00ff00ff是从第0位开始,1和0每隔八位交替出现
0x00ff00ff= 0b00000000111111110000000011111111
-
x & 0x00ff00ff
x & 0x00ff00ff的结果如下图所示:
-
(x & 0x00ff00ff) << 8
左移八位后结果如下图所示:
-
x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
然后两个数或运算,结果如下图所示:
第四行代码运算完成。
总结,x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
这行的代码就是以16个bit位为一组,分成左边八个bit位一段和右边八个bit位一段,然后这两段相互对调。
图解就是将下图
转换成
分析最后一行代码:
((x >> 16) | (x << 16))
目前x的数值为:
高16bit与低16bit进行交换,结果如下图:
完成整个位反转算法。
8位数的高效位反转算法实现
如果要对8位数进行位反转,原理相同,代码如下:
unsigned char reverse(unsigned char x)
{
x = (((x & 0xaa) >> 1) | ((x & 0x55) << 1));
x = (((x & 0xcc) >> 2) | ((x & 0x33) << 2));
return ((x >> 4) | (x << 4));
}
即可实现对8位数的高效位反转。
[参考资料]
[Hacker’s Delight] 作者: Henry S. Warren Jr.
The Aggregate Magic Algorithms