一.作用:
判断一个元素是否存在一个集合中
二.基本原理通俗:
当一个元素被加入集合时,通过K个Hash函数将这个元素映射成一个一维的布尔型的数组中的K个点,把它们置为1。检索时,我们只要看看这些点是不是都是1就(大约)知道集合中有没有它了:如果这些点有任何一个0,则被检元素一定不在;如果都是1,则被检元素很可能在。这就是布隆过滤器的基本思想。
三.算法原理详细:
- n个要添加的元素
- k个hash函数
- m位的空的bitArray
- 添加一个元素key时,用k个hash函数计算出k个散列值,并把bitArray对应的比特位置为1
- 判断一个元素key是否存在时,用k个hash函数计算出k个散列值,并查询bitArray中对应的比特位;如果至少有一位不为1,则一定不在集合中;如果全部都为1,则认为在集合中(存在误判)
四.优缺点
优点:
新增,查询速度足够快,内存小,代码简单
缺点:
有一定误判率且随数据增加而增加; 不支持删除
五.简单代码实现
package scala02
import scala.collection.mutable.ArrayBuffer
import scala.math.abs
import scala.util.hashing.MurmurHash3
object BloomFilter {
private val BYTE_SIZE: Int = 8
private var m: Int = _ //m 为要存的比特数组长度
private var k: Int = _ //k 为哈希函数的个数
private var bitmapCharArray: Array[Char] = _
private var seedArray: Array[Int] = _
/**
* 生成空的bitArray
* @param m
* @return
*/
private def generateEmptyBitmap(m:Int): Array[Char] ={
val charNum = (m.toDouble/BYTE_SIZE).ceil.toInt //ceil 不小于该浮点数的最小整数, (2.1).ceil则为3.0
val charArrayBuffer = ArrayBuffer.empty[Char]
val char=0x00.toChar
for (elem <- 0 until charNum ) {//0 until len 或者 0 to len-1
charArrayBuffer.append(char)
}
charArrayBuffer.toArray
}
/**
* 判断字符串是否可能存在于过滤器中
*
* @param str
* @return
*/
def exists(str: String): Boolean = {
var flag = true
var s = 0
while (s < k) {
val pos = hash(str, seedArray(s))
if (!getBit(pos)) {
flag = false
s = k
}
s = s + 1
}
flag
}
/**
* 将字符串添加到过滤器中
*
* @param str
*/
def put(str: String) = {
seedArray.foreach(seed => {
val pos = hash(str, seed)
setBit(pos)
})
}
/**
* 将bitmap的第pos个bit置为1
*
* @param pos
*/
private def setBit(pos: Int): Unit = {
val charPos = getCharPos(pos)
val char = bitmapCharArray(charPos)
val bitPos = pos - charPos * BYTE_SIZE
val byte = char.toByte
val mask = 0x01 << bitPos
val or = byte | mask
bitmapCharArray(charPos) = or.toChar
}
/**
* 基于MurmurHash3算法计算字符串的hash值
*
* @param str
* @param seed hash种子
* @return 取值范围 0 ~ m-1
*/
private def hash(str: String, seed: Int): Int = {
abs(MurmurHash3.stringHash(str, seed)) % m
}
/**
* 读取bitmap的第pos个bit
*
* @param pos
*/
private def getBit(pos: Int): Boolean = {
val charPos = getCharPos(pos)
val char = bitmapCharArray(charPos)
val bitPos = pos - charPos * BYTE_SIZE
val byte = char.toByte
val mask = 0x01 << bitPos
val and = byte & mask
if (0 == and) false else true
}
/**
* 获取第pos个bit对应的char的位置(从0开始编号)
*
* @param pos
* @return 0 ~ m/BYTE_SIZE-1
*/
private def getCharPos(pos: Int): Int = {
(pos.toDouble / BYTE_SIZE).toInt
}
/**
* 判断n是否为质数
*
* @param n
* @return
*/
private def isPrime(n: Int) = {
var flag = true
for (i <- 2 to n - 1) {
if (n % i == 0) flag = false
}
flag
}
}
六.参考链接
布隆过滤器原理及数学推导
https://www.cnblogs.com/allensun/archive/2011/02/16/1956532.html