本文轉自: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表示爲:
如何判斷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)); }等價於:
void set(int i)
{
a[i/32] |= (1<<(i%32));
}
即實現上面提到的三步:
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如下:
直接按Bit位輸出就可以得到排序結果了。
五、總結
本文主要講述了BitMap算法的相關概念以及其一些相關的應用場景和實現方法。其實BitMap的應用場景遠遠不止點,比如還可以用於壓縮、爬蟲系統中url去重、解決全組合問題。可能有些人覺得BitMap算法實現起來有點麻煩,其實某些語言是對BitMap算法進行了封裝的,比如java中對應BitMap的數據結構就有BitSet類。其使用方法相當簡單,看看API就ok,還是給個例子吧:
對應的bit位如果有對應整數那麼通過bitSet.get(x)會返回true,反之false。其中x爲BitMap位置下標。
好了,BitMap就說到這裏。下次blog說說處理海量數據的“萬金油”-Hash算法,以及它在MapReduce框架中的應用。