1. 程式人生 > >Bitmap和2-Bitmap海量資料處理問題

Bitmap和2-Bitmap海量資料處理問題

內容會持續更新,有錯誤的地方歡迎指正,謝謝!

題目

題1:在2.5億個整數找出不重複的整數,記憶體不足以容納著2.5億個整數。
題2:給40億個不重複的unsigned int的整數,沒排過序的,然後再給一個數,如何快速判斷這個數是否在那40億個數當中?
題3:“遊戲任務標記”的程式設計題,請見連結:
http://blog.csdn.net/billcyj/article/details/78948402

分析

要解決上面的問題,都需可以用Bitmap或2-Bitmap。

Bitmap

BitMap也就是點陣圖

引出Bitmap

舉一個小例子,有一個無序整形陣列{8,4,9},也就佔用記憶體3*4=12位元組,這很正常,但如果有1億個這樣的數呢?1億*4/(1024*1024*1024)=0.372G左右。如果對該資料做查詢,那記憶體壓力很大,我們想要高效能地解決這個問題,就得引出Bitmap。

Bitmap概念

一個byte佔8個bit,如果每一個bit的值就是有或沒有,也就是二進位制的0或1。那就可用bit的0或1代表某陣列值有或沒有。0代表該數值沒有出現過,1代表該陣列值出現過。具體如下圖:

這裡寫圖片描述

那1億的資料現在所需的空間0.372G/32,一個原佔32bit的資料現在只佔用1bit,節省了不少記憶體。

應用和程式碼

疑問:一個數怎麼快速定位它的索引,也就是說搞清楚byte[index]的索引號index是多少,位置號position是哪一位。

例子:例如add(14)。14已經超出byte[0]的對映範圍,在byte[1]的範圍內。那怎麼快速定位它的索引號?如果找到它的索引號,又怎麼找到它的位置號?Index(N)代表N的索引號,Position(N)代表N的位置號。

公式:
Index(N) = N/8 = N >> 3;
Position(N) = N%8 = N & 7;(對於2的冪的數才能這樣幹!)

例子:
add(int num)向Bitmap裡add資料該怎麼辦?
上面已分析了,add的目的是為了將所在的位置從0變成1,其他位置不變。
(圖中間,不是“作與操作”,是“作或操作”。“a|=b”表示a和b作或操作,結果賦給a。)
這裡寫圖片描述

程式碼:

public void add(int num)
{
    int index=num>>3;
    int position=num&7;
    byte
[index] |= 1<<position;//如圖,將1左移position位,使目標位置1 }

Bitmap的其他操作與此類似。還可把byte換成unsigned int,即用32位的unsigned來存。

總結: Bitmap是簡單快速的資料結構,常用於空間的優化。

2-Bitmap

每個數分配2bit,00表示不存在,01表示出現一次,10表示多次,其他同Bitmap。

求解

題1答案

也就是出現過但只出現過一次的整數。

方案1:使用2-Bitmap
每個數分配2bit,00表示不存在,01表示出現一次,10表示多次,11無意義。然後遍歷修改Bitmap中的對應位,如果是00則變01,01則變10,10則保持不變。遍歷修改完後,最後遍歷輸出對應位是01的整數。

方案2:分治法
先將2.5億個數劃分成 2.5億/2 個組,每個組裡2個整數,再在每個組裡去掉重複的整數後進行排序,然後再進行歸併,最終便可得到只出現過一次的整數。

總結:還是感覺點陣圖好。。。

題2答案

方案1:使用Bitmap
一個bit位代表一個unsigned int值。讀入40億個數,設定相應的bit位。由於2^32=42.9+億,那麼2^32bit才能存下40億個數,也就需要2^32=4Gb=0.5GB=512M記憶體。讀入要查詢的數,檢視相應bit位是否為1,為1表示存在,為0表示不存在。

方案2:二分法
我們把40億個數中的每一個數都用32位的二進位制來表示,因為它們的二進位制中每一位要麼是0要麼是1,因此可以根據資料的某一位來分,該位為1的在分配在一組,該位為0的分配在另一組。這裡的二分法就是採用了這種思想。
已知需要查詢的這位數的二進位制的每位是0還是1,所以從最高位到最低位,總共需進行31次二分查詢(由於是unsigned型別,所以最高那位都為0),時間複雜度為O(logn)。

總結: 用點陣圖法很直接,但佔用記憶體量會大點;利用二分法比較巧妙,不太容易想到。