目录
前言
A.建议
1.学习算法最重要的是理解算法的每一步,而不是记住算法。
2.建议读者学习算法的时候,自己手动一步一步地运行算法。
B.简介
布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,用于判断一个元素是否可能存在于集合中,但可能出现一定的误判率(false positive)。
一 代码实现
在C语言中实现布隆过滤器通常包括以下几个关键步骤:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
// 假设我们使用bit数组和几个哈希函数
#define BITS_PER_FILTER 1 << 24 // 假设我们创建一个24位的布隆过滤器
#define HASH_FUNCTIONS 5 // 假设我们使用5个独立的哈希函数
// 布隆过滤器结构体定义
typedef struct {
unsigned char *bits; // 位数组
int size; // 位数组大小
int hash_count; // 哈希函数数量
} BloomFilter;
// 创建布隆过滤器
BloomFilter *create_bloom_filter(int expected_elements, double false_positive_rate) {
int size = (int)(-expected_elements * log(false_positive_rate) / (log(2) * log(2)));
size = (size + 7) / 8 * 8; // 保证大小为8的倍数
int optimal_hash_count = (int)(size * log(2) / expected_elements);
BloomFilter *filter = malloc(sizeof(BloomFilter));
filter->bits = calloc(size, sizeof(unsigned char));
filter->size = size;
filter->hash_count = optimal_hash_count > 0 ? optimal_hash_count : HASH_FUNCTIONS;
return filter;
}
// 使用多个哈希函数计算哈希值并设置位数组
void bloom_filter_insert(BloomFilter *bf, const char *key) {
for (int i = 0; i < bf->hash_count; ++i) {
int hash_val = custom_hash(key, i); // 自定义哈希函数,返回哈希值
int bit_index = hash_val % bf->size; // 将哈希值映射到位数组的索引
bf->bits[bit_index / 8] |= (1 << (bit_index % 8)); // 设置位数组相应位置为1
}
}
// 判断一个元素是否可能在布隆过滤器中
bool bloom_filter_maybe_contains(BloomFilter *bf, const char *key) {
bool maybe_present = true;
for (int i = 0; i < bf->hash_count && maybe_present; ++i) {
int hash_val = custom_hash(key, i);
int bit_index = hash_val % bf->size;
maybe_present &= (bf->bits[bit_index / 8] & (1 << (bit_index % 8))) != 0; // 如果所有哈希位置均为1,则可能存在于集合中
}
return maybe_present;
}
// 自定义哈希函数,此处仅作示意,实际应用中应使用高质量的哈希函数
int custom_hash(const char *key, int index) {
// 此处省略实际哈希函数实现,应确保哈希函数速度快且分布均匀
return simple_hash_function(key) + index * PRIME_MODIFIER; // 伪代码,实际需要实现稳定的哈希算法
}
// 释放布隆过滤器资源
void destroy_bloom_filter(BloomFilter *bf) {
free(bf->bits);
free(bf);
}
// 仅作示意,实际需要实现合适的哈希函数
int simple_hash_function(const char *key) {
// 这里只是一个简单的哈希函数示例,实际中应当使用更强大的哈希函数
int hash = 0;
while (*key) {
hash = hash * 31 + *key++;
}
return hash % BITS_PER_FILTER;
}
// 主函数示例
int main() {
BloomFilter *bf = create_bloom_filter(1000, 0.01);
bloom_filter_insert(bf, "apple");
bloom_filter_insert(bf, "banana");
printf("%s is maybe in the set? %s\n", "apple", bloom_filter_maybe_contains(bf, "apple") ? "yes" : "no");
printf("%s is maybe in the set? %s\n", "cherry", bloom_filter_maybe_contains(bf, "cherry") ? "yes" : "no");
destroy_bloom_filter(bf);
return 0;
}
请注意,上述代码仅作为示例,实际应用中需要实现正确的哈希函数,并且应尽量选择质量高、碰撞少的哈希函数。同时,布隆过滤器的大小、哈希函数数量等参数需要根据预期元素数量和允许的误判率进行调整。在实际C语言程序中,还需要适配实际的哈希函数库。
二 时空复杂度
布隆过滤器(Bloom Filter)是一种空间效率极高的概率型数据结构,其时空复杂度如下:
A.空间复杂度(Space Complexity):
布隆过滤器的空间复杂度主要指的是存储元素所需要的位数组大小。假设有m
位的位数组和k
个独立哈希函数,当期望插入n
个元素且达到一定误判率时,布隆过滤器的理想大小(即最小位数组大小)可以通过公式估算得出,一般情况下空间复杂度为O(m)
。实际应用中,m
通常远小于n
,因此布隆过滤器的空间效率很高。
B.时间复杂度(Time Complexity):
- 插入操作(Insertion):插入一个元素到布隆过滤器中,需要调用
k
次哈希函数并将位数组相应位置设为1,因此插入操作的时间复杂度为O(k)
。 - 查询操作(Query):查询一个元素是否可能存在于布隆过滤器中,同样需要调用
k
次哈希函数并检查位数组相应位置,因此查询操作的时间复杂度也为O(k)
。
C.总结:
综上所述,布隆过滤器的主要优势在于其空间效率极高,插入和查询操作的时间复杂度较低,均为常数级。然而,代价是存在一定的误判率,即可能出现假阳性(False Positive),但不会出现假阴性(False Negative)。
三 优缺点
布隆过滤器(Bloom Filter)是一种空间效率较高的概率型数据结构,用于判断一个元素是否可能存在于集合中。以下是其主要优缺点:
A.优点:
-
空间效率高:相较于传统的数据结构,如哈希表,布隆过滤器所需的存储空间非常小。特别是对于大型数据集,它可以极大地节省存储资源。
-
查询速度快:布隆过滤器的查询操作(判断一个元素是否可能存在于集合中)的时间复杂度为常数时间O(k),其中k为哈希函数的个数。
-
无假阴性:一旦布隆过滤器报告一个元素存在,它必定在集合中(不过要注意这是一个概率声明,存在一定的误判率)。这意味着布隆过滤器不会漏过任何真正的成员,只是可能会将一些非成员误认为成员(假阳性)。
-
分布式友好:由于布隆过滤器的简洁性,它非常适合在网络中进行传输和在分布式系统中使用。
B.缺点:
-
误报率(False Positives):布隆过滤器最大的缺点是存在一定的误报率,即它可能会错误地报告一个元素存在于集合中,尽管实际上该元素不在。误报率随着存储空间的压缩(即位数组的大小相对于实际元素数量的减小)而增加。
-
不可删除元素:一旦元素被加入到布隆过滤器中,就不能轻易地从过滤器中删除,因为单个位对应的是多个可能的元素,删除一个元素会导致其他元素的状态发生错误。
-
无法精确查询:布隆过滤器只能回答“可能在集合中”或“肯定不在集合中”的问题,而不能准确地说出一个元素绝对存在于集合中。
-
哈希冲突:布隆过滤器的性能严重依赖于所使用的哈希函数的质量。如果哈希函数不够优秀,可能会导致更多冲突和更高的误报率。
C.总结:
因此,布隆过滤器在诸如垃圾邮件过滤、缓存穿透防御、去重等对空间效率要求高且能容忍一定误报率的场景中表现优异。但在需要精确结果的场合下,它不是一个合适的选择。
四 现实中的应用
布隆过滤器(Bloom Filter)在现实中的应用非常广泛,特别是在那些需要高效地判断数据是否存在,且对偶尔的误判有一定容忍度的场景中。以下是一些具体的应用实例:
-
网页爬虫:
- 爬虫在抓取网页时,可以使用布隆过滤器存储已抓取过的URL,避免重复抓取相同的网页,节约网络资源和计算资源。
-
缓存系统:
- 在大型分布式缓存系统中,布隆过滤器可以用来检测一个查询请求是否可能命中缓存,对于布隆过滤器返回肯定不存在的结果,系统可以直接跳过昂贵的数据库查询,大大提升了效率。但是,如果布隆过滤器判断可能存在的结果,系统还需进一步查询实际的缓存或数据库以确认。
-
数据库索引:
- 在数据库查询优化中,布隆过滤器可以作为一种轻量级索引,快速排除不可能存在的记录,减轻主键查询的压力。
-
垃圾邮件过滤:
- 邮件服务商可以使用布隆过滤器存储已知的垃圾邮件地址或关键字,对新邮件进行快速筛选,减少垃圾邮件进入用户的邮箱。
-
实时数据分析:
- 在实时流处理系统中,布隆过滤器可以用来过滤掉已知或无关的数据条目,减少后续处理负载。
-
去重处理:
- 在处理大规模数据集时,布隆过滤器可以用来检测重复数据,尤其是当存储全部数据集或维护常规哈希表太耗费资源时。
-
生物信息学:
- 在基因组学研究中,布隆过滤器可以用来快速判断一个碱基序列是否可能出现在参考数据库中,有助于提高比对效率。
-
社交网络:
- 在好友推荐或反作弊系统中,布隆过滤器可以帮助系统迅速识别出可能是虚假账号或已有好友关系的用户。
-
广告投放:
- 广告平台可以利用布隆过滤器存储已展示过广告的用户ID,避免对同一用户短时间内重复展示同一条广告。
总之,布隆过滤器凭借其高效的空间占用和查询速度,在众多需要快速判定数据存在性的场景中发挥了重要作用。尽管它可能会带来一定的误报率,但在合理设计和参数选择下,往往能以较小的成本换取显著的性能提升。