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 ...
随机推荐
- QT:图形的描画(折线,柱状图,多边形)
1. 创建一个继承于QWidget的类,重载一个叫paintEvent的函数, 2. 在paintEvent函数中调用Qpainter类,此类是一个重量级类,在paintEvent函数尽量只创建一次 ...
- 用servlet验证密码2
function createXMLHttpRequest() { var XMLHttpRequest1; if (window.XMLHttpRequest) { XMLHttpRequest_t ...
- mycat环境搭建
最近工作中突然让搞mycat,特意私下在家先搞一套练个手: 1.先下载一个CentOS7 mini版本就可以(本人机器性能有限): 2.使用VMware创建虚拟机,过程百度下一大堆,这里不做详细介绍. ...
- FtpWebRequest.UsePassive属性:设置FTP工作模式
默认值:true,被动模式 PASV(被动)方式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路. 当需要传送数据时, 服务器在命令链路上用PASV ...
- 第7天:Q Quant库(未完待续)
一.本文大纲: 1.Python内置函数计算期权的价格 2.numpy加速数值计算 3.SciPy进行仿真模拟 4.SciPy求解器计算隐含波动率 5.matplotlib绘图 二.案例 (看不懂,略 ...
- java web中验证码生成的demo
首先创建一个CaptailCode类 package com.xiaoqiang.code; import java.awt.*; import java.awt.font.FontRenderCon ...
- Exception、Thorow、Throws、TryCatch
一.异常 概述: 异常指的是不正常,指的是程序中出现了某种问题 java中,所有问题都可以使用一个类来表示,这个类叫做Throwable Throwable: Throwawble是java中所有异常 ...
- oralce定时任务
oracle定时任务(dbms_job) author:skate time:2007-09-12 http://publish.it168.com/2006/0311/20060311017002. ...
- 一个简单的Quartz定时任务
package com.shuadan.quartz; import org.springframework.scheduling.annotation.Scheduled; import org.s ...
- SQL-50 将employees表中的所有员工的last_name和first_name通过(')连接起来
题目描述 将employees表中的所有员工的last_name和first_name通过(')连接起来.CREATE TABLE `employees` (`emp_no` int(11) NOT ...