利用bloom filter算法处理大规模数据过滤
Bloom Filter是由Bloom在1970年提出的一种快速查找算法,通过多个hash算法来共同判断某个元素是否在某个集合内。可以用于网络爬虫的url重复过滤、垃圾邮件的过滤等等。
它相比hash容器的一个优势就是,不需要存储元素的实际数据到容器中去来一个个的比较是否存在。
只需要对应的位段来标记是否存在就行了,所以想当节省内存,特别适合海量的数据处理。并且由于省去了存储元素和比较操作,所以性能也比基于hash容器的高了很多。
但是由于bloom filter没有去比较元素,只通过多个hash来判断唯一性,所以存在一定的hash冲突导致误判。误判率的大小由hash函数的个数、hash函数优劣、以及存储的位空间大小共同决定。
并且删除也比较困难,解决办法是使用其变种,带计数的bloom filter,这个这里就不多说了。
对于bloom filter算法的实现,相当简单:
首先分配一块固定的连续空间,大小是m个比特位(m/8+1个字节),然后再提供k个不同hash函数,同时对每个元素进行计算位索引。如果每个位索引对应的位都为1,则存在该元素,否则就是不存在。
可以看出,如果判断为不存在,那么肯定是不存在的,只有在判断为存在的时候,才会存在误判。
bloom filter主要的难点其实在于估算:
保证指定误判率的情况下,到底需要多少个hash函数,多少的存储空间。
首先来看下bloom filter的误判率计算公式:
假定有k个hash函数,m个比特位的存储空间,n个集合元素,则有误判率p:
p = (1 - ((1 - 1/ m) ^ kn))^k ~= (1 - e^(-kn/m))^k
根据这个,官方给出了一个计算k的最优解公式,使其满足给定p的情况下,存储空间达到最小:
k = (m / n) * ln2
把它带入概率公式得到:
p = (1 - e ^-((m/nln2)n/m))^(m/nln2)
简化为:
lnp = -m/n * (ln2)^2
因此,如果指定p,只需要满足如果公式,就可以得到最优解:
s = m/n = -lnp / (ln2 * ln2) = -log2(p) / ln2
k = s * ln2 = -log2(p)
理论值:
p < 0.1: k = 3.321928, m/n = 4.79
p < 0.01: k = 6.643856, m/n = 9.58
p < 0.001: k = 9.965784, m/n = 14.37
p < 0.0001: k = 13.287712, m/n = 19.170117
可以看出,这个确实能够在保证误判率的前提下,使其存储空间达到最小,但是使用的hash函数个数k
相对较多,至少也得4个,要满足p < 0.001,需要10个才行,这个对于字符串hash的计算来讲,性能损耗相当大的,实际使用中根本没法接受。
因此我们需要另外一种推到公式,可以认为指定p和k的情况下,来计算空间使用s=m/n的大小,这样我们在实际使用的时候,灵活性就大大提高了。
下面来看下,我自己推到出来的公式,首先还是依据误判率公式:
p = (1 - e^(-kn/m))^k
假定s=m/n,则有
p = (1 - e^(-k/s))^k
两边取导,得到:
lnp = k * ln(1 - e^(-k/s))
交换k:
(lnp) / k = ln(1 - e^(-k/s))
重新上e:
e^((lnp) / k) = 1 - e^(-k/s)
化简:
e^(-k/s) = 1 - e^((lnp) / k) = 1 - (e^lnp)^(1/k) = 1 - p^(1/k)
再求导:
-k/s = ln(1 - p^(1/k))
得出:
s = -k / ln(1 - p^(1/k))
假定`c = p^(1/k)`:
s = -k / ln(1 - c)
利用泰勒展开式:`ln(1 + x) ~= x - 0.5x^2 while x < 1` 化简得到:
s ~= -k / (-c-0.5c^2) = 2k / (2c + c * c)
最后得出公式:
c = p^(1/k)
s = m / n = 2k / (2c + c * c)
假定有n=10000000的数据量,则有理论值:
p < 0.1 and k = 1: s = m/n = 9.523810
p < 0.1 and k = 2: s = m/n = 5.461082
p < 0.1 and k = 3: s = m/n = 5.245850, space ~= 6.3MB
p < 0.1 and k = 4: s = m/n = 5.552045, space ~= 6.6MB
p < 0.01 and k = 1: s = m/n = 99.502488
p < 0.01 and k = 2: s = m/n = 19.047619
p < 0.01 and k = 3: s = m/n = 12.570636, space ~= 15MB
p < 0.01 and k = 4: s = m/n = 10.922165, space ~= 13MB
p < 0.001 and k = 1: s = m/n = 999.500250
p < 0.001 and k = 2: s = m/n = 62.261118
p < 0.001 and k = 3: s = m/n = 28.571429, space ~= 34MB
p < 0.001 and k = 4: s = m/n = 20.656961, space ~= 24.6MB
p < 0.0001 and k = 1: s = m/n = 9999.500025
p < 0.0001 and k = 2: s = m/n = 199.004975
p < 0.0001 and k = 3: s = m/n = 63.167063, space ~= 75.3MB
p < 0.0001 and k = 4: s = m/n = 38.095238, space ~= 45.4MB
p < 0.0001 and k = 5: s = m/n = 29.231432, space ~= 24.8MB
可以看到,在k=3的情况下,其实已经可以达到我们平常使用所能的接受范围内了,没必要非得
使用最优解,除非在空间使用极为苛刻的情况下,而且这个公式,针对程序空间使用的调整,更加的灵活智能。
特别提下,经过实测,如果每个hash的实现非常优质,分布很均匀的情况下,其实际的误判率比理论值低很多:
就拿TBOX的bloom filter的实现做测试,n=10000000:
p < 0.01 and k = 3的情况下,其实际误判率为:0.004965
p < 0.001 and k = 3的情况下,其实际误判率为:0.000967
所以好的hash函数算法也是尤为的重要。
下面来看下TBOX提供的bloom filter的使用,用起来也是相当的方便:
// 总的元素个数
tb_size_t count = 10000000;
/* 初始化bloom filter
*
* TB_BLOOM_FILTER_PROBABILITY_0_01: 预定义的误判率,接近0.01
* 注:由于内部使用位移数来表示:1 / 2^6 = 0.015625 ~= 0.01
* 所以实际传入的误判率,有可能稍微大一点,但是还是相当接近的
*
* 3:为k值,hash函数的个数,最大不超过15个
*
* count:指定的元素规模数
*
* tb_item_func_long():容器的元素类型,主要是用其内定的hash函数,如果要自定义hash函数,可以替换:
*
* tb_size_t tb_xxxxxx_hash(tb_item_func_t* func, tb_cpointer_t data, tb_size_t mask, tb_size_t index)
* {
* // mask为hash掩码,index为第index个hash算法的索引
* return compute_hash(data, index) & mask;
* }
*
* tb_item_func_t func = tb_item_func_long();
* func.hash = tb_xxxxxx_hash;
*
* 来进行
*/
tb_bloom_filter_ref_t filter = tb_bloom_filter_init(TB_BLOOM_FILTER_PROBABILITY_0_01, 3, count, tb_item_func_long());
if (filter)
{
tb_size_t i = 0;
for (i = 0; i < count; i++)
{
// 产生随机数
tb_long_t value = tb_random();
// 设置值到filter内,如果不存在,则返回tb_true表示设置成功
if (tb_bloom_filter_set(filter, (tb_cpointer_t)value))
{
// 添加元素成功,之前元素不存在
// 不会存在误判
}
else
{
// 添加失败,添加的元素已经存在
// 这里可能会存在误判
}
// 仅仅判断元素是否存在
if (tb_bloom_filter_get(filter, (tb_cpointer_t)data)
{
// 元素已经存在
// 这里可能会存在误判
}
else
{
// 元素不存在
// 不会存在误判
}
}
// 退出filter
tb_bloom_filter_exit(filter);
}
// 常用预定义的误判率,也可以指定其他值,注:必须是位移数,而不是实际值
typedef enum __tb_bloom_filter_probability_e
{
TB_BLOOM_FILTER_PROBABILITY_0_1 = 3 ///!< 1 / 2^3 = 0.125 ~= 0.1
, TB_BLOOM_FILTER_PROBABILITY_0_01 = 6 ///!< 1 / 2^6 = 0.015625 ~= 0.01
, TB_BLOOM_FILTER_PROBABILITY_0_001 = 10 ///!< 1 / 2^10 = 0.0009765625 ~= 0.001
, TB_BLOOM_FILTER_PROBABILITY_0_0001 = 13 ///!< 1 / 2^13 = 0.0001220703125 ~= 0.0001
, TB_BLOOM_FILTER_PROBABILITY_0_00001 = 16 ///!< 1 / 2^16 = 0.0000152587890625 ~= 0.00001
, TB_BLOOM_FILTER_PROBABILITY_0_000001 = 20 ///!< 1 / 2^20 = 0.00000095367431640625 ~= 0.000001
}tb_bloom_filter_probability_e;
----------
* [TBOX项目详情](http://www.oschina.net/p/tbox)
* [TBOX项目源码](https://github.com/waruqi/tbox)
* [TBOX项目文档](https://github.com/waruqi/tbox/wiki/%E7%9B%AE%E5%BD%95)
转载于:https://blog.51cto.com/aa9383632/1571992
利用bloom filter算法处理大规模数据过滤相关推荐
- Bloom Filter算法
一.概念 Bloom Filter的中文翻译叫做布隆过滤器,是1970年由布隆提出的.它实际上是一个很长的二进制向量和一系列随机映射函数.布隆过滤器可以用于检索一个元素是否在一个集合中.它的优点是空间 ...
- Bloom Filter算法优化
Bloom Filter算法优化 基于Redis的Bloom Filter去重,利用Redis的String数据结构,但Redis的String最大只能512M,所以数据过大的时候,需要申请多个去重块 ...
- 布隆过滤器(Bloom Filter)算法
布隆过滤器原理 开发一个电商项目,因为数据量一直在增加(已达亿级),所以需要重构之前开发好的秒杀功能,为了更好的支持高并发,在验证用户是否重复购买的环节,就考虑用布隆过滤器. 也顺便更加深入的去了解下 ...
- Scrapy 爬虫去重效率优化之 Bloom Filter的算法的对接
From:https://cloud.tencent.com/developer/article/1084962 Python分布式爬虫打造搜索引擎Scrapy精讲-将bloomfilter(布隆过滤 ...
- Bloom Filter 大规模数据处理利器
2019独角兽企业重金招聘Python工程师标准>>> 最近工作中涉及到bloom Filter,真是一把科研利器呀,大数据.网络.云等等都可以用到! Bloom Filter是由B ...
- 网页查重算法Shingling和Simhash和bloom filter研究
网页查重算法Shingling和Simhash和bloom filter研究 在网页查重算法中 shingling 和 simhash 被认为是当前最好的两个算法. shingling算法 shing ...
- Bloom Filter原理及python实现
文章目录 一.Bloom Filter存在的意义 二.Bloom Filter算法原理 三.Bloom Filter的优化 哈希函数选择 参数设计 误判率P(true) Hash Function的数 ...
- Bloom Filter与Cuckoo Filter
Bloom Filter 背景 日常生活中,包括在设计计算机软件时,我们经常要判断一个元素是否在一个集合中.最直接的方法就是将集合中全部的元素存在计算机中,遇到一个新元素时,将它和集合中的元素直接比较 ...
- 大数据量下的集合过滤—Bloom Filter
算法背景 如果想判断一个元素是不是在一个集合里,一般想到的是将集合中所有元素保存起来,然后通过比较确定.链表.树.散列表(又叫哈希表,Hash table)等等数据结构都是这种思路,存储位置要么是磁盘 ...
最新文章
- 程序员缺乏经验的 7 种表现
- 【原创】如何优化一个网站使之提高访问速度--更新20120216
- win2012中让IIS同时支持多版本ASP.NET 3.5/4.0/4.5的方法
- C 库函数 int fprintf(FILE *stream, const char *format, ...) 发送格式化输出到流 stream 中
- 学习VUE时,利用webpack打包的错误处理方法
- 2018【比特杯】编程大赛
- GIS创新实践【实验1】郑州市地图制作与发布
- 关于jquery与dom元素转换
- Java平台扩展机制#3:SLF4J怪招
- python如何循环使用input_python基础知识input到while循环
- linux 环境搭建Jenkins
- root用户安装的软件在普通用户不生效
- gogoclient java_链路跟踪-GRPC请求 - GoFrame官网 - 类似PHP-Laravel, Java-SpringBoot的Go企业级开发框架...
- 绝地求生key钓鱼全套教程加源码
- 遗传算法在组卷中的应用
- SLIC超像素算法学习笔记
- 使用html5静态页面的总结,幸福西饼:静态页面制作项目总结
- ARM与Calxeda/华芯通
- 3D建模 UG8.0 32位安装过程
- Git用户手册--GitHub
热门文章
- 当layer动态加载无法勾选 多个复选框时
- Layui form 表单验证 基本属性
- 山东春季高考计算机知识点,山东春季高考专业知识点总结
- java集群解析文件_java相关:springboot整合redis集群过程解析
- python email 附件_使用python调用email模块实现附件发送
- 如何吧本地仓库提交到github_如何将本地代码提交到github远程仓库(第一次,建立连接并且提交)...
- jsp中提供的四种属性范围
- 如何能把 fastdfs-client-java的jar包安装到本地的仓库中
- 收集:Hibernate中常见问题 No row with the given identifier exists问题的原因及解决
- 当你使用R安装包出现rdb is corrupt问题的时候