海量數據解決思路之BitMap

本文轉自:https://blog.csdn.net/u013063153/article/details/70800381

一、概述

  本文將講述Bit-Map算法的相關原理,Bit-Map算法的一些利用場景,例如BitMap解決海量數據尋找重複、判斷個別元素是否在海量數據當中等問題.最後說說BitMap的特點已經在各個場景的使用性。

二、Bit-Map算法

先看看這樣的一個場景:給一臺普通PC,2G內存,要求處理一個包含40億個不重複並且沒有排過序的無符號的int整數,給出一個整數,問如果快速地判斷這個整數是否在文件40億個數據當中?

問題思考:

   40億個int佔(40億*4)/1024/1024/1024 大概爲14.9G左右,很明顯內存只有2G,放不下,因此不可能將這40億數據放到內存中計算。要快速的解決這個問題最好的方案就是將數據擱內存了,所以現在的問題就在如何在2G內存空間以內存儲着40億整數。一個int整數在java中是佔4個字節的即要32bit位,如果能夠用一個bit位來標識一個int整數那麼存儲空間將大大減少,算一下40億個int需要的內存空間爲40億/8/1024/1024大概爲476.83 mb,這樣的話我們完全可以將這40億個int數放到內存中進行處理。

具體思路:

   1個int佔4字節即4*8=32位,那麼我們只需要申請一個int數組長度爲 int tmp[1+N/32]即可存儲完這些數據,其中N代表要進行查找的總數,tmp中的每個元素在內存在佔32位可以對應表示十進制數0~31,所以可得到BitMap表:

tmp[0]:可表示0~31

tmp[1]:可表示32~63

tmp[2]可表示64~95

.......

那麼接下來就看看十進制數如何轉換爲對應的bit位:

假設這40億int數據爲:6,3,8,32,36,......,那麼具體的BitMap表示爲:

wKiom1NaKh-yltStAAJ8sL4gHCQ269.jpg

如何判斷int數字在tmp數組的哪個下標,這個其實可以通過直接除以32取整數部分,例如:整數8除以32取整等於0,那麼8就在tmp[0]上。另外,我們如何知道了8在tmp[0]中的32個位中的哪個位,這種情況直接mod上32就ok,又如整數8,在tmp[0]中的第8 mod上32等於8,那麼整數8就在tmp[0]中的第八個bit位(從右邊數起)。

三、Bit-Map算法原始實現

   標註下,這部分來自blog:http://blog.csdn.net/hguisu/article/details/7880288的第五部分。好,來看看c語言的實現:


註明: 左移n位就是乘以2的n次方,右移n位就是除以2的n次方

解析本例中的void set(int i) {        a[i>>SHIFT] |=  (1<<(i & MASK)); }
1)  i>>SHIFT: 
其中SHIFT=5,即i右移5爲,2^5=32,相當於i/32,即求出十進制i對應在數組a中的下標。比如i=20,通過i>>SHIFT=20>>5=0 可求得i=20的下標爲0;

2)  i & MASK: 
其中MASK=0X1F,十六進制轉化爲十進制爲31,二進制爲0001 1111,i&(0001 1111)相當於保留i的後5位。 

比如i=23,二進制爲:0001 0111,那麼 
                        0001 0111 
                  &    0001 1111 = 0001 0111 十進制爲:23 
比如i=83,二進制爲:0000 0000 0101 0011,那麼 
                         0000 0000 0101 0011 
                    &   0000 0000 0001 0000 = 0000 0000 0001 0011 十進制爲:19 

i & MASK相當於i%32。


3) 1<<(i & MASK) 
相當於把1左移 (i & MASK)位。 
比如(i & MASK)=20,那麼i<<20就相當於: 
        0000 0000 0000 0000 0000 0000 0000 0001 << 20 
      =
0000 0000 0001 0000 0000 0000 0000 0000


注意上面 “|=”.

在博文:位運算符及其應用 提到過這樣位運算應用:

將int型變量a的第k位清0,即a=a&~(1<<k)
將int型變量a的第k位置1, 即a=a|(1<<k)

這裏的將  a[i/32] |= (1<<M));第M位置1 .


4) void set(int i) {        a[i>>SHIFT]  |=  (1<<(i & MASK)); }等價於:

  1. void set(int i)  

  2. {  

  3.   a[i/32] |= (1<<(i%32));  

  4. }  

即實現上面提到的三步:

1.求十進制0-N對應在數組a中的下標: n/32 

2.求0-N對應0-31中的數:N%32=M

3.利用移位0-31使得對應32bit位爲1: 1<<M,並置1;

四、BitMap算法一些其他應用場景擴展

(1)BitMap小小變種:2-BitMap。

看個小場景:在3億個整數中找出不重複的整數,限制內存不足以容納3億個整數。

對於這種場景我可以採用2-BitMap來解決,即爲每個整數分配2bit,用不同的0、1組合來標識特殊意思,如00表示此整數沒有出現過,01表示出現一次,11表示出現過多次,就可以找出重複的整數了,其需要的內存空間是正常BitMap的2倍,爲:3億*2/8/1024/1024=71.5MB。

具體的過程如下:

   掃描着3億個整數,組BitMap,先查看BitMap中的對應位置,如果00則變成01,是01則變成11,是11則保持不變,當將3億個整數掃描完之後也就是說整個BitMap已經組裝完畢。最後查看BitMap將對應位爲11的整數輸出即可。

(2)對沒有重複元素的整數進行排序。

   對於非重複的整數排序BitMap有着天然的優勢,它只需要將給出的無重複整數掃描完畢,組裝成爲BitMap之後,那麼直接遍歷一遍Bit區域就可以達到排序效果了。

舉個例子:對整數4、3、1、7、6進行排序

BitMap如下:

wKiom1NeFg-wLliaAAB_Sfb5gkc258.jpg

直接按Bit位輸出就可以得到排序結果了。


五、總結

本文主要講述了BitMap算法的相關概念以及其一些相關的應用場景和實現方法。其實BitMap的應用場景遠遠不止點,比如還可以用於壓縮、爬蟲系統中url去重、解決全組合問題。可能有些人覺得BitMap算法實現起來有點麻煩,其實某些語言是對BitMap算法進行了封裝的,比如java中對應BitMap的數據結構就有BitSet類。其使用方法相當簡單,看看API就ok,還是給個例子吧:


對應的bit位如果有對應整數那麼通過bitSet.get(x)會返回true,反之false。其中x爲BitMap位置下標。

好了,BitMap就說到這裏。下次blog說說處理海量數據的“萬金油”-Hash算法,以及它在MapReduce框架中的應用。


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