布隆过滤器(附go语言代码实现,详细注释)

一、简介

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的,它实际上是由一个很长的二进制向量一系列随机映射函数组成,布隆过滤器可以用于检索一个元素是否在一个集合中。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率(假正例False positives,即Bloom Filter报告某一元素存在于某集合中,但是实际上该元素可能并不在集合中)和删除困难,但是没有识别错误的情形(即假反例False negatives,如果Bloom Filter报告某元素不在集合中,那么该元素一定没有在该集合中)

关键两点:(第三部分图解例子解释原因)

  • Bloom Filter报告某一元素存在于集合中,但是实际上该元素可能并不在集合中
  • Bloom Filter报告某一元素不存在于集合中,那么该元素一定不在集合中

二、算法原理

布隆过滤器由两部分组成:

  • 二进制数组(元素只为0或1,java中可用byte数组来存)
  • 多个哈希函数(用来计算每个key在数组中的位置)

算法原理:
1、初始化
(1)二进制数组初始化长度n,所有值默认为0
(2)多个哈希函数初始化(常用哈希计算方法均可),其可把传入key映射为一个整数,然后对数组长度求余(或和数组长度减1相与)得到一个小于n的索引

2、添加元素
(1)用多个哈希函数分别计算传入key在数组中的索引值i
(2)将数组中对应i位置设为1

3、判断元素是否存在
(1)用多个哈希函数分别计算传入key在数组中的索引值i1、i2…
(2)依次查询数组中对应i1、i2…位置是否为1,若有某一个不为1,则直接返回不存在;否则认为存在


三、图解例子

在这里插入图片描述
如上图,设置哈希函数个数为3,数组长度为18。按照算法步骤添加x、y、z,将相应位置1。

判断w是否存在,通过哈希值计算,发现第16个位置上的值为0,表明该元素一定不存在。因为如果w存在,由于哈希函数计算方式是固定的,则对应位一定为1,如果不为1,就说明w之前肯定没有出现过。

但另一方面,如果此时传入一个元素三个位置都为1,认为它存在,但实际上有可能它并不存在,因为存在哈希冲突,可能之前有某一个元素经过哈希计算也是这三个位置的索引,因此会误判。


四、优缺点

优点:

  • 时间和空间上相比于其他数据结构有很大优势,和普通哈希表相比不需要存储元素本身

缺点:

  • 存在误算,即认为本不存在的元素存在
  • 不能删除元素

五、常见问题

1、为何要使用多个哈希函数?

Hash本身就会面临冲突,如果只使用一个哈希函数,那么冲突的概率会比较高。例如长度100的数组,如果只使用一个哈希函数,添加一个元素后,添加第二个元素时冲突的概率为1%,添加第三个元素时冲突的概率为2%…但如果使用两个哈希函数,添加一个元素后,添加第二个元素时冲突的概率降为万分之4(四种可能的冲突情况,情况总数100x100)


六、go语言实现

package main

import (
	"fmt"

	"github.com/bits-and-blooms/bitset"
)

//设置哈希数组默认大小为16
const DefaultSize = 16

//设置种子,保证不同哈希函数有不同的计算方式
var seeds = []uint{7, 11, 13, 31, 37, 61}

//布隆过滤器结构,包括二进制数组和多个哈希函数
type BloomFilter struct {
	//使用第三方库
	set *bitset.BitSet
	//指定长度为6
	hashFuncs [6]func(seed uint, value string) uint
}

//构造一个布隆过滤器,包括数组和哈希函数的初始化
func NewBloomFilter() *BloomFilter {
	bf := new(BloomFilter)
	bf.set = bitset.New(DefaultSize)

	for i := 0; i < len(bf.hashFuncs); i++ {
		bf.hashFuncs[i] = createHash()
	}
	return bf
}

//构造6个哈希函数,每个哈希函数有参数seed保证计算方式的不同
func createHash() func(seed uint, value string) uint {
	return func(seed uint, value string) uint {
		var result uint = 0
		for i := 0; i < len(value); i++ {
			result = result*seed + uint(value[i])
		}
		//length = 2^n 时,X % length = X & (length - 1)
		return result & (DefaultSize - 1)
	}
}

//添加元素
func (b *BloomFilter) add(value string) {
	for i, f := range b.hashFuncs {
		//将哈希函数计算结果对应的数组位置1
		b.set.Set(f(seeds[i], value))
	}
}

//判断元素是否存在
func (b *BloomFilter) contains(value string) bool {
	//调用每个哈希函数,并且判断数组对应位是否为1
	//如果不为1,直接返回false,表明一定不存在
	for i, f := range b.hashFuncs {
		//result = result && b.set.Test(f(seeds[i], value))
		if !b.set.Test(f(seeds[i], value)) {
			return false
		}
	}
	return true
}

func main() {
	filter := NewBloomFilter()
	filter.add("asd")
	fmt.Println(filter.contains("asd"))
	fmt.Println(filter.contains("2222"))
	fmt.Println(filter.contains("155343"))
}

输出结果如下:

true
false
false
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值