一、概述
本文將講述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表示為:
那么怎么快速定位它的索引呢。如果找到它的索引號,又怎么定位它的位置呢。Index(N)代表N的索引號,Position(N)代表N的所在的位置號。
Index(N) = N/8 = N >> 3;
Position(N) = N%8 = N & 0x07;
add方法:
public void add(int num){ // num/8得到byte[]的index int arrayIndex = num >> 3; // num%8得到在byte[index]的位置 int position = num & 0x07; //將1左移position后,那個位置自然就是1,然后和以前的數據做|,這樣,那個位置就替換成1了。 bits[arrayIndex] |= 1 << position; }
code:
public class BitMap { //保存數據的 private byte[] bits; //能夠存儲多少數據 private int capacity; public BitMap(int capacity){ this.capacity = capacity; //1bit能存儲8個數據,那么capacity數據需要多少個bit呢,capacity/8+1,右移3位相當於除以8 bits = new byte[(capacity >>3 )+1]; } public void add(int num){ // num/8得到byte[]的index int arrayIndex = num >> 3; // num%8得到在byte[index]的位置 int position = num & 0x07; //將1左移position后,那個位置自然就是1,然后和以前的數據做|,這樣,那個位置就替換成1了。 bits[arrayIndex] |= 1 << position; } public boolean contain(int num){ // num/8得到byte[]的index int arrayIndex = num >> 3; // num%8得到在byte[index]的位置 int position = num & 0x07; //將1左移position后,那個位置自然就是1,然后和以前的數據做&,判斷是否為0即可 return (bits[arrayIndex] & (1 << position)) !=0; } public void clear(int num){ // num/8得到byte[]的index int arrayIndex = num >> 3; // num%8得到在byte[index]的位置 int position = num & 0x07; //將1左移position后,那個位置自然就是1,然后對取反,再與當前值做&,即可清除當前的位置了. bits[arrayIndex] &= ~(1 << position); } public static void main(String[] args) { BitMap bitmap = new BitMap(100); bitmap.add(7); System.out.println("插入7成功"); boolean isexsit = bitmap.contain(7); System.out.println("7是否存在:"+isexsit); bitmap.clear(7); isexsit = bitmap.contain(7); System.out.println("7是否存在:"+isexsit); } }
每個byte存8個數字,相對於int類型來說,節省了32倍的存儲空間
存儲數據范圍,就上面的例子來說,上面bits數組的長度為13,那么總共可以存儲(13*8)104個數字,分別是(0~103)
上面的代碼並沒有擴容方法,超出范圍會報錯,
使用bit數組來表示某些元素是否存在,比如8位電話號碼.
缺點:
如果是比較特殊的數字,比如[1,100000000],那么就會浪費存儲空間,比如這個就必須要(100000000>>3)個字節
針對上面的缺點,谷歌所實現的EWAHCompressedBitmap對bitmap存儲空間做了一定的優化
相信的講解:http://www.sohu.com/a/166661005_479559
問題實例
1、在2.5億個整數中找出不重復的整數,注,內存不足以容納這2.5億個整數
解法一:采用2-Bitmap(每個數分配2bit,00表示不存在,01表示出現一次,10表示多次,11無意義)進行,共需內存2^32 * 2 bit=1 GB內存,還可以接受。然后掃描這2.5億個整數,查看Bitmap中相對應位,如果是00變01,01變10,10保持不變。所描完事后,查看bitmap,把對應位是01的整數輸出即可。
解法二:也可采用與第1題類似的方法,進行划分小文件的方法。然后在小文件中找出不重復的整數,並排序。然后再進行歸並,注意去除重復的元素。”
2、給40億個不重復的unsigned int的整數,沒排過序的,然后再給一個數,如何快速判斷這個數是否在那40億個數當中?
解法一:可以用位圖/Bitmap的方法,申請512M的內存,一個bit位代表一個unsigned int值。讀入40億個數,設置相應的bit位,讀入要查詢的數,查看相應bit位是否為1,為1表示存在,為0表示不存在。
https://www.jianshu.com/p/6082a2f7df8e
https://wizardforcel.gitbooks.io/the-art-of-programming-by-july/content/06.07.html
http://blog.51cto.com/zengzhaozheng/1404108
http://blog.csdn.net/h348592532/article/details/45362661