在python中使用BitMap排序

朕月關發表於2020-12-03

學習過redis快取穿透解決方案的可能知道布隆過濾器,關於布隆過濾器的知識這裡不再多講,它的底層使用的是bitmap實現的,bitmap就是一個二進位制的bit位組成的陣列,一個int型別為4個位元組,則bit位為32位(1Byte=8bit),減去一個符號位,我們可用的位數為31位,也就可以用bit位對應的1來表示對應的十進位制整數。

使用bitmap對整數陣列排序時首先我們要分配這個bitmap的大小,獲取陣列中的最大整數 M,M//31+1 除以31向上取整,比如一個未排序陣列最大數不超過60,那麼得到結果為2,意思是我們需要兩個int型資料大小的空間來存放這個陣列。
bm=[a[0],a[1]] ,a[0]和a[1]就是我們申請到的資源,a[0]的0-31位分別表示整數的0-31,a[1]的0-31位分別表示整數的32-63,排序時只需將陣列中元素對應bit位的0置為1即表示該整數存在,一趟下來每個陣列中的元素都在對應的bit位被置為1.我們輸出排序結果時只需按順序輸出為1的bit位的索引即可。比如a=[1,5,3,4,7,8,15,6,9] 我們只需用16位的bit即可表示全部整數,他們在bitmap中的位置應該是這樣的“1000001111111010” 從左往右數為1的位置索引就是1,3,4,5,6,7,8,9,15這就是a的排序結果。

上面的方法是不是太複雜了,python中有沒有更簡便的方法呢?

一切從簡,pip install bitmap ,我們只需下載第三方包bitmap。這個包直接為我們封裝了一系列的bitmap用法,比自己寫容易多了。

import bitmap
print(dir(bitmap))
#result:['BitMap', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__path__', '__spec__', 'bitmap']
[Finished in 0.3s]

首先引引入bitmap模組,使用dir檢視有哪些屬性,BitMap這個應該就是類,我們可以使用它來建立一個bitmap物件。

from bitmap import BitMap
a=[1,5,3,4,7,8,15,6,9] 
bm=BitMap(max(a))
print(dir(bm))
print(bm.tostring())
for i in a:
    bm.set(i)
print(bm.tostring())
print(bm.nonzero())
#
['BITMASK', 'BIT_CNT', '__class__', '__del__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'all', 'any', 'bitmap', 'count', 'flip', 'fromhexstring', 'fromstring', 'none', 'nonzero', 'reset', 'set', 'size', 'test', 'tohexstring', 'tostring']
0000000000000000
1000001111111010
[1, 3, 4, 5, 6, 7, 8, 9, 15]

我們先引入bitmap包下的BitMap類,例項化一個bm物件,BitMap初始化時傳入的引數值為我們需要排序的陣列中的最大值,他可以幫我們自動分配所需的最小位數。bm此時應該是16位的0,使用dir(bm),可以發現它有三個方法set、tostring和nonzero字面意思理解為轉為字串,非零位,我們使用set(陣列中的元素值),將a中的元素全部存入bm的bit位置為1,使用bm.tostring()方法可以看到每一個為1的位對應的索引就是a中的元素的值。只需輸出這個為1的索引列表即為我們的排序結果。nonzero方法就是封裝好的方法。

bitmap排序有什麼用呢?

該演算法為解決大量的資料排序而生,如一個10G的檔案,每行都為數字,想要對它進行排序,我們要考慮記憶體不夠的情況,如果使用bitmap排序則可以大大減少空間資源使用。

缺點有哪些?

  1. bitmap的缺點如果我們需要排序的元素數量過少且間隔過大,就容易造成空間浪費,比如一個陣列只有[1,100000]兩個元素,使用bitmap就很浪費空間。
  2. bitmap無法保留陣列中的重複元素,因為每個位置只能儲存一個資料,若兩個相同的資料都在a中,經過bitmap排序後的輸出只會有一個,這也可以算作它的優勢幫我們自動去重。

以上僅為個人理解,有不對的地方也請賜教。關於bitmap的更多用法後續有用到再做探討。

相關文章