golang实现的布隆过滤器_布隆过滤器(Bloom Filter)原理及Golang实现

布隆过滤器简介

布隆过滤器(Bloom Filter)是一个基于hash的概率性的数据结构,它实际上是一个很长的二进制向量,可以检查一个元素可能存在集合中,和一定不存在集合中。它的优点是空间效率高,但是有一定false positive(元素不在集合中,但是布隆过滤器显示在集合中)。

布隆过滤器原理

布隆过滤器就是一个长度为m个bit的bit数组,初始的时候每个bit都是0,另外还有k个hash函数。

82c61201ca10d3f08c3f1adf1731f5b8.png

布隆过滤器加入元素

当加入一个元素时,先用k个hash函数得到k个hash值,将k个hash值与bit数组长度取模得到个k个位置,将这k个位置对应的bit置位1。

8076d2cac80172855a77fb1ec6690a1e.png

在加入了bloom之后,再加入filter。

79324a4b86456835b80ca7c019919044.png

布隆过滤器查询元素

在布隆过滤器中查询元素比较简单,同样地,先用k个hash函数得到k个hash值,将k个hash值与bit数组长度取模得到个k个位置,然后检查这k个位置的bit是否是1。如果都是1,布隆过滤器返回这个原始存在。

8e5f5187da46b63604ed2f1f105ecb3b.png

布隆过滤器的false positive

查询元素中,有可能k个hash值对应的位置都已经置一,但这都是其他元素的操作,实际上这个元素并不在布隆过滤器中,这就是false positive。

看下面这个例子,添加完bloom,filter后,检查cat是否在

布隆过滤器中。

f5d2c0531d597d28d83a4c4b2c55edba.png

实际上,cat并不在布隆过滤器中。所以说,布隆过滤器返回true,元素不一定在其中;但是返回false,元素一定不在布隆过滤器中。

布隆过滤器的false positive计算

false positive计算,有3个重要的参数。

m表示bit数组的长度

k表示散列函数的个数

n表示插入的元素个数

布隆过滤器中,一个元素插入后,某个bit为0的概率是

(1−1/m)^k

复制代码

n元素插入后,某个bit为0的概率是

(1−1/m)^(nk)

复制代码

false positive的概率是

(1−(1−1/m)^nk)^k

复制代码

因为需要的是k个不同的bit被设置成1,概率是大约是

(1−e^(−kn/m))^k

复制代码

这个就是false positive的概率

Golang代码实现

代码实现在我的github仓库。

这个Golang实现,支持并发操作,批量加入byte数组,字符串,数字等。

bit数组的大小选择

代码中,bit数组表示成[]byte数组。由于后续在[]byte定位hash需要取余操作,%操作是一个比较慢的操作,如果数组的长度是2的n次方,%可以被优化成& (2^n-1)。因此,New()函数初始化的时候,会将[]byte数组的长度拉长到2^n,加快计算。

type Filter struct {

lock *sync.RWMutex

concurrent bool

m uint64 // bit array of m bits, m will be ceiling to power of 2

n uint64 // number of inserted elements

log2m uint64 // log_2 of m

k uint64 // the number of hash function

keys []byte // byte array to store hash value

}

func New(size uint64, k uint64, race bool) *Filter {

log2 := uint64(math.Ceil(math.Log2(float64(size))))

filter := &Filter{

m: 1 << log2,

log2m: log2,

k: k,

keys: make([]byte, 1<

concurrent: race,

}

if filter.concurrent {

filter.lock = &sync.RWMutex{}

}

return filter

}

// location returns the bit position in byte array

// & (f.m - 1) is the quick way for mod operation

func (f *Filter) location(h uint64) (uint64, uint64) {

slot := (h / bitPerByte) & (f.m - 1)

mod := h & mod7

return slot, mod

}

复制代码

hash函数的选择

因为需要快速的操作,因此不选择md5,sha等耗时比较久的hash操作。经过比较之后,我选择使用murmur3的hash算法,来对key进行hash。

// baseHash returns the murmur3 128-bit hash

func baseHash(data []byte) []uint64 {

a1 := []byte{1} // to grab another bit of data

hasher := murmur3.New128()

hasher.Write(data) // #nosec

v1, v2 := hasher.Sum128()

hasher.Write(a1) // #nosec

v3, v4 := hasher.Sum128()

return []uint64{

v1, v2, v3, v4,

}

}

复制代码

输入一段元素的字节数组,将其hash值返回,计算出这个元素的位置。

更多内容,可以访问代码实现在我的博客。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是用Golang实现布隆过滤器的示例代码: ```go package main import ( "bufio" "fmt" "hash/fnv" "os" ) type BloomFilter struct { bitset []bool k uint64 } func NewBloomFilter(m uint64, k uint64) *BloomFilter { return &BloomFilter{make([]bool, m), k} } func (bf *BloomFilter) Add(s string) { for i := uint64(0); i < bf.k; i++ { h := fnv.New64a() h.Write([]byte(s)) index := h.Sum64() % uint64(len(bf.bitset)) bf.bitset[index] = true } } func (bf *BloomFilter) Contains(s string) bool { for i := uint64(0); i < bf.k; i++ { h := fnv.New64a() h.Write([]byte(s)) index := h.Sum64() % uint64(len(bf.bitset)) if !bf.bitset[index] { return false } } return true } func main() { bf := NewBloomFilter(10000, 3) // Add some strings to the Bloom filter bf.Add("apple") bf.Add("banana") bf.Add("orange") // Check if some strings are in the Bloom filter fmt.Println(bf.Contains("apple")) // true fmt.Println(bf.Contains("banana")) // true fmt.Println(bf.Contains("orange")) // true fmt.Println(bf.Contains("grape")) // false fmt.Println(bf.Contains("peach")) // false fmt.Println(bf.Contains("pineapple")) // false // Read some strings from a file and check if they are in the Bloom filter file, _ := os.Open("words.txt") defer file.Close() scanner := bufio.NewScanner(file) for scanner.Scan() { word := scanner.Text() if bf.Contains(word) { fmt.Printf("%s may be in the set\n", word) } } } ``` 以上是一个简单的布隆过滤器实现,它使用了FNV-1a哈希算法来计算字符串的哈希值,并将哈希值映射到一个布隆过滤器的位数组中,如果位数组中的所有位都被置位了,那么就认为这个字符串可能在集合中。注意,这个实现中的位数组大小是固定的,如果集合中的元素数量超过了位数组的大小,那么误判率就会增加。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值