布隆过滤器的原理和简单实现

举报
码乐 发表于 2024/10/10 20:58:48 2024/10/10
【摘要】 1 简介布隆过滤器的原理是当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。检索时,只要看看这些点是不是都是1就大概知道集合中有没有它了;如果这些点有任何一个0,则被检元素一定不在;如果都是 1,则被检元素很可能在。 2 实现一个布隆过滤器布隆过滤器(Bloom Filter)是一种用于检索是否某个元素在集合中的数据结构,它以高效的内存使用来进行集...

1 简介

布隆过滤器的原理是当一个元素被加入集合时,通过K个散列函数将这个元素映射成一个位数组中的K个点,把它们置为1。

检索时,只要看看这些点是不是都是1就大概知道集合中有没有它了;

如果这些点有任何一个0,则被检元素一定不在;
如果都是 1,则被检元素很可能在。

2 实现一个布隆过滤器

布隆过滤器(Bloom Filter)是一种用于检索是否某个元素在集合中的数据结构,它以高效的内存使用来进行集合元素的判断,但允许有一定的误判(假阳性),不会出现漏判(假阴性)。

在Golang中,我们可以使用slice来实现一个简单的布隆过滤器。下面,我们来实现一个10位的布隆过滤器,并举例说明如何使用它。

实现布隆过滤器的步骤:

初始化一个长度为10的位数组,所有值初始化为0。
使用K个哈希函数将元素映射到位数组中的位置,并将对应位置的值设为1。
当检查某个元素是否在集合中时,使用同样的K个哈希函数来计算该元素映射到的位数组中的位置,如果所有位置的值都是1,则认为元素可能在集合中。

示例代码:Golang 实现10位的布隆过滤器

	package main

	import (
	    "fmt"
	    "hash/fnv"
	)

布隆过滤器的结构体

	type BloomFilter struct {
	    bitset []bool // 位数组
	    size   int    // 位数组大小
	}

创建布隆过滤器

	func NewBloomFilter(size int) *BloomFilter {
	    return &BloomFilter{
	        bitset: make([]bool, size), // 初始化位数组
	        size:   size,
	    }
	}

一个简单的哈希函数(使用内置的hash/fnv库)

	func hash(data string, seed uint32) int {
	    h := fnv.New32a()
	    h.Write([]byte(data))
	    return int((h.Sum32() + seed) % uint32(10)) // 返回0-9之间的索引值
	}

向布隆过滤器中添加元素

	func (bf *BloomFilter) Add(data string) {
	    // 使用2个哈希函数示例
	    index1 := hash(data, 0)  // 第一哈希
	    index2 := hash(data, 1)  // 第二哈希
	    
	    bf.bitset[index1] = true
	    bf.bitset[index2] = true
	}

检查元素是否可能存在于布隆过滤器中

	func (bf *BloomFilter) Check(data string) bool {
	    index1 := hash(data, 0)
	    index2 := hash(data, 1)

	    // 检查两个索引位置是否都为1
	    return bf.bitset[index1] && bf.bitset[index2]
	}

创建一个10位的布隆过滤器,然后添加一些元素然后检查元素是否可能存在。 并测试一定不存在的元素。

	func main() {
	    // 
	    bf := NewBloomFilter(10)

	    // 添加一些元素
	    bf.Add("hello")
	    bf.Add("world")
	    bf.Add("golang")

	    // 检查某些元素是否可能存在
	    fmt.Println("Check 'hello':", bf.Check("hello"))   // 可能存在,输出:true
	    fmt.Println("Check 'world':", bf.Check("world"))   // 可能存在,输出:true
	    fmt.Println("Check 'golang':", bf.Check("golang")) // 可能存在,输出:true

	    // 检查不存在的元素
	    fmt.Println("Check 'python':", bf.Check("python")) // 不存在,可能输出:false
	    fmt.Println("Check 'java':", bf.Check("java"))     // 不存在,可能输出:false
	}

布隆过滤器结构体:

bitset: 使用slice来表示位数组(长度为10)。
size: 位数组的大小,固定为10。

哈希函数(hash):

使用Go的hash/fnv库生成32位的哈希值。
为了模拟不同的哈希函数,使用了不同的seed(即将输入加上一个偏移量)。

添加元素(Add):

	使用2个哈希函数将元素映射到位数组中的两个位置,并将相应位置的值设为true。

检查元素(Check):

	使用与添加元素相同的2个哈希函数,检查这两个位置的值是否都为true,如果都为true,则认为该元素可能存在。

示例运行结果:

	Check 'hello': true
	Check 'world': true
	Check 'golang': true
	Check 'python': true
	Check 'java': false

当我们调用bf.Add(“hello”)时,"hello"字符串被两个哈希函数映射到了位数组中的两个位置,并将这两个位置的值设为true。
当我们调用bf.Check(“hello”)时,程序会检查这两个位置是否都为true,如果是,则认为"hello"可能存在。
“python” 和 “java” 没有被添加到布隆过滤器中,所以它们的检查结果预期都应该为false。
但是实际上python为true,这就是布隆过滤器的误报问题。

3 小结

布隆过滤器允许一定的假阳性。

例如,检查一个从未插入的元素可能会返回true,因为哈希函数可能会碰巧将它映射到位数组中所有为true的位置。
但是如果布隆过滤器返回false,可以确定该元素不在集合中。

可以优化的功能点,

	可以增加哈希函数的数量(K值),以提高布隆过滤器的准确性。
增加位数组的长度,可以减少哈希冲突和假阳性的概率。

1.增加和查询元素的时间复杂度为:O(K),(K为哈希函数的个数,一般比较小),与数据量大小无关
2.哈希函数相互之间没有关系,方便硬件并行运算
3.布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势
4.在能够承受一定的误判时,布隆过滤器比其他数据结构有很大的空间优势
5.数据量很大时,布隆过滤器可以表示全集,其他数据结构不能表示全集
6.使用同一组散列函数的布隆过滤器可以进行交、并、差运算

但是它的缺点也很明显。

1.有误判率
2.不能获取元素本身
3.一般情况下不能从布隆过滤器中删除元素
4.如果采用计数方式删除,可能会存在计数回绕问题

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。