跳到主要內容

variable precision SWAR算法

      計算二進制形式中1的數量這種問題,在各種刷題網站上比較常見,以往都是選擇最笨的遍歷方法"矇混"過關。在了解Redis的過程中接觸到了variable precision SWAR算法(以下簡稱VP-SWAR算法),算法異常簡潔,是目前已知的同類方法中最快的。但如果對於位運算不是很熟悉的話,卻不一定容易理解,所以有必要記錄一下。


      下面先看看VP-SWAR算法的完整實現,然後再逐行解釋。


  public int vpSWAR(int i){ 
i
= (i & 0x55555555) + ((i>>1) & 0x55555555);
i
= (i & 0x33333333) + ((i>>2) & 0x33333333);
i
= (i & 0x0F0F0F0F) + ((i>>4) & 0x0F0F0F0F);
i
= (i * 0x01010101) >> 24;
return i;
}

      VP-SWAR算法分為四步,第一步


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

      第一步的作用是計算每兩位為一組的二進制形式包含1的個數。要理解這句話,我們需要從二進制的角度看看到底發生了什麼。首先, 0x55555555 的二進製表示為 0101 0101 0101 0101 0101 0101 0101 0101 ,這個数字的規律是基數位為1,偶數位為0。為簡單起見,我們只考慮兩位,總共有四種情況,即:


 






























i b i & b 結果
00 01 00
01 01 01
10 01 00
11 01 01


       觀察發現, i & (0b01) 是i的基數位對應b的1位,i的偶數位對應着b的0位, i & (0b01) 的結果會將I的偶數位置為0,而基數位保持不變,得到的結果就是i的基數位包含1的個數。 (i >> 1) & 0x55555555 先將i右移一位,也就是將i的基數位對應b的0位,i的偶數位對應着b的1位,然後再與 0x55555555 按位與,計算出來的是i的偶數位包含1的個數。兩個計算結果相加就得到i每兩位為一組中包含的1的數量,我們最後需要的就是這每兩位一組的和。


      第二步是在第一步的基礎上,計算每四位為一組包含1的個數。按照每2位為一組分組用到了 0x55555555 這個數,那麼自然的,按照每4位為一組分組自然就需要 0b0011 這種形式,這就是使用 0x33333333 的原因。理論上, i & (0b0011) 總共有16種情況,但是四位二進制位最多包含4個1,用二進製表示為 0b0100 ,所以經過第一步之後,i最多有5種取值,如下:



































i b i & b 結果
0000 0011 0000
0001 0011 0001
0010 0011 0010
0011 0011 0011
0100 0011 0000

 


      觀察發現, i & (0b0011) 得到的是i的低兩位包含的1的個數,  (i >> 2) & 0b0011 )得到的是i的高兩位包含的1的個數,兩個結果相加得到每四位包含的1的個數。注意,這裏並不是說任何數與 0b0011 按位與得到的都是低兩位包含的1的個數,這裏的前提是第一步的計算,因為經過第一步計算之後,每兩位包含多少個1已經記錄了下來,再和 0b0011 按位與才得到正確的結果。例如, 0x0010 & 0x 0011=0x0010 ,但是我們不能說 0x0010 包含兩個1,但是如果 0x0010 是經過第一步的計算得來,那才說明 0x0010 記錄原始數據低兩位有兩個1。


      第三步在第二步基礎上,計算每8位有多少個1,由 0x010x0011 ,很自然想到 0x00001111 ,其對應的32位的十六進制數就是 0x0F0F0F0F


      第四步就很有意思了,它不再是計算每16位包含1的個數,而是直接計算32位包含1的個數。對於32位的數來說,可以將其按每8位一組分為4組,分別用ABCD表示,例如 0x01020304 用這種形式表示為:



 


 


      假設 0x01020304 是經過前三步計算之後得到的結果,那麼要計算其總共包含多少個1,只需計算A+B+C+D。而ABCD表示的是不同的位區間範圍,不能直接相加,該如何快速計算A+B+C+D的值呢?這裏又用到了移位運算,將B、C、D分別左移8位、16位、24位,使其分別與A對齊:



 


       我們發現,將数字i分別左移0位、8位、16位、24位然後相加的結果,就是 i * 0x01010101 ,因為 i + (i << 8) + (i << 16) + (i << 24) = i * (1 + 1 << 8 + 1 << 16 + 1 << 24) = i * 0x01010101 。對於32位数字來說,左移之後超過32位的部分會被捨棄,低位補0,將左移之後得到的四個数字相加,結果的高8位的值就是原32位數包含的1的個數,要得到這個值,只需要將結果右移24位,將值放在低8位即可。


      到這裏,整個算法就結束了,右移的結果就是1的數量。在Redis中,BITCOUNT命令同時使用了查表法和VP-SWAR這兩種方法。當要計算的位數小於128位時,使用查表法,否則使用VP-SWAR算法。其中查表法的做法是,程序先存一個256長度的表,按順序記錄從0-255(即 0b00000000 - 0b11111111) 數中二進制1的個數,然後對於輸入參數每8位查一次表。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!



