統計二進制數組中1的個數--variable-precision SWAR算法解析

統計一個32位數字的二進制表示中,1的個數(把這個32位數字當做一個長度爲32的二進制數組)。
如0xffc1, 其二進制爲1111 1111 1100 0001, 那麼1的個數爲11個。

算法如下:

unsigned int swar(unsigned int n)                                                                                                                                            
{
    n = (n & 0x55555555) + ((n >> 1) & 0x55555555);  // line1

    n = (n & 0x33333333) + ((n >> 2) & 0x33333333);  // line 2

    n = (n & 0x0f0f0f0f) + ((n >> 4) & 0x0f0f0f0f);  // line3

    n = (n * 0x01010101) >> 24;                      // line4

    return n;
}

返回結果即爲1的個數。

下面簡要分析一個原理:
假定給出一個數字(32位二進制數字),0x20191124, 其二進制
0010 0000 0001 1001 0001 0001 0010 0100
2 0 1 9 1 1 2 4
共有8個1, 我們現在就是要求出這個8.
swar算法的思想就是把輸入的二進制數組(數字)轉換爲有意義的數字(當然也可以使用臨時變量存放,這些細節不作討論)。
什麼叫有意義的數字?
對於0x20191124,我們無法直接看出其二進制中1的個數,但是如果我們把它轉換成
0x10121111, 這個數字是不就明顯了?
什麼?不明顯?
你把0x10121111各個數字加起來看看,1 + 0 + 1 + 2 + 1 + 1 + 1 + 1 = 8;
0x10121111這個數字的每一位代表了原數中相應位置4位中1的個數,然後把這8個數字加起來就是32位數字中1的個數。
這就是上面說的有意義的數字。(原數字0x20191124我們是無法直接看出結果的)

line4, 就是在把各位數字加和。

先看第一行:

 n = (n & 0x55555555) + ((n >> 1) & 0x55555555); 

0x20191124先與上 0x55555555, 然後又移位與上 0x55555555,好像有點複雜。
我們一點點看,分析一下,
0x55555555的二進制 0101 0101 0101 0101 0101 0101 0101 0101 (奇數位爲1)
0x20191124 & 0x55555555的結果不就是記錄了 0x20191124的二進制奇數位是否爲1麼。
奇數位記錄了,偶數位呢?這就是後半部分的意義。
n>>1錯開一位, 再與上0x55555555不就是統計了偶數位是否爲1麼。
然後奇偶統計結果相加就是整個數字(0x20191124)中對1的統計結果(不是最終結果)。
0x20191124 & 0x55555555 結果a爲:
0000 0000 0001 0001 0001 0001 0000 0100
奇數位上共有5個1

((n >> 1) & 0x55555555)結果b爲:
0001 0000 0000 0100 0000 0000 0001 0000
偶數位上共有3個1

a,b相加之後:不要以爲加完是0x20191124!
0001 0000 0001 0101 0001 0001 0001 0100
每2位爲一個單位整理一下,
0 1 0 0 0 1 1 1 0 1 0 1 0 1 1 0 可以看到這8個1已經被統計得有規律了,規律就是結果存放在以兩個二進制位的單位內。
現在的目標就是把這些數字以兩個二進制位爲單位進行相加,怎麼相加呢?

下面補充一點小學數學的知識,以便大學發現規律,爲了方便還是以16進制進行計算(16位)
0x1234 * 0x1111進行乘法運算
在這裏插入圖片描述
寫出結果a的那一列正是1 + 2 + 3 + 4,各位相加的結果。
只需要(0x1234*0x1111) >> 12, 結果a右邊的(自動捨去,溢出),就可計算出各位相加的結果。
這也就是Line4的原理。
至於line2, 3讀者可自行分析。

總結:
swar算法分兩個步驟:

  1. 統計數字(二進制數組),轉換爲一個各位表示原數1的個數的數字。
  2. 把轉換後的數字各位相加(每位表示相應位置1的個數)。

文章開頭給出的算法是逐漸歸納,由2位爲一個單位進行統計,再到4位(line2),再到8位(line3),最後line4進行各位相加。

其實根據上面兩個步驟,完全可以實現簡化版的算法,如下圖,只不多會有更多限制(比如原本32位右移30位,只剩2位有意義的數字,結果最大隻能爲3)。
swar算法最後右移了24位剩餘8位,最大可返回255(255個1),已經足夠了。(最多32個1)
在這裏插入圖片描述
以上爲個人理解,如有錯誤,還望指正。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章