位图法就是利用位数组来存储数据状态,其优点就是节省数据存储空间。
布隆过滤器(Bloom Filter)是一种空间效率高的概率数据结构,由 Burton·Howar·Bloom 于1970年构思,用于测试元素是否是一组元素的成员。它实际上就是由一个很长的二进制向量(bitmap)和一系列随机映射函数(哈希函数)组成,将一系列数据通过一组哈希函数映射到位图数组中,以表示该数据存在。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
位图法(bitmap)
位图法就是利用位数组来存储数据状态,其优点就是节省数据存储空间。如下图所示:2 byte
的位数组存储数字0,1,5,11。
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
15 | 14 | 13 | 12 | 11 | 10 | 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 1 |
位运算
1 | 1 byte (字节)= 8 bit(位); |
2 | 1 KB = 1024 B(字节); |
3 | 1 MB = 1024 KB; |
4 | 1 GB = 1024 MB; |
5 | 1 TB = 1024 GB; |
与(&) | 0&0=0 | 1&0=0 | 0&1=0 | 1&1=1 |
或(|) | 0|0=0 | 1|0=1 | 0|1=1 | 1|1=1 |
异或(^) | 0^0=0 | 1^0=1 | 0^1=1 | 1^1=0 |
左移运算
左移运算符m<<n表示把m左移n位。
在左移n位的时候,最左边的n位将被丢弃,同时在最右边补上n个0。
1 | 00001010<<2=00101000 |
2 | 10001010<<3=01010000 |
右移运算
右移运算符m>>n表示把m右移n位。
在右移n位的时候,最右边的n位将被丢弃。左边的处理情况分两种:
- 如果数字是一个无符号数值,则用0填补最左边的n位。
- 如果数字是一个有符号数值,则用数字的符号位填补最左边的n位。也就是说,如果数字原先是一个正数,则右移之后在最左边补n个0,如果数字原先是负数,则右移之后在最左边补n个1。
1 | 00001010>>2=00000010 |
2 | 10001010>>3=11110001 |
位图法实现
1 | typedef char bitmap_type; |
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
布隆过滤器
布隆过滤器(Bloom Filter)是一种空间效率高的概率数据结构,由 Burton·Howar·Bloom 于1970年构思,用于测试元素是否是一组元素的成员。它实际上就是由一个很长的二进制向量(bitmap)和一系列随机映射函数(哈希函数)组成,将一系列数据通过一组哈希函数映射到位图数组中,以表示该数据存在。它的优点是空间效率和查询时间都远远超过一般的算法,缺点是有一定的误识别率和删除困难。
布隆过滤器比较常用的场景有:
- 网页url去重
- 垃圾邮件判别
- 数据库防止雪崩击穿
- 查询是否存在
False Positives
布隆过滤器器的误识别率(假正例False Positives)是指Bloom Filter认为某一元素存在于位集合中,但是实际上该元素不在集合中。
但是布隆过滤器器不存在识别错误(假反例False Negatives)的情况,即某个位元素不存在该位集合中,那么Bloom Filter不会判断该元素在集合中。
数学推导
假设:位数组大小为m
,哈希函数个数为k
,插入元素数量为n
:
- 位数组中某一特定的位在进行元素插入后没有被插入的概率为:$1 - \frac{1}{m} $
- 在k次Hash操作后,该位置都没有被插入的概率为:$(1 - \frac{1}{m})^k$
- 插入n个元素后,某一位任然没有被插入的概率为:$(1-\frac{1}{m})^kn $
- 插入n个元素后,该位被插入的概率为:$1-(1-\frac{1}{m})^kn$
现在检测一个不在集合里的元素。经过哈希之后的这k个数组位置都是1的概率为:
$$(1-[1-\frac{1}{m}]^{kn})^k \approx (1-e^{-\frac{kn}{m}})^k $$
Hash最佳个数
对与给定的m和n,使误报率最小的k值为:$k=\frac{m}{n}\ln$ ,此时误报率为:$\ln p = -\frac{m}{n}(\ln 2)^2$ 。
bloom filter实现
1 | typedef unsigned int (*hashfunc_t)(const char *); |
2 | typedef struct { |
3 | size_t asize; |
4 | unsigned char *a; |
5 | size_t nfuncs; |
6 | hashfunc_t *funcs; |
7 | } BLOOM; |
8 | |
9 |
|
10 |
|
11 | |
12 | unsigned int sax_hash(const char *key) |
13 | { |
14 | unsigned int h = 0; |
15 | |
16 | while(*key) |
17 | h^=(h<<5)+(h>>2)+(unsigned char)*key++; |
18 | |
19 | return h; |
20 | } |
21 | |
22 | unsigned int sdbm_hash(const char *key) |
23 | { |
24 | unsigned int h = 0; |
25 | |
26 | while(*key) |
27 | h=(unsigned char)*key++ + (h<<6) + (h<<16) - h; |
28 | |
29 | return h; |
30 | } |
31 | |
32 | //bloom_create(2500000, 2, sax_hash, sdbm_hash); |
33 | |
34 | BLOOM *bloom_create(size_t size, size_t nfuncs, ...) |
35 | { |
36 | BLOOM *bloom; |
37 | va_list l; |
38 | int n; |
39 | |
40 | if(!(bloom=malloc(sizeof(BLOOM)))) return NULL; |
41 | if(!(bloom->a=calloc((size+CHAR_BIT-1)/CHAR_BIT, sizeof(char)))) { |
42 | free(bloom); |
43 | return NULL; |
44 | } |
45 | if(!(bloom->funcs=(hashfunc_t*)malloc(nfuncs*sizeof(hashfunc_t)))) { |
46 | free(bloom->a); |
47 | free(bloom); |
48 | return NULL; |
49 | } |
50 | |
51 | va_start(l, nfuncs); |
52 | for(n=0; n<nfuncs; ++n) { |
53 | bloom->funcs[n]=va_arg(l, hashfunc_t); |
54 | } |
55 | va_end(l); |
56 | |
57 | bloom->nfuncs=nfuncs; |
58 | bloom->asize=size; |
59 | |
60 | return bloom; |
61 | } |
62 | |
63 | int bloom_destroy(BLOOM *bloom) |
64 | { |
65 | free(bloom->a); |
66 | free(bloom->funcs); |
67 | free(bloom); |
68 | |
69 | return 0; |
70 | } |
71 | |
72 | int bloom_add(BLOOM *bloom, const char *s) |
73 | { |
74 | size_t n; |
75 | |
76 | for(n=0; n<bloom->nfuncs; ++n) { |
77 | SETBIT(bloom->a, bloom->funcs[n](s)%bloom->asize); |
78 | } |
79 | |
80 | return 0; |
81 | } |
82 | |
83 | int bloom_check(BLOOM *bloom, const char *s) |
84 | { |
85 | size_t n; |
86 | |
87 | for(n=0; n<bloom->nfuncs; ++n) { |
88 | if(!(GETBIT(bloom->a, bloom->funcs[n](s)%bloom->asize))) return 0; |
89 | } |
90 | |
91 | return 1; |
92 | } |