網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!



※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師"嚨底家"!!



Orignal From: variable precision SWAR算法

留言

這個網誌中的熱門文章

掃地機器人可以隨身帶上飛機嗎?我想要拿去送給國外的朋友

掃地機器人如果要隨身戴上飛機需要滿足兩個條件: 一個是掃地機器人連同你的隨身行李,整體的體積和重量要符合上機條件,這個具體每家航空公司都不同,可以諮詢,簡單的說就是隨身行李不要超寬超重。 還有一個就是由於掃地機器人內置了鋰電池,所以內置電池的容量要符合相關規定,每個掃地機器人電池容量都不同,具體自行查詢。 根據民航的相關安全要求,凡帶有鋰電池的電子設備均不可以托運,但符合重量要求,尺寸要求以及電量要求的鋰電池及其設備是可以帶上飛機的。 《鋰電池航空運輸規範》中內含鋰離子電池的設備電池額定能量不應超過100Wh的規定,符合國標GB31241-2014,並通過UN38.3航空運輸認證等國際安全標準,所以可以帶上飛機。但是不能托運,只能隨身攜帶。 掃地機器人     掃地機器人     掃地機器人吸塵器 http://www.greenh3y.com/?p=400 Orignal From: 掃地機器人可以隨身帶上飛機嗎?我想要拿去送給國外的朋友

垃圾是怎樣產生的

是指人類在生產、消費、生活和其他活動中產生的固態、半固態廢棄物質(國外的定義則更加廣泛,動物活動產生的廢棄物也屬於此類),主要包括固體顆粒、垃圾、爐渣、污泥、廢棄的製品、破損器皿、殘次品、動物屍體、變質食品、人畜糞便等。有些國家把廢酸、廢鹼、廢油、廢有機溶劑等高濃度的液體也歸為固體廢棄物。 新北垃圾清運     台北垃圾清運 http://www.greenh3y.com/?p=234 Orignal From: 垃圾是怎樣產生的

固體廢棄物的產生原因有甚麼呢?

所謂廢棄物,多指固體廢棄物或含多量固體的廢棄物,被丟棄的廢棄物有可能成為生產的原材料、燃料或消費物品,這是固體廢棄物資源化處理的基礎。 固體廢棄物的產生與排放,伴隨著人類社會還在延續,社會化生產的生產、分配、交換、消費環節都會產生廢棄物;產品生命週期的產品的規劃、設計、原材料採購、製造、包裝、運輸、分配和消費等環節也會產生固體廢棄物,即使是利用固體廢棄物進行逆生產及相應的逆向物流過程也同樣會產生固體廢棄物;土地使用的各功能區,住宅區、商業區、工業區、農業區、市政設施、文化娛樂區、戶外空地等都會產生固體廢棄物;全社會的任何個人、企事業單位、政府組織和社會組織都會產生並排放固體廢棄物。 人類活動產生固體廢棄物的主要原因有: 1)人類認識能力限制,導致自然環境破壞,如水土流失、森林破壞等; 2)參與規劃、設計、製造、運輸、消費、管理等活動的人員的技術水平限制,導致資源浪費,如機加工邊角邊料、不合格產品、不當使用致廢產品等; 3)物質變化規律限制,導致物品、物質功能的演變,如甘蔗渣、爐渣、尾礦等生產過程的副產品、報廢產品、腐變食物等; 4)追求自利、自保、奢侈、虛榮心等理性和非理性心理限制,導致資源浪費,如過度包裝、一次性用品、奢侈品等; 新北垃圾清運 台北垃圾清運 http://www.greenh3y.com/?p=183 Orignal From: 固體廢棄物的產生原因有甚麼呢?