HyperLogLog算法
项目在统计UV/PV时用到了Druid的Hyper hyperunique算法,书上介绍这种算法求出的UV/PV存在一定误差,因此需要了解下误差来自哪里。
实现去重功能,最简单的就是使用set记录集合本身,缺点与前面Bloom Filter差不多,显而易见,需要大量内存空间。HyperLogLog为解决这个问题而生。
另外redis也实现了HyperLogLog的结构,所以可以从redis源码上分析下其实现。
1。基数计数
基数是指一个集合中不同元素的个数。假设有一组数据{1, 2, 3, 3, 4, 5},除去重复的数字之后,该组数据中不同的数有5个,则该组数据的基数为5。
那什么是基数统计呢?基数统计是指在误差允许的情况下估算出一组数据的基数。
2。伯努利过程
投掷一次硬币出现正、反两面的概率均为1/2。如果我们不断的投掷硬币,直到出现一次正面,在这样的一个过程中,投掷一次得到正面的概率为1/2,投掷两次才得到正面的概率为1/2^2….依次类推,投掷k次才得到一次正面的概率为1/2^k。这个过程在统计学上称为伯努利问题。
思考下面两个问题:
进行n次伯努利过程,所有投掷次数都小于k的概率
进行n次伯努利过程,所有投掷次数都大于k的概率
针对第一个问题,在一次伯努利过程中,投掷次数大于k的概率为1/2^k,也就是投了k次反面的概率。因此,在一次过程中投掷次数不大于k的概率为1-1/2^k。因此n次伯努利过程所有投掷次数都不大于k的概率为
很显然,第二个问题,n次伯努利过程,所有投掷次数都不小于k的概率为
从上述公式中可得出结论:当n远小于2^k时,P(x>=k)几乎为0,即所有投掷次数都小于k;当n远大于2^k时,P(x <= k)几乎为0,即所有投掷次数都大于k。因此,当x=k的情况下,我们可以把2^k当成n的一个粗糙估计。
3。基数统计和HyperLogLog算法
将上述伯努利过程转换到比特位串上,假设我们有8位比特位串,每一位上出现0或者1的概率均为1/2,投掷k次才得到一次正面的过程可以理解为第k位上出现第一个1的过程。
那么针对一个数据集来说,我们用某种变换将其转换成一个比特子串,就可以根据上述理论来估算出该数据集的技术。例如数据集转换成00001111,第一次出现1的位置为4,那么该数据集的基数为16。
于是现在的问题就是如何将数据集转换成一个比特位串?很明显,哈希变换可以帮助我们解决这个问题。
选取一个哈希函数,该函数满足一下条件:
具有很好的均匀性,无论原始数据集分布如何,其哈希值几乎服从均匀分布。这就保证了伯努利过程中的概率均为1/2
碰撞几乎忽略不计,也就是说,对于不同的原始值,其哈希结果相同的概率几乎为0
哈希得出的结果比特位数是固定的。
有了以上这些条件,就可以保证“伯努利过程”的随机性和均匀分布了。
接下来,对于某个数据集,其基数为n,将其中的每一个元素都进行上述的哈希变换,这样就得到了一组固定长度的比特位串,设f(i)为第i个元素比特位上第一次出现”1“的位置,简单的取其最大值f_max为f(i)的最大值,这样,我们就可以得出以下结论:
当n远小于2^f_max时,f_max为当前值的概率为0
当n远大于2^f_max时,f_max为当前值的概率为0
这样一来,我们就可以将f_max作为n的一个粗糙估计。当然,在实际应用中,由于数据存在偶然性,会导致估计量误差较大,这时候需要采用分组估计来消除误差,并且进行偏差修正。
HyperLogLog算法原理通俗版:Here I’ll cover only the basic idea using a very clever example found at [3]. Imagine you tell me you spent your day flipping a coin, counting how many times you encountered a non interrupted run of heads. If you tell me that the maximum run was of 3 heads, I can imagine that you did not really flipped the coin a lot of times. If instead your longest run was 13, you probably spent a lot of time flipping the coin.
所谓分组估计就是,每一个数据进行hash之后存放在不同的桶中,然后计算每一个桶的f_max,最后对这些值求一个平均f_avg,即可得到基数的粗糙估计2^f_avg。
误差消减原理通俗版:However if you get lucky and the first time you get 10 heads, an event that is unlikely but possible, and then stop flipping your coin, I’ll provide you a very wrong approximation of the time you spent flipping the coin. So I may ask you to repeat the experiment, but this time using 10 coins, and 10 different piece of papers, one per coin, where you record the longest run of heads. This time since I can observe more data, my estimation will be better.
实际实现中,分组估计也不能很好的解决误差问题,还需要额外的偏差修正工作。
http://algo.inria.fr/flajolet/Publications/DuFl03-LNCS.pdf
4。Redis实现
对于输入,计算64位hash
hash最右14bit来进行分桶,桶的个数是2 ^14=16384个
统计hash左侧50bit,第一次1出现的位置(用bit存储需要6bit:2^6 = 64 > 50)
与对应桶的6bit值比较,若大于原来值,则更新之
redis HyperLogLog内存占用:6(存储第一次出现位置) * 16384(桶个数) / 8 (1byte = 8bit)/ 1024(bytes) = 12K
因此redis使用12K固定大小内存即可完成对一个Key的Unique统计,空间效率极高。
HyperLogLog算法的更多相关文章
- HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的
作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...
- 高可用Redis(六):瑞士军刀之bitmap,HyperLoglog和GEO
1.bitmap位图 1.1 bitmap位图的概念 首先来看一个例子,字符串big, 字母b的ASCII码为98,转换成二进制为 01100010 字母i的ASCII码为105,转换成二进制为 01 ...
- 浅谈redis的HyperLogLog与布隆过滤器
首先,HyperLogLog与布隆过滤器都是针对大数据统计存储应用场景下的知名算法. HyperLogLog是在大数据的情况下关于数据基数的空间复杂度优化实现,布隆过滤器是在大数据情况下关于检索一个元 ...
- 基数计数——HyperLogLog
所谓的基数计数就是统计一组元素中不重复的元素的个数.如统计某个网站的UV,或者用户搜索网站的关键词数量:再如对一个网站分别统计了三天的UV,现在需要知道这三天的UV总量是多少,怎么融合多个统计值. 1 ...
- HyperLogLog
数据量一大,连统计基数也成了一个麻烦事.在使用kylin的时候,遇到对度量值进行基数统计,使用的是Hyperloglog算法,占用内存小,误差小,实乃不错的方法,但查阅网上的资料与内容,感觉未能理解的 ...
- redis HyperLogLog 基数估算
HyperLogLog 可以接受多个元素的输入,返回输入元素的基数估算值基数,集合中不同元素的数量.如集合{1,2,3,1,2,3,4}的基数是4.估算,HyperLogLog算法返回的基数不是完全精 ...
- Redis HyperLogLog用法简介
(1)HyperLogLog简介 在Redis 在 2.8.9 版本才添加了 HyperLogLog,HyperLogLog算法是用于基数统计的算法,每个 HyperLogLog 键只需要花费 12 ...
- HyperLogLog算法分析及其应用
HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的 探索HyperLogLog算法(含Java实现) 神奇的HyperLogLog算法 Sketch of the Day: Hyp ...
- 【转】高可用Redis(六):瑞士军刀之bitmap,HyperLoglog和GEO
1.bitmap位图 1.1 bitmap位图的概念 首先来看一个例子,字符串big, 字母b的ASCII码为98,转换成二进制为 01100010 字母i的ASCII码为105,转换成二进制为 01 ...
随机推荐
- css常用的属性
CSS------属性值篇 display: none | block | inline(默认值) | inline-block(css2新增) | inherit none :此元素不会再显示 {注 ...
- C# List<string> to string
List<string> names = new List<string>() { "John", "Anna", "Moni ...
- 中文乱码 URLEncode之后,后台获取仍是乱码问题详解
java中获取到参数的时候,这时候默认使用的是iso8859-1进行解码的,那么就再使用URLEncode的encoe方法对其进行编码一次,编码格式使用iso8859-1,这样我们就获得最初使用utf ...
- GridView有用的小方法--2017年2月13日[转]
快速预览: GridView无代码分页排序GridView选中,编辑,取消,删除GridView正反双向排序GridView和下拉菜单DropDownList结合GridView和CheckBox结合 ...
- Angular2+ 实现组件交互的众多方式
实现组件交互有很多方式,下面列举. 1.父组件向子组件传递数据:(属性绑定) 父组件 [子属性名] = "父属性名" <child-content [data]=" ...
- C# Winform 通过Socket实现客户端和服务端TCP通信
操作界面如下: 1.声明Socket 第一个参数:寻址方式,第二个参数:传输数据的方式,第三个参数:通信协议 Socket socket = new Socket(AddressFamily.Inte ...
- 第一个HTML文档
属性 和 值 1.作用 用来修饰元素 ex:让 p 标记的文本水平居中对齐 <p>Hello World</p> 2.语法 1.属性的声明必须位于开始标记里 ...
- vsftp在iptables中的配置
在 /etc/vsftpd/vsftpd.conf 文件末添加以下几行 pasv_enable=YESpasv_min_port=20000pasv_max_port=20010 在 iptables ...
- web开发中 代码解决部分IE兼容问题
首先是自己遇到问题: 一套系统,以前的开发asp旧+c#新后台管理扩展.完善后,在2013年前基本无问题,很是畅顺. 其中.到升级了浏览器后.例如ie9以后,则问题出现了. 如图: 这是一个js的 ...
- c++中,如果访问数组越界,程序可能会意外终止(像死循环)
#include<iostream> using namespace std; ];// int main(){ vis[]=;//访问越界 ; } 程序错误表现: