学习笔记 详解一种高效位反转算法

位反转

这里的位反转(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位数,如下图所示

32位数

分析第一行代码:

x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
第一行代码

  1. 0xaaaaaaaa

    0xaaaaaaaa 是从第0位开始,所有奇数位为1

    0xaaaaaaaa = 0b10101010101010101010101010101010

  2. x & 0xaaaaaaaa
    x & 0xaaaaaaaa的结果如下图所示:

x&0xaaaaaaaa

  1. (x & 0xaaaaaaaa) >> 1
    右移一位后结果如下图所示:

(x & 0xaaaaaaaa) >> 1

  1. 0x55555555

    0x55555555是从第0位开始,所有偶数位为1

    0x55555555 = 0b01010101010101010101010101010101

  2. x & 0x55555555
    x & 0x55555555的结果如下图所示:

 x & 0x55555555

  1. (x & 0x55555555) << 1

    左移一位的结果如下图所示:

  1. x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));

然后两个数或运算,结果如下图所示:

x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));

第一行代码运算完成。
总结,x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));这行的代码就是以两个bit位一组,对调相邻的bit位。
图解就是将下图
1-1转换成
1-2


分析第二行代码:

x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));

目前x的数值为:

第二行代码

  1. 0xcccccccc

    0xcccccccc是从第0位开始,0和1每隔两位交替出现0
    xcccccccc = 0b11001100110011001100110011001100

  2. x & 0xcccccccc

    x & 0xcccccccc的结果如下图所示:

x & 0xcccccccc

  1. (x & 0xcccccccc) >> 2

    右移两位后结果如下图所示:

(x & 0xcccccccc) >> 2

  1. 0x33333333

    0x33333333是从第0位开始,1和0每隔两位交替出现
    0x33333333 = 0b00110011001100110011001100110011

  2. x & 0x33333333

x & 0x33333333的结果如下图所示:

x & 0x33333333

  1. (x & 0x33333333) << 2

左移两位后结果如下图所示:

(x & 0x33333333) << 2

  1. x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));

然后两个数或运算,结果如下图所示:

x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
第二行代码运算完成。
总结,x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));这行的代码就是以4个bit位为一组,分成左边两个bit位一段和右边两个bit位一段,然后这两段相互对调。
图解就是将下图
2-1转换成
2-2


分析第三行代码:

x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));

目前x的数值为:

第三行代码

  1. 0xf0f0f0f0

    0xf0f0f0f0是从第0位开始,0和1每隔四位交替出现
    0xf0f0f0f0= 0b11110000111100001111000011110000

  2. x & 0xf0f0f0f0

    x & 0xf0f0f0f0的结果如下图所示:

x & 0xf0f0f0f0

  1. (x & 0xf0f0f0f0) >> 4

    右移四位后结果如下图所示:

(x & 0xf0f0f0f0) >> 4

  1. 0x0f0f0f0f

    0x0f0f0f0f是从第0位开始,1和0每隔四位交替出现

    0x0f0f0f0f= 0b00001111000011110000111100001111

  2. x & 0x0f0f0f0f

    x & 0x0f0f0f0f的结果如下图所示:

x & 0x0f0f0f0f

  1. (x & 0x0f0f0f0f) << 4

    左移四位后结果如下图所示:

(x & 0x0f0f0f0f) << 4

  1. x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));

    然后两个数或运算,结果如下图所示:

x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));

第三行代码运算完成。
总结,x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));这行的代码就是以8个bit位为一组,分成左边四个bit位一段和右边四个bit位一段,然后这两段相互对调。
图解就是将下图
3-1
转换成
3-2


分析第四行代码:

x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));

目前x的数值为:

第四行代码

  1. 0xff00ff00

    0xff00ff00是从第0位开始,0和1每隔八位交替出现

    0xff00ff00= 0b11111111000000001111111100000000

  2. x & 0xff00ff00

    x & 0xff00ff00的结果如下图所示:

x & 0xff00ff00

  1. (x & 0xff00ff00) >> 8

    右移八位后结果如下图所示:

(x & 0xff00ff00) >> 8

  1. 0x00ff00ff

    0x00ff00ff是从第0位开始,1和0每隔八位交替出现

    0x00ff00ff= 0b00000000111111110000000011111111

  2. x & 0x00ff00ff

    x & 0x00ff00ff的结果如下图所示:

x & 0x00ff00ff

  1. (x & 0x00ff00ff) << 8

    左移八位后结果如下图所示:
    (x & 0x00ff00ff) << 8

  2. x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));

    然后两个数或运算,结果如下图所示:

x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));

第四行代码运算完成。
总结,x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));这行的代码就是以16个bit位为一组,分成左边八个bit位一段和右边八个bit位一段,然后这两段相互对调。

图解就是将下图
4-1

转换成
4-2


分析最后一行代码:

((x >> 16) | (x << 16))

目前x的数值为:

5-1

高16bit与低16bit进行交换,结果如下图:

5-2

完成整个位反转算法。


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

©️2020 CSDN 皮肤主题: 1024 设计师:上身试试 返回首页