☕【難點攻克技術系列】「海量資料計算系列」如何使用BitMap在海量資料中對相應的進行去重、查詢和排序

浩宇天尚發表於2021-12-31

BitMap(點陣圖)的介紹

BitMap從字面的意思,很多人認為是點陣圖,其實準確的來說,翻譯成基於位的對映,其中資料庫中有一種索引就叫做點陣圖索引。

在具有效能優化的資料結構中,大家使用最多的就是hash表,是的,在具有定位查詢上具有O(1)的常量時間,多麼的簡潔優美。但是資料量大了,記憶體就不夠了。此外,可以使用類似外排序來解決問題的,由於要走IO所以時間上又不行。

所謂的Bit-map就是用一個bit位來標記某個元素對應的Value, 而Key即是該元素。由於採用了Bit為單位來儲存資料,因此在儲存空間方面,可以節省。

BitMap(點陣圖)的應用

  • 1)可進行資料的快速查詢,判重,刪除,一般來說資料範圍是int的10倍以下。
  • 2)去重資料而達到壓縮資料

BitMap(點陣圖)的原理

上面說了BitMap的基本思想就是用一個bit位來標記某個元素對應的Value,而Key即是該元素。由於採用了Bit為單位來儲存資料。

BitMap(點陣圖)的案例

假設有這樣一個需求:在20億個隨機整數中找出某個數m是否存在其中,並假設32位作業系統,4G記憶體

在Java中,int佔4位元組,1位元組=8位(1 byte = 8 bit)

  • 如果每個數字用int儲存,那就是20億個int,因而佔用的空間約為 (2000000000*4/1024/1024/1024)≈7.45G

  • 如果按位儲存就不一樣了,20億個數就是20億位,佔用空間約為 (2000000000/8/1024/1024/1024)≈0.233G

如何表示一個數呢

每一位表示一個數,0表示不存在,1表示存在,這正符合二進位制

這樣可以很容易表示{1,2,4,6}這幾個數:

計算機記憶體分配的最小單位是位元組,也就是8位,那如果要表示{12,13,15}怎麼辦呢?當然是在另一個8位上表示了:

這樣的話,好像變成一個二維陣列了

1個int佔32位,那麼我們只需要申請一個int陣列長度為 int tmp[1+N/32] 即可儲存,其中N表示要儲存的這些數中的最大值,於是乎:

  • tmp[0]:可以表示0~31

  • tmp[1]:可以表示32~63

  • tmp[2]:可以表示64~95

如此一來,給定任意整數M,那麼M/32就得到下標,M%32就知道它在此下標的哪個位置。

新增

怎麼把一個數放進去呢?例如,想把5這個數字放進去,怎麼做呢?

首先,5/32=0,5%32=5,也是說它應該在tmp[0]的第5個位置,那我們把1向左移動5位,然後按位或

換成二進位制就是

這就相當於 86 | 32 = 118

86 | (1<<5) = 118
b[0] = b[0] | (1<<5)

要想插入一個數,將1左移帶代表該數字的那一位,然後與原數進行按位或操作

簡化一下,就是 86 + (5/8) | (1<<(5%8))

因此,公式可以概括為:p + (i/8)|(1<<(i%8)) 其中,p表示現在的值,i表示待插入的數

清除

如果要清除該怎麼做呢?

還是上面的例子,假設我們要6移除,該怎麼做呢?

從圖上看,只需將該數所在的位置為0即可

1左移6位,就到達6這個數字所代表的位,然後按位取反,最後與原數按位與,這樣就把該位置為0了

b[0] = b[0] & (~(1<<6))

b[0] = b[0] & (~(1<<(i%8)))

查詢

每一位代表一個數字,1表示有(或者說存在),0表示無(或者說不存在)。通過把該為置為1或者0來達到新增和清除的效果,那麼判斷一個數存不存在就是判斷該數所在的位是0還是1

假設,我們想知道3在不在,那麼只需判斷 b[0] & (1<<3) 如果這個值是0,則不存在,如果是1,就表示存在。

Bitmap快速排序

假設我們要對0-7內的5個元素(4,7,2,5,3)排序(這裡假設這些元素沒有重複),我們就可以採用Bit-map的方法來達到排序的目的。要表示8個數,我們就只需要8個Bit(1Bytes),首先我們開闢1Byte的空間,將這些空間的所有Bit位都置為0,然後將對應位置為1。最後,遍歷一遍Bit區域,將該位是一的位的編號輸出(2,3,4,5,7),這樣就達到了排序的目的,時間複雜度O(n)。

優點:

  • 運算效率高,不需要進行比較和移位;
  • 佔用記憶體少,比如N=10000000;只需佔用記憶體為N/8=1250000Byte=1.25M

缺點:

  • 所有的資料不能重複。即不可對重複的資料進行排序和查詢。
  • 只有當資料比較密集時才有優勢

Bitmap快速去重

  • 20億個整數中找出不重複的整數的個數,記憶體不足以容納這20億個整數。

  • 首先,根據“記憶體空間不足以容納這05億個整數”我們可以快速的聯想到Bit-map。下邊關鍵的問題就是怎麼設計我們的Bit-map來表示這20億個數字的狀態了。其實這個問題很簡單,一個數字的狀態只有三種,分別為不存在,只有一個,有重複。因此,只需要2bits就可以對一個數字的狀態進行儲存了,假設我們設定一個數字不存在為00,存在一次01,存在兩次及其以上為11。那我們大概需要儲存空間2G左右。

  • 接下來的任務就是把這20億個數字放進去(儲存),如果對應的狀態位為00,則將其變為01,表示存在一次;如果對應的狀態位為01,則將其變為11,表示已經有一個了,即出現多次;如果為11,則對應的狀態位保持不變,仍表示出現多次。

  • 最後,統計狀態位為01的個數,就得到了不重複的數字個數,時間複雜度為O(n)。

快速查詢

int陣列中的一個元素是4位元組佔32位,那麼除以32就知道元素的下標,對32求餘數(%32)就知道它在哪一位,如果該位是1,則表示存在.

Bitmap的場景總結

  • Bitmap主要用於快速檢索關鍵字狀態,通常要求關鍵字是一個連續的序列(或者關鍵字是一個連續序列中的大部分), 最基本的情況,使用1bit表示一個關鍵字的狀態(可標示兩種狀態),但根據需要也可以使用2bit(表示4種狀態),3bit(表示8種狀態)。

  • Bitmap的主要應用場合:表示連續(或接近連續,即大部分會出現)的關鍵字序列的狀態(狀態數/關鍵字個數 越小越好)。

  • 32位機器上,對於一個整型數,比如int a=1 在記憶體中佔32bit位,這是為了方便計算機的運算。但是對於某些應用場景而言,這屬於一種巨大的浪費,因為我們可以用對應的32bit位對應儲存十進位制的0-31個數,而這就是Bit-map的基本思想。Bit-map演算法利用這種思想處理大量資料的排序、查詢以及去重。

參考資料

https://blog.csdn.net/qq_41369135/article/details/116938671

相關文章