项目在统计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实现

  1. 对于输入,计算64位hash

  2. hash最右14bit来进行分桶,桶的个数是2 ^14=16384个

  3. 统计hash左侧50bit,第一次1出现的位置(用bit存储需要6bit:2^6 = 64 > 50)

  4. 与对应桶的6bit值比较,若大于原来值,则更新之

redis HyperLogLog内存占用:6(存储第一次出现位置) * 16384(桶个数) / 8 (1byte = 8bit)/ 1024(bytes) = 12K

因此redis使用12K固定大小内存即可完成对一个Key的Unique统计,空间效率极高。

HyperLogLog算法的更多相关文章

  1. HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

  2. 高可用Redis(六):瑞士军刀之bitmap,HyperLoglog和GEO

    1.bitmap位图 1.1 bitmap位图的概念 首先来看一个例子,字符串big, 字母b的ASCII码为98,转换成二进制为 01100010 字母i的ASCII码为105,转换成二进制为 01 ...

  3. 浅谈redis的HyperLogLog与布隆过滤器

    首先,HyperLogLog与布隆过滤器都是针对大数据统计存储应用场景下的知名算法. HyperLogLog是在大数据的情况下关于数据基数的空间复杂度优化实现,布隆过滤器是在大数据情况下关于检索一个元 ...

  4. 基数计数——HyperLogLog

    所谓的基数计数就是统计一组元素中不重复的元素的个数.如统计某个网站的UV,或者用户搜索网站的关键词数量:再如对一个网站分别统计了三天的UV,现在需要知道这三天的UV总量是多少,怎么融合多个统计值. 1 ...

  5. HyperLogLog

    数据量一大,连统计基数也成了一个麻烦事.在使用kylin的时候,遇到对度量值进行基数统计,使用的是Hyperloglog算法,占用内存小,误差小,实乃不错的方法,但查阅网上的资料与内容,感觉未能理解的 ...

  6. redis HyperLogLog 基数估算

    HyperLogLog 可以接受多个元素的输入,返回输入元素的基数估算值基数,集合中不同元素的数量.如集合{1,2,3,1,2,3,4}的基数是4.估算,HyperLogLog算法返回的基数不是完全精 ...

  7. Redis HyperLogLog用法简介

    (1)HyperLogLog简介 在Redis 在 2.8.9 版本才添加了 HyperLogLog,HyperLogLog算法是用于基数统计的算法,每个 HyperLogLog 键只需要花费 12 ...

  8. HyperLogLog算法分析及其应用

    HyperLogLog 算法的原理讲解以及 Redis 是如何应用它的 探索HyperLogLog算法(含Java实现) 神奇的HyperLogLog算法 Sketch of the Day: Hyp ...

  9. 【转】高可用Redis(六):瑞士军刀之bitmap,HyperLoglog和GEO

    1.bitmap位图 1.1 bitmap位图的概念 首先来看一个例子,字符串big, 字母b的ASCII码为98,转换成二进制为 01100010 字母i的ASCII码为105,转换成二进制为 01 ...

随机推荐

  1. 博客编辑器Open Live Writer的安装以及配置

    下载安装包 访问官网  http://openlivewriter.org/ 或者微软商店  https://www.microsoft.com/en-us/p/open-live-writer/9n ...

  2. js 获取二级域名

    js获取页面完整地址: window.location.href; var s =" https://ejym.baidu.com";            var h = s.s ...

  3. 李航《统计学习方法》CH03

    CH03 k近邻法 前言 章节目录 k近邻算法 k近邻模型 模型 距离度量 k值选择 分类决策规则 k近邻法的实现: KDTree 构造KDTree 搜索KDTree 导读 kNN是一种基本分类与回归 ...

  4. jsp的page、request、session、application四个作用域的作用

    1.page作用域也是最小的作用域,它只能在当前页面中使用. 2.request作用域主要是发送请求,但只能在两个页面之间发送一次请求. 3.session作用域是一个会话,也就是一个浏览器,意思是说 ...

  5. Gradient Boosting, Decision Trees and XGBoost with CUDA ——GPU加速5-6倍

    xgboost的可以参考:https://xgboost.readthedocs.io/en/latest/gpu/index.html 整体看加速5-6倍的样子. Gradient Boosting ...

  6. day 10 函数命名空间、函数嵌套和作用域

    1. day 09 内容复习 # 函数 # 可读性强 复用性强 # def 函数名(): # 函数体 #return 返回值 # 所有的函数 只定义不调用就一定不执行 #先定义后调用 #函数名() # ...

  7. NPOI 关于Excel的学习

    1.传送门:http://blog.csdn.net/guo_lover/article/details/52399570

  8. 错误:Could not find a getter for CreatTime in class

    org.hibernate.PropertyNotFoundException: Could not find a getter for CreatTime in class org.com.xk.h ...

  9. Django知识总结(三)

    拾伍 ● Ajax技术 一 ● Ajax定义 Ajax: 异步的 JavaScript 和 XML (Asynchronous+Javascript+XML) 通过Ajax, 我们可以在不重新加载整个 ...

  10. 使用DD 创建SWAP

    1创建所需swap空间的文件 + records in + records out bytes ( MB) copied, 2.01386 s, 52.1 MB/s[root@web01 ~]# ll ...