解读Cardinality Estimation算法(第一部分:基本概念)
传统的精确基数计数算法在数据量大时会存在一定瓶颈,瓶颈主要来自于数据结构合并内存使用两个方面。因此出现了很多基数估计的概率算法,这些算法虽然计算出的结果不是精确的,但误差可控,重要的是这些算法所使用的数据结构易于合并,同时比传统方法大大节省内存。

解读Cardinality Estimation算法(第二部分:Linear Counting)

简介

Linear Counting(以下简称LC)在1990年的一篇论文“A linear-time probabilistic counting algorithm for database applications”中被提出。作为一个早期的基数估计算法,LC在空间复杂度方面并不算优秀,实际上LC的空间复杂度与上文中简单bitmap方法是一样的(但是有个常数项级别的降低),都是O(Nmax),因此目前很少单独使用LC。不过作为Adaptive Counting等算法的基础,研究一下LC还是比较有价值的。

思路

LC的基本思路是:设有一哈希函数H,其哈希结果空间有m个值(最小值0,最大值m-1),并且哈希结果服从均匀分布。使用一个长度为m的bitmap,每个bit为一个桶,均初始化为0,设一个集合的基数为n,此集合所有元素通过H哈希到bitmap中,如果某一个元素被哈希到第k个比特并且第k个比特为0,则将其置为1。当集合所有元素哈希完成后,设bitmap中还有u个bit为0。则:
n^=mlogum \hat{n}=-m \log \frac{u}{m}
nn的一个最大似然估计,证明见论文。
Cardinality Estimation算法

算法应用

在应用LC算法时,主要需要考虑的是bitmap长度m的选择。这个选择主要受两个因素的影响:基数n的量级以及容许的误差。这里假设估计基数n的量级大约为N,允许的误差为ϵ,则m的选择需要遵循如下约束。

误差控制

这里以标准差作为误差。由上面标准差公式可以推出,当基数的量级为N,容许误差为ϵ时,有如下限制:
m>ett1(et)2 m>\frac{e^{t}-t-1}{(e t)^{2}}
将量级和容许误差带入上式,就可以得出m的最小值,其中t=n/mt=n/m

满桶控制

由LC的描述可以看到,如果m比n小太多,则很有可能所有桶都被哈希到了,此时u的值为0,LC的估计公式就不起作用了(变成无穷大)。因此m的选择除了要满足上面误差控制的需求外,还要保证满桶的概率非常小。
数学证明省略
当基数量级为N,可接受误差为ϵ,则m的选取应该遵从
m>β(ett1) m>\beta\left(e^{t}-t-1\right)
其中β=max(5,1/(ϵt)2)\beta=\max \left(5,1 /(\epsilon t)^{2}\right)
下图是论文作者预先计算出的关于不同基数量级和误差情况下,m的选择表:
Cardinality Estimation算法
可以看出精度要求越高,则bitmap的长度越大。随着m和n的增大,m大约为n的十分之一。因此LC所需要的空间只有传统的bitmap直接映射方法的1/10,但是从渐进复杂性的角度看,空间复杂度仍为O(Nmax)。

合并

LC非常方便于合并,合并方案与传统bitmap映射方法无异,都是通过按位或的方式。

小结

这篇文章主要介绍了Linear Counting。LC算法虽然由于空间复杂度不够理想已经很少被单独使用,但是由于其在元素数量较少时表现非常优秀,因此常被用于弥补LogLog Counting在元素较少时误差较大的缺陷,实际上LC及其思想是组成HyperLogLog Counting和Adaptive Counting的一部分。

解读Cardinality Estimation算法(第三部分:LogLog Counting)

Linear Counting算法相较于直接映射bitmap的方法能大大节省内存(大约只需后者1/10的内存),但毕竟只是一个常系数级的降低,空间复杂度仍然为O(Nmax)。而本文要介绍的LogLog Counting却只有O(log2(log2(Nmax)))。例如,假设基数的上限为1亿,原始bitmap方法需要12.5M内存,而LogLog Counting只需不到1K内存(640字节)就可以在标准误差不超过4%的精度下对基数进行估计,效果可谓十分惊人。

简介

LogLog Counting(以下简称LLC)出自论文“Loglog Counting of Large Cardinalities”。LLC的空间复杂度仅有O(log2(log2(Nmax))),使得通过KB级内存估计数亿级别的基数成为可能,因此目前在处理大数据的基数计算问题时,所采用算法基本为LLC或其几个变种。下面来具体看一下这个算法。
设a为待估集合(哈希后)中的一个元素,由上面对H的定义可知,a可以看做一个长度固定的比特串(也就是a的二进制表示),设H哈希后的结果长度为L比特,我们将这L个比特位从左到右分别编号为1、2、…、L:
Cardinality Estimation算法
又因为a是从服从均与分布的样本空间中随机抽取的一个样本,因此a每个比特位服从如下分布且相互独立。
P(x=k)={0.5(k=0)0.5(k=1)P(x=k)=\left\{\begin{array}{l}{0.5(k=0)} \\ {0.5(k=1)}\end{array}\right.
通俗说就是a的每个比特位为0和1的概率各为0.5,且相互之间是独立的。
设ρ(a)为a的比特串中第一个“1”出现的位置,显然1≤ρ(a)≤L,这里我们忽略比特串全为0的情况(概率为1/2L)。如果我们遍历集合中所有元素的比特串,取ρmax为所有ρ(a)的最大值。

此时我们可以将2ρmax作为基数的一个粗糙估计,即:
n^=2ρmax\hat{n}=2^{\rho_{\max }}
从二次分布和伯努利过程的角度来分析了为什么可以这么估计,忽略。

误差分析

StdError(n^/n)1.30mStdError(\hat{n} / n) \approx \frac{1.30}{\sqrt{m}}
其中m是桶数,总之误差就很小啦

算法应用

误差控制

在应用LLC时,主要需要考虑的是分桶数m,而这个m主要取决于误差。根据上面的误差分析,如果要将误差控制在ϵ之内,则:m>(1.30ϵ)2m>\left(\frac{1.30}{\epsilon}\right)^{2}

内存使用分析

内存使用与m的大小及哈希值得长度(或说基数上限)有关。设H的值为32bit,由于ρmax≤32,因此每个桶需要5bit空间存储这个桶的ρmax,m个桶就是5×m/8字节。例如基数上限为一亿(约2272^{27}),当分桶数m为1024时,每个桶的基数上限约为227/210=2172^{27} / 2^{10}=2^{17},而log2(log2(217))=4.09\log _{2}\left(\log _{2}\left(2^{17}\right)\right)=4.09,因此每个桶需要5bit,需要字节数就是5×1024/8=640。

合并

与LC不同,LLC的合并是以桶为单位而不是bit为单位,由于LLC只需记录桶的ρmax,因此合并时取相同桶编号数值最大者为合并后此桶的数值即可。

小结

本文主要介绍了LogLog Counting算法,相比LC其最大的优势就是内存使用极少。不过LLC也有自己的问题,就是当n不是特别大时,其估计误差过大,因此目前实际使用的基数估计算法都是基于LLC改进的算法,这些改进算法通过一定手段抑制原始LLC在n较小时偏差过大的问题。后面要介绍的HyperLogLog Counting和Adaptive Counting就是这类改进算法。

第四弹居然打不开了
503 (服务不可用) 服务器目前无法使用(由于超载或停机维护)。 通常,这只是暂时状态。
刚好也觉得自己是不是有点太深入了,溜了溜了。(明明都没怎么仔细看公式推导就好意思说自己太深入????????)

相关文章: