【原创】大叔算法分享(4)Cardinality Estimate 基数计数概率算法
读过《编程珠玑》(<Programming Pearls>)的人应该还对开篇的Case记忆犹新,大概的场景是:
作者的一位在电话公司工作的朋友想要统计一段时间内不同的电话号码的个数,电话号码的数量很大,当时的内存很小,所以不能把所有的电话号码全部放到内存来去重统计,他的朋友很苦恼。
作者聪明的想到了用bit数组来解决问题,每个电话号码可以映射为bit数组的index,bit数组初始状态所有位为0,所有电话号码逐一处理:将bit数组对应位置为1,处理完之后统计bit数组中有多少个1即可。
示例:[0,1,0,0,0,1,0,...] 这个bit数组表示2和5存在
不得不说这种想法非常精妙,即减少了内存占用(8位电话号码如果全部放到内存需要381M(每个电话号码存成Integer占4Byte计算),而使用bit数组只需要11M),而且只需要两次循环就可以得到结果;
这是一个基数计数的问题,Cardinality:estimating the number of distinct elements.
1 Bitmap
上边提到的方式是使用bitmap,思路是将dataset中的每一个element映射到一个bit位,不允许冲突,所需要的内存空间大概为基数*1bit(上例中是100,000,000bit),并且计数精准;
但是当基数非常大时,即使bitmap内存也放不下!
好消息是如果不要求计数精准(允许一定范围内的误差),可以采用概率估算算法:
2 LC:Linear Counting
公式

其中:n是估算值,m是bitmap大小,Vn是bitmap中0出现的比率,比如0.1;
原理
使用固定大小的hashtable来存放dataset,可以想见:
- dataset的基数越小,hashtable越空,当基数为0时hashtable所有的key都是空的
- dataset的基数越大,hashtable越满,冲突也越多,当基数无穷大时hashtable所有的key都被占用并且每个key都有大量冲突
所以可以根据hashtable中key被占用的情况来估算dataset的基数,这里主要用到了对数曲线的特性(0<x<1这一段):

过程
初始化一个bitmap,所有位为0,dataset的每一个element都hash到bitmap的一个bit位,hash之后将对应的bit位置为1,hash允许冲突,最后根据bitmap中0的数量和bitmap大小由公式来估算基数;
注意:虽然LC用的也是bitmap,但是相比原始的bitmap算法,LC的bitmap大小可以比基数小很多,因为LC的映射允许冲突,另外可以设置bitmap大小来决定误差的大小;
推导过程详见参考
参考:A linear-time probabilistic counting algorithm for database applications
3 LLC:LogLog Counting
公式

其中:m是桶的数量,M为桶的集合,k是用于分桶的位数,ρ为bit数组中第一个为1的下标即index,E是估算值;
原理
来看抛硬币的过程,抛硬币的过程是伯努利Bernoulli过程,每次的结果要么是0,要么是1,并且概率均为1/2,假设一次抛硬币game定义为抛到1为止,可能第一次就抛到1(P=0.5),也可能前边抛了i次0最后才抛到1(P=1-0.5^i);抛硬币game玩的次数越多,越容易出现前边很多次都是0的情况,这是因为开头连续出现的0的个数越多,出现概率越小,需要尝试伯努利过程的次数就越多,所以可以利用概率根据结果(开头出现0的个数,即i)来反推出条件(game玩了多少次,即n=2^i,这个也很容易理解,比如彩票的中奖概率是1/10000,即平均买10000张彩票才能中一次奖,反过来说,如果有人中了一次奖,他很大概率上应该买了10000张彩票);
但是由于随机性的存在导致误差较大,所以通过将dataset分为m份,每份单独统计,最后取算数平均值的方式来降低随机性从而减小误差;
过程
dataset的每一个element先映射到一个bit数组,比如32位bit数组,将这个bit数组的1到k位的值作为桶的bucket_index(即第几个桶),将k+1到32位中第一个为1的index作为value放到桶中,如果桶里已经有value,桶会保存一个最大的value,数据集元素全部映射完之后,将所有桶的value取算数平均值,根据n=2^i,这样可以得到每个桶内的基数,再乘以m可以得到整个dataset的基数,公式最前边的α是修正参数;
比如k=2,则m=2^k=4,即4个桶,dataset中一个element映射的bit数组为[1,0,0,0,0,1,0,...],取前两位[1,0]对应的值是2,即第2个桶,取第3位之后的数组[0,0,0,1,0,...]可见第一个位1的index是3,将3放到桶2中,以此类推;
推导过程详见参考
参考:Loglog Counting of Large Cardinalities
4 HLLC:Hyper LogLog Counting
公式

原理
同LLC,只有一点不同:取均值的时候不使用算数平均数而改用调和平均数
推导过程详见参考
参考:HyperLogLog: the analysis of a near-optimal cardinality estimation algorithm
5 最优实践
公式

过程
在大量实践中根据各个参数和结果的情况进行调优,综合使用HLLC和LC等算法
总结
目前HLLC在很多开源组件中都有应用,比如redis、druid等

其他:
https://research.neustar.biz/2012/10/25/sketch-of-the-day-hyperloglog-cornerstone-of-a-big-data-infrastructure/
【原创】大叔算法分享(4)Cardinality Estimate 基数计数概率算法的更多相关文章
- 【原创】大叔算法分享(5)聚类算法DBSCAN
一 简介 DBSCAN:Density-based spatial clustering of applications with noise is a data clustering algorit ...
- 【原创】大叔经验分享(83)impala执行多个select distinct
impala在一个select中执行多个count distinct时会报错,比如执行 select key, count(distinct column_a), count(distinct col ...
- 解读Cardinality Estimation<基数估计>算法(第一部分:基本概念)
基数计数(cardinality counting)是实际应用中一种常见的计算场景,在数据分析.网络监控及数据库优化等领域都有相关需求.精确的基数计数算法由于种种原因,在面对大数据场景时往往力不从心, ...
- 【原创】经验分享:一个小小emoji尽然牵扯出来这么多东西?
前言 之前也分享过很多工作中踩坑的经验: 一个线上问题的思考:Eureka注册中心集群如何实现客户端请求负载及故障转移? [原创]经验分享:一个Content-Length引发的血案(almost.. ...
- 基数计数——HyperLogLog
所谓的基数计数就是统计一组元素中不重复的元素的个数.如统计某个网站的UV,或者用户搜索网站的关键词数量:再如对一个网站分别统计了三天的UV,现在需要知道这三天的UV总量是多少,怎么融合多个统计值. 1 ...
- 【原创】大叔案例分享(4)定位分析--见证scala的强大
一 场景分析 定位分析广泛应用,比如室外基站定位,室内蓝牙beacon定位,室内wifi探针定位等,实现方式是三点定位 Trilateration 理想情况 这种理想情况要求3个基站‘同时’采集‘准确 ...
- 【原创】大叔经验分享(51)docker报错Exited (137)
docker container启动失败,报错:Exited (137) *** ago,比如 Exited (137) 16 seconds ago 这时通过docker logs查不到任何日志,从 ...
- 【原创】大叔经验分享(50)hue访问mysql(librdbms)
cloudera manager安装hue后想开启访问mysql(librdbms)需要在这里配置(hue_safety_valve.ini) 添加配置如下 [librdbms] # The RDBM ...
- 【原创】大叔经验分享(49)hue访问hdfs报错/hue访问oozie editor页面卡住
hue中使用hue用户(hue admin)访问hdfs报错: Cannot access: /. Note: you are a Hue admin but not a HDFS superuser ...
随机推荐
- 袁创:使用反射动态调用ActiveX控件
■■■■前言 目前的基于.NET平台的软件研发中仍然存在大量的对COM及ActiveX控件的调用.使用C#调用ActiveX控件时一般是使用vs.net工具自动生成的互操作性程序集.这种方法操作简单, ...
- Python的dnspython库使用指南
因为平时在测试DNS的时候有些操作手动完成不方便,所以需要用到脚本,而在Python里dnspython这个用于DNS操作的库十分强大,但是无奈网上大部分资料只列举了少部分的用法,所以记录一下我平时使 ...
- 转 vue实现双向数据绑定之原理及实现篇
转自:https://www.cnblogs.com/canfoo/p/6891868.html vue的双向绑定原理及实现 前言 先上个成果图来吸引各位: 代码: ...
- java进阶学习的一些思路
搞 Java 的年薪 40W 是什么水平? - 乔戈里的回答 - 知乎 https://www.zhihu.com/question/31437847/answer/566852748 在知乎上看了他 ...
- vue2.0里的路由钩子
路由钩子 在某些情况下,当路由跳转前或跳转后.进入.离开某一个路由前.后,需要做某些操作,就可以使用路由钩子来监听路由的变化 全局路由钩子: router.beforeEach((to, from, ...
- python dlib opencv 人脸68点特征检测
不得不感慨,现在现成的东西太多了,直接拿来用就行了 dlib安装(指定版本安装,避免踩坑) pip dlib中训练好的文件http://dlib.net/files/shape_predictor_6 ...
- Linux(Ubuntu)使用日记(三)------git安装使用
1. 安装 首先,确认你的系统是否已安装git,可以通过git指令进行查看,如果没有,在命令行模式下输入sudo apt-get install git命令进行安装. 2. 配置 git confi ...
- mysqldump 导出
导出单张表数据:mysqldump -h127.0.0.1 -uroot -p database_name table_name > user_action.sql
- vivado place30-378
AR# 60131 Vivado Placer - [Place 30-378] Input pin of input buffer has an illegal connection to a lo ...
- BZOJ5341[Ctsc2018]暴力写挂——边分治+虚树+树形DP
题目链接: CSTC2018暴力写挂 题目大意:给出n个点结构不同的两棵树,边有边权(有负权边及0边),要求找到一个点对(a,b)满足dep(a)+dep(b)-dep(lca)-dep'(lca)最 ...