1、概述
本文將講述Bit-Map算法的相關原理,Bit-Map算法的一些利用場景,例如BitMap解決海量數據尋找重複、判斷個別元素是否在海量數據當中等問題.最後說說BitMap的特色已經在各個場景的使用性。
2、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表示爲:html
那麼怎麼快速定位它的索引呢。若是找到它的索引號,又怎麼定位它的位置呢。Index(N)表明N的索引號,Position(N)表明N的所在的位置號。java
Index(N) = N/8 = N >> 3;
Position(N) = N%8 = N & 0x07;
add方法:git
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)優化
上面的代碼並無擴容方法,超出範圍會報錯,this
使用bit數組來表示某些元素是否存在,好比8位電話號碼.spa
缺點:.net
若是是比較特殊的數字,好比[1,100000000],那麼就會浪費存儲空間,好比這個就必需要(100000000>>3)個字節code
針對上面的缺點,谷歌所實現的EWAHCompressedBitmap對bitmap存儲空間作了必定的優化
相信的講解:http://www.sohu.com/a/166661005_479559
問題實例
一、在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題相似的方法,進行劃分小文件的方法。而後在小文件中找出不重複的整數,並排序。而後再進行歸併,注意去除重複的元素。」
二、給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