计数布隆过滤器 - 介绍和实现
本文将讨论计数布隆过滤器的实现。以下是将要讨论的主题。
- 为什么我们需要概率数据结构?
- 有哪些可以应用的领域?
- 什么是会员类型的问题?
- 计数布隆过滤器与传统布隆过滤器有何不同?
- 介绍计数布隆过滤器和实现。
为什么我们需要概率数据结构?
在这个大数据时代,每家公司每天都在处理大量数据。例如,IBM 曾表示人们每天创建 2.5 万亿字节的数据,这个数字还在不断增长,因此公司需要一些技术来快速了解数据以发现价值并采取行动。然而,包括数据结构和算法在内的传统技术在处理大数据时变得无效。此类问题的最佳解决方案是使用概率数据结构。
确定性数据结构还可以执行概率数据结构所做的所有操作,但仅限于低数据集。如果数据集太大而无法放入内存,那么确定性数据结构就会失败,根本不可行。
PDS(概率数据结构)总是提供近似的答案,但有可靠的方法来估计可能的错误。幸运的是,极低的内存要求、恒定的查询时间和可扩展性完全补偿了潜在的损失和错误,这些因素在大数据应用程序中变得至关重要。
我们适用于哪些领域?
如前所述,PDS 的优势出现在大数据问题中,例如
- 成员资格问题 我们需要找出特定元素是否存在于数据中?
- 在诸如查找数据中存在的唯一元素的数量之类的问题中。
- 计算整个数据中特定元素的频率。
什么是会员类型的问题?
数据集的成员资格问题是确定某个元素是否属于数据集的任务。与确定性数据结构不同,它会找到发生精确匹配的位置。在许多情况下,我们不需要知道集合中的哪个元素已匹配,只需知道匹配已完成,因此可以只存储元素的签名而不是整个值。
计数布隆过滤器与传统布隆过滤器有何不同?
经典布隆过滤器的主要缺点是无法进行删除操作,即只能插入元素,并且可以检查元素是否存在于数据中。即使有人尝试更改与 k 个位置相对应的位数组中的位,也可能导致误报。幸运的是,对于许多实际应用程序来说,缺少删除支持并不是问题,
计数布隆过滤器及其实现
支持删除的经典布隆过滤器最流行的扩展是计数布隆过滤器,由 Li Fan、Pei Cao、Jussara Almeida 和 Andrei Z. Broder 在 2000 年提出。计数布隆过滤器引入了 m 个计数器 {C j } 的数组m j=1对应于滤波器数组中的每一位。
计数布隆过滤器允许通过在每次添加元素时增加相应的计数器来近似每个元素在过滤器中出现的次数。相关的 CountingBloomFilter 数据结构包含一个位数组和长度为 m 的计数器数组,全部初始化为零。当一个新元素被插入到 CountingBloomFilter 中时,首先计算其对应的位位置,然后对于每个位置,我们增加相关的计数器,这些是用任何首选语言编码的一些基本思想。
Pseudo Code:
Input: Element x belongs to D
Input: Counting Bloom Filter with m counters {Cj}j=1m and k hash functions {hi}i=1k
Algo:
for i=1 to k do
j=hi(x)
Cj=Cj+1
Next to test whether an element is present or not we check the counters corresponding to k positions if the value of all the counters is greater than 0 implies the element is probably present.
Input: Element x belongs to D
Input: Counting Bloom Filter with m counters and k hash functions.
Algo:
for i=1 to k do
j=hi(x)
if CountingBloomFilter[j]<1 then
return False
return True
现在,最后是重要的删除部分。删除与插入非常相似,但相反。为了删除一个元素 x ,我们计算所有 k 个哈希值 h i = {h i (x )} k i=1并减少相应的计数器。
下面是上述算法的实现
Python3
import math
from fnvhash import fnv1a_32
from bitarray import bitarray
from bitarray.util import ba2int,int2ba
class CBloomFilter():
def __init__(self, n,Counter_size,bucket_size,no_hashfn):
self.n=n
self.N=Counter_size
self.m=bucket_size
self.k=no_hashfn
self.bit_array = []
for i in range(self.m):
count=bitarray(self.N)
count.setall(0)
self.bit_array.append(count)
def hash(self,item,seed):
return fnv1a_32(item.encode(),seed) % self.m
def add(self, item):
for i in range(self.k):
index = self.hash(item,i)
cur_val=ba2int(self.bit_array[index])
new_array=int2ba(cur_val+1,length=self.N)
self.bit_array[index]=new_array
def check(self, item):
for i in range(self.k):
index = self.hash(item,i)
cur_val=ba2int(self.bit_array[index])
if(not cur_val>0):
return False
return True
def remove(self,item):
if(self.check(item)):
for i in range(self.k):
index = self.hash(item,i)
cur_val=ba2int(self.bit_array[index])
new_array=int2ba(cur_val-1,length=self.N)
self.bit_array[index]=new_array
print('Element Removed')
else:
print('Element is probably not exist')
Python3
from counting_bloom_filter import CBloomFilter
from random import shuffle
n = 20 # no of items to add
N = 4 # size of the each counter
m = 150 # total number of the buckets
k = 5 # number of hash functions
cbloomf = CBloomFilter(n, N, m, k)
print("Size of bit array:{}".format(cbloomf.m))
print("Size of each bucket:{}".format(cbloomf.N))
print("Number of hash functions:{}".format(cbloomf.k))
# words to be added
word_present = ['abound', 'abounds', 'abundance',
'abundant', 'accessable',
'bloom', 'blossom', 'bolster', 'bonny',
'bonus', 'bonuses',
'coherent', 'cohesive', 'colorful',
'comely', 'comfort',
'gems', 'generosity', 'generous',
'generously', 'genial']
# word not added
word_absent = ['bluff', 'cheater', 'hate',
'war', 'humanity',
'racism', 'hurt', 'nuke',
'gloomy', 'facebook',
'geeksforgeeks', 'twitter']
for item in word_present:
cbloomf.add(item)
shuffle(word_present)
shuffle(word_absent)
test_words = word_present[:10] + word_absent
shuffle(test_words)
for word in test_words:
if cbloomf.check(word):
if word in word_absent:
print("'{}' is a false positive!".format(word))
else:
print("'{}' is probably present!".format(word))
else:
print("'{}' is definitely not present!".format(word))
在上面对元素进行散列的代码中,使用了广泛用于概率数据结构中的 FNV 散列函数(您也可以使用 murmur 散列函数)。 n 是您要输入过滤器的预期元素数。 Counter_size 是桶中每个计数器的位数。 bucket_size 是我们要在过滤器中使用的 m 个桶,最后,no_hashfn 是我们要使用的散列函数的数量。
现在用一些例子测试这个类,将文件保存为counting_bloom_filter.py 现在创建另一个文件进行测试。
Python3
from counting_bloom_filter import CBloomFilter
from random import shuffle
n = 20 # no of items to add
N = 4 # size of the each counter
m = 150 # total number of the buckets
k = 5 # number of hash functions
cbloomf = CBloomFilter(n, N, m, k)
print("Size of bit array:{}".format(cbloomf.m))
print("Size of each bucket:{}".format(cbloomf.N))
print("Number of hash functions:{}".format(cbloomf.k))
# words to be added
word_present = ['abound', 'abounds', 'abundance',
'abundant', 'accessable',
'bloom', 'blossom', 'bolster', 'bonny',
'bonus', 'bonuses',
'coherent', 'cohesive', 'colorful',
'comely', 'comfort',
'gems', 'generosity', 'generous',
'generously', 'genial']
# word not added
word_absent = ['bluff', 'cheater', 'hate',
'war', 'humanity',
'racism', 'hurt', 'nuke',
'gloomy', 'facebook',
'geeksforgeeks', 'twitter']
for item in word_present:
cbloomf.add(item)
shuffle(word_present)
shuffle(word_absent)
test_words = word_present[:10] + word_absent
shuffle(test_words)
for word in test_words:
if cbloomf.check(word):
if word in word_absent:
print("'{}' is a false positive!".format(word))
else:
print("'{}' is probably present!".format(word))
else:
print("'{}' is definitely not present!".format(word))
输出:
特性:
计数布隆过滤器继承了经典布隆过滤器的所有属性,包括误报错误估计和对 m 和 k 参考的最佳选择的建议。