學習筆記 詳解一種高效位反轉演算法

dadalaohua發表於2020-10-01

位反轉

這裡的位反轉(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

相關文章