布隆过滤
什么是布隆过滤
回答这个问题之前,先回答布隆过滤能干什么?
布隆过滤主要被用于判断一个元素是否是否属于某个数据集,通常这个数据集比较大,不能放入内存中。
但是布隆过滤会存在误判,就是 把某个不属于这个数据集的元素 误判为 这个数据集的元素 , 但是 如果某个元素 属于这个数据集 布隆过滤判断这个元素是否属于这个数据集 不会存在误判。
现在我们再来回到什么是布隆过滤
布隆过滤 有几大要素
二进制数组
指纹数组
训练样本数
Hash函数
就是根据 训练的样本 建立一个二进制数组 用来存储 样本中每个元素的特征
怎么得到每个元素的特征 , 以及怎么把这些特征存储到二进制数组中,答案是hash函数
为了减少误判率,建了一个指纹数组,一般为8个,分八次 应用哈希函数 得到八个特征值,将这八个特征值映射到二进制数组对应的索引 , 8 个索引值对应的地方 的数被置为1 从而提取到了 这个元素的特征值,并把它存储到二进制数组中了
判断的时候也是同样 , 当被判断的元素的8个特征值对应的二进制数组索引上的数都为1 ,判断这个元素属于这个数据集 否则 不属于
误判率
(https://blog.csdn.net/sunnyyoona/article/details/43482843)
我提取到其中的一行文字
m为二进制数组的长度
n为训练样本中元素的个数
k为指纹数组的长度
表格中的数据为误判率
m/n | k=1 | k=2 | k=3 | k=4 | k=5 | k=6 | k=7 | k=8 |
---|---|---|---|---|---|---|---|---|
16 | 6% | 1% | 0.5% | 0.2% | 0.1% | 0.09% | 0.07% | 0.05% |
16 表示 一个元素的特征用16个bit去记录它
k=8 表示这16 个bit位中有8个bit位被置为了1 其余八个还是0
如果两个元素的在这16个bit位中相同的8个bit位都被置为1 这两个元素有相同的特征值
布隆过滤就分不清这两个元素了 , 这就是误判存在的原因 如果相同的元素 他们的特征值一定相同 , 所以在相同元素的判断上不存在误判
实现
话说的太多,容易干,得整点硬菜
下面我就用scala 和 python 来实现布隆过滤
python code
import random
import time
"""
将一个十进制数组转化为 一个数 这个数 为2 的指数幂 大于这个十进制的数且最接近这个十进制数的 数
"""
def get_2_number(ten_number):
bit_num = 1
while True:
re = ten_number >> bit_num
if re <= 1:
break
else:
bit_num += 1
return 2 << bit_num
"""
简单hash 函数 将 string 中的 特征提取出来 映射出 bit_array 的索引
"""
def simple_hash(string, finger_print, size):
count = 1
for ch in string:
count = count * finger_print + ord(ch)
return count & (size - 1)
class bloom_filter:
"""
train_number 训练样本数
finger_prints 指纹数组
size 二进制数组的长度
hashFun 哈希函数 用于提取单词的特征
"""
def __init__(self, train_number, finger_prints):
self.train_number = train_number
self.finger_prints = finger_prints
self.size = get_2_number(train_number) << 4
self.bit_array = []
for i in range(0, self.size, 1):
self.bit_array[i] = False
self.hashFun = simple_hash
def __init__(self, train_number):
self.train_number = train_number
'''
要每次产生随机数相同就要设置种子,相同种子数的Random对象,相同次数生成的随机数字是完全相同的
'''
random.seed(time.time)
""" 得到二进制数组的长度 """
self.size = get_2_number(train_number) << 4
""" 生成指纹数组 """
self.finger_prints = []
self.finger_prints.append(random.randint(1, self.size) % train_number)
exists = False
index = 1
while index < 8:
num = random.randint(1, self.size) % train_number
for j in range(0, index, 1):
if self.finger_prints[j] == num:
exists = True
if exists:
exists = False
else:
self.finger_prints.append(num)
index += 1
""" 初始化二进制数组 """
self.bit_array = []
for i in range(0, self.size, 1):
self.bit_array.append(False)
""" 设定hash函数 """
self.hashFun = simple_hash
""" 提取样本元素的特征值 并将特征 存储到二进制数组 """
def add(self, string):
for finger_print in self.finger_prints:
self.bit_array[self.hashFun(string, finger_print, self.size)] = True
""" 判读元素是否在 数据集中 """
def is_contain(self, string):
contain = True
for finger_print in self.finger_prints:
if not self.bit_array[self.hashFun(string, finger_print, self.size)]:
contain = False
return contain
bloom = bloom_filter(3571)
""" train """
file = open("C:\\Users\\24590\\IdeaProjects\\PythonJava\\src\\com\\company\\bigdata\\xunlian", 'r')
for line in file.readlines():
bloom.add(line.replace('\n', ''))
""" judge """
dics = ["act",
"acting",
"activity",
"activate",
"actualize",
"enact",
"actt",
"actingt",
"activityt",
"activatet",
"actualizet",
"enactt"]
# for word in dics:
# bloom.add(word)
for word in dics:
print(word + " is in or not in bloom :" + str(bloom.is_contain(word)))
scala code
package com.huawei.bigdata
import scala.util.Random
import scala.util.control.Breaks
class BloomFilterScala[T](trainNumber:Int){
val random=new Random
random.setSeed(System.currentTimeMillis)
val size=getBitArraySize << 4
var fingerPrints=getFingerPrints
val bitArray:Array[Boolean]=new Array[Boolean](size)
var hashFun:(T,Int)=>Int=(word:T,fingerPrint:Int)=>{
(fingerPrint*word.hashCode()) & (size-1)
}
def add(element:T): Unit ={
for(fingerPrint<-fingerPrints){
val num:Int=hashFun(element,fingerPrint)
if(num <0 || num >=size){
throw new ArrayIndexOutOfBoundsException(num +"is not between 0 to " + (size-1) )
}else
bitArray(num)=true
}
}
def isContain(element: T): Boolean ={
var contain=true
for(fingerPrint<-fingerPrints){
val num:Int=hashFun(element,fingerPrint)
if(num <0 || num >=size){
throw new ArrayIndexOutOfBoundsException(num +"is not between 0 to " + (size-1) )
}else
if(!bitArray(num)) contain=false
}
contain
}
def getFingerPrints ={
val array=new Array[Int](8)
array(0)=(random.nextInt(size)+1) % trainNumber
var index=1
var exists=false
var num=0
while(index<8){
num=(random.nextInt(size)+1)%trainNumber
for(j <- (0 to index-1)){
if(array(j)==num) exists=true
}
if(exists)
exists=false
else {
array(index) = num
index += 1
}
}
array
}
def getBitArraySize: Int ={
var bitNum=1
val break=new Breaks
break.breakable(
while(true){
val re=trainNumber >> bitNum
if(re <=1) break.break
else bitNum+=1
}
)
2 << bitNum
}
}
import sys.process._
object test1 {
def main(args: Array[String]): Unit = {
val bloom:BloomFilterScala[String]=new BloomFilterScala[String](3571)
bloom.hashFun=(word:String,fingerPrint:Int)=>{
var count=1
for(w<-word){
count=count*fingerPrint+w
}
count & ( bloom.size -1)
}
val lines="cat C:\\Users\\24590\\IdeaProjects\\MRShuffle\\src\\com\\huawei\\bigdata\\xunlian ".lines_!
lines.foreach(line=>{
bloom.add(line)
})
val list= Array[String]("act",
"acting",
"activity",
"activate",
"actualize",
"enact",
/*******************************/
"actt",
"actingt",
"activityt",
"activatet",
"actualizet",
"enactt")
list.foreach(word=>{
val result=bloom.isContain(word)
println(s"$word is in or not in 数据集 : $result")
})
}
}