golang bitmap(點陣圖)
一、概述
本文將講述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整數在golang中是佔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
如何判斷int數字在tmp數組的哪個下標?
這個其實可以通過直接除以32取整數部分,例如:整數8除以32取整等於0,那麼8就在tmp[0]上。另外,我們如何知道了8在tmp[0]中的32個位中的哪個位,這種情況直接mod上32就ok,又如整數8,在tmp[0]中的第8 mod上32等於8,那麼整數8就在tmp[0]中的第八個bit位(從右邊數起)。
go簡單實現:
package bitmap
import (
"bytes"
"fmt"
)
type Bitmap struct {
words []uint64
length int
}
func New() *Bitmap {
return &Bitmap{}
}
func (bitmap *Bitmap) Has(num int) bool {
word, bit := num/64, uint(num%64)
return word < len(bitmap.words) && (bitmap.words[word]&(1<<bit)) != 0
}
func (bitmap *Bitmap) Add(num int) {
word, bit := num/64, uint(num%64)
for word >= len(bitmap.words) {
bitmap.words = append(bitmap.words, 0)
}
// 判斷num是否已經存在bitmap中
if bitmap.words[word]&(1<<bit) == 0 {
bitmap.words[word] |= 1 << bit
bitmap.length++
}
}
func (bitmap *Bitmap) Len() int {
return bitmap.length
}
func (bitmap *Bitmap) String() string {
var buf bytes.Buffer
buf.WriteByte({)
for i, v := range bitmap.words {
if v == 0 {
continue
}
for j := uint(0); j < 64; j++ {
if v&(1<<j) != 0 {
if buf.Len() > len("{") {
buf.WriteByte( )
}
fmt.Fprintf(&buf, "%d", 64*uint(i)+j)
}
}
}
buf.WriteByte(})
fmt.Fprintf(&buf,"
Length: %d", bitmap.length)
return buf.String()
}
推薦閱讀: