新增了布隆过滤器bloomFilter的注解,修正了位图bitmap中出现的位运算导致的溢出问题

This commit is contained in:
hlccd 2021-12-28 22:45:56 +08:00 committed by GitHub
parent fb0b93e0ef
commit 325fdcc21b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 183 additions and 1 deletions

View File

@ -179,7 +179,7 @@ func (bm *bitmap) Check(num uint) (b bool) {
return false return false
} }
//判断第num是否为1,为1返回true,否则为false //判断第num是否为1,为1返回true,否则为false
if bm.bits[num/64]&(1<<num%64) > 0 { if bm.bits[num/64]&(1<<(num%64)) > 0 {
return true return true
} }
return false return false

View File

@ -0,0 +1,182 @@
github仓库存储地址https://github.com/hlccd/goSTL
### 概述
布隆过滤器bloom filter,它实际上是一个很长的二进制向量和一系列随机映射函数。布隆过滤器可以用于**寻找**一个元素是否在一个集合中但由于元素的通过hash映射转化到集合内的所以存在误差即可以百分百判断其不存在但不能确定其一定存在。它的优点是空间效率和查询时间都比一般的算法要好的多缺点是有一定的**误识别率**和**删除困难**。
### 原理
如果想要判断一个元素是不是在一个集合里除了可以将所有元素保存起来通过比较确定外也可以通过将元素转化为一个hash值放入哈希表内通过hash函数将元素转化为hash值该方案可以通过**位图**的形式存贮当判断其是否存在的时候只需要在位图上找到这个点是否为1即可判断其是否存在。极大的提高了空间利用率同时减小了时间开销但带来了一定的**识别错误率**识别错误主要由hash冲突导致。
相比于其它的数据结构,布隆过滤器在空间和时间方面都有巨大的优势。布隆过滤器存储空间和插入/查询时间都是常熟级别。另外,布隆过滤器不需要存储元素本身,在某些对保密要求非常严格的场合有优势。同时,布隆过滤器可以用来表示全集,这是其他数据结构所不能实现的。布隆过滤器可以表示全集,其它任何数据结构都不能。
对于出现hash冲突的情况来说它是会出现错误识别的当然一般来说并不会有很多。对于此类问题可以通过建立白名单的方式去存储可能误判的元素即对hash使用开放寻址法但一般来说并不需要。
同时,考虑到识别错误的问题,删除的操作几乎是不应当实现的,所以本次实现中主要实现**添加和查找**操作,当然也包含清空操作。
### 实现
bloomFilter布隆过滤器结构体包含其用于存储的uint64元素切片选用uint64是为了更多的利用bit位。
```go
type bloomFilteror interface {
Insert(v interface{}) //向布隆过滤器中插入v
Check(v interface{}) (b bool) //检查该值是否存在于布隆过滤器中,该校验存在误差
Clear() //清空该布隆过滤器
}
```
#### 接口
```go
type bloomFilteror interface {
Insert(v interface{}) //向布隆过滤器中插入v
Check(v interface{}) (b bool) //检查该值是否存在于布隆过滤器中,该校验存在误差
Clear() //清空该布隆过滤器
}
```
#### hash
传入一个虚拟节点id和实际结点计算出它的hash值逐层访问并利用素数131计算其hash值随后返回。
该hash函数可自行传入也可使用库内函数
```go
type Hash func(v interface{}) (h uint32)
func hash(v interface{}) (h uint32) {
h = uint32(0)
s := fmt.Sprintf("131-%v-%v", v,v)
bs := []byte(s)
for i := range bs {
h += uint32(bs[i]) * 131
}
return h
}
```
#### New
新建一个bloomFilter布隆过滤器容器并返回初始bloomFilter的切片数组为空。
```go
func New(h Hash) (bf *bloomFilter) {
if h == nil {
h = hash
}
return &bloomFilter{
bits: make([]uint64, 0, 0),
hash: h,
}
}
```
#### Insert
以bloomFilter布隆过滤器容器做接收者先将待插入的value计算得到其哈希值hash再向布隆过滤器中第hash位插入一个元素(下标从0开始)当hash大于当前所能存储的位范围时,需要进行扩增若要插入的位比冗余的多不足2^16即1024*64时,则新增1024个uint64否则则直接增加到可以容纳第hash位的位置,以此可以提高冗余量,避免多次增加。
```go
func (bf *bloomFilter) Insert(v interface{}) {
//bm不存在时直接结束
if bf == nil {
return
}
//开始插入
h := bf.hash(v)
if h/64+1 > uint32(len(bf.bits)) {
//当前冗余量小于num位,需要扩增
var tmp []uint64
//通过冗余扩增减少扩增次数
if h/64+1 < uint32(len(bf.bits)+1024) {
//入的位比冗余的多不足2^16即1024*64时,则新增1024个uint64
tmp = make([]uint64, len(bf.bits)+1024)
} else {
//直接增加到可以容纳第num位的位置
tmp = make([]uint64, h/64+1)
}
//将原有元素复制到新增的切片内,并将bm所指向的修改为扩增后的
copy(tmp, bf.bits)
bf.bits = tmp
}
//将第num位设为1即实现插入
bf.bits[h/64] ^= 1 << (h % 64)
}
```
#### Check
以bloomFilter布隆过滤器容器做接收者将待查找的值做哈希计算得到哈希值h检验第h位在位图中是否存在当h大于当前所能存储的位范围时,直接返回false否则判断第h为是否为1,为1返回true,否则返回false利用布隆过滤器做判断存在误差,即返回true可能也不存在,但返回false则必然不存在。
```go
func (bf *bloomFilter) Check(v interface{}) (b bool) {
//bf不存在时直接返回false并结束
if bf == nil {
return false
}
h := bf.hash(v)
//h超出范围,直接返回false并结束
if h/64+1 > uint32(len(bf.bits)) {
return false
}
//判断第num是否为1,为1返回true,否则为false
if bf.bits[h/64]&(1<<(h%64)) > 0 {
return true
}
return false
}
```
#### Clear
以bloomFilter布隆过滤器容器做接收者清空整个布隆过滤器。
```go
func (bf *bloomFilter) Clear() {
if bf == nil {
return
}
bf.bits = make([]uint64, 0, 0)
}
```
### 使用示例
```go
package main
import (
"fmt"
"github.com/hlccd/goSTL/data_structure/bloomFilter"
)
func hash(v interface{}) uint32 {
return uint32(v.(int))
}
func main() {
bf := bloomFilter.New(nil)
for i := 0; i < 10; i++ {
bf.Insert(i)
}
for i := 0; i < 15; i++ {
fmt.Println(i,bf.Check(i))
}
}
```
> 0 true
> 1 true
> 2 true
> 3 true
> 4 true
> 5 true
> 6 true
> 7 true
> 8 true
> 9 true
> 10 false
> 11 false
> 12 false
> 13 false
> 14 false