cardinality-estimation
如何计算数据流中不同元素的个数?例如,独立访客
方案1: HashSet
首先最容易想到的办法是用
方案2: bitmap
这个方案的缺点是,
实际上目前还没有发现在大数据场景中准确计算基数的高效算法,因此在不追求绝对准确的情况下,使用近似算法算是一个不错的解决方案。
方案3: Linear Counting
- 选择一个哈希函数
h ,其结果服从均匀分布 - 开一个长度为
m 的bitmap ,均初始化为0(m 设为多大后面有讨论) - 数据流每来一个元素,计算其哈希值并对
m 取模,然后将该位置为1 - 查询时,设
bitmap 中还有u 个bit 为0 ,则不同元素的总数近似为$$-m\log\dfrac{u}{m}$$
在使用m
。
精度越高,需要的
方案4: LogLog Counting
-
均匀随机化。选取一个哈希函数
h 应用于所有元素,然后对哈希后的值进行基数估计。哈希函数h 必须满足如下条件,- 哈希碰撞可以忽略不计。哈希函数
h 要尽可能的减少冲突 h 的结果是均匀分布的。也就是说无论原始数据的分布如何,其哈希后的结果几乎服从均匀分布(完全服从均匀分布是不可能的,D. Knuth 已经证明不可能通过一个哈希函数将一组不服从均匀分布的数据映射为绝对均匀分布,但是很多哈希函数可以生成几乎服从均匀分布的结果,这里我们忽略这种理论上的差异,认为哈希结果就是服从均匀分布) 。- 哈希后的结果是固定长度的
- 哈希碰撞可以忽略不计。哈希函数
-
对于元素计算出哈希值,由于每个哈希值是等长的,令长度为
L -
对每个哈希值,从高到低找到第一个
1 出现的位置,取这个位置的最大值,设为p ,则基数约等于$$2^p$$
如果直接使用上面的单一估计量进行基数估计会由于偶然性而存在较大误差。因此,p[i]
,然后对这
关于该算法的数学证明,请阅读原始论文和参考资料里的链接,这里不再赘述。
方案5: HyperLogLog Counting
HyperLogLog Counting(以下简称
-
第
1 个改进是使用调和平均数替代几何平均数,调和平均数可以有效抵抗离群值的扰。注意LLC 是对各个桶取算术平均数,而算术平均数最终被应用到2 的指数上,所以总体来看LLC 取的是几何平均数。由于几何平均数对于离群值(例如0 )特别敏感,因此当存在离群值时,LLC 的偏差就会很大,这也从另一个角度解释了为什么基数n 不太大时LLC 的效果不太好。这是因为n 较小时,可能存在较多空桶,这些特殊的离群值干扰了几何平均数的稳定性。使用调和平均数后,估计公式变为$$\hat{n}=\frac{\alpha_mm^2}{\Sigma_{i=0}^{m-1} p[i]}$$ ,其中$$\alpha_m=(m\int_0^{\infty}(log_2(\frac{2+u}{1+u}))^mdu)^{-1}$$ -
第
2 个改进是加入了分段偏差修正。具体来说,设e 为基数的估计值,- 当
$$e \leq \frac{5}{2}m$$ 时,使用Linear Counting - 当
$$\frac{5}{2}m<e\leq \frac{1}{30}2^{32}$$ 时,使用HyperLogLog Counting - 当
$$e>\frac{1}{30}2^{32}$$ 时,修改估计公式为$$\hat{n}=-2^{32}\log(1-e/2^{32})$$
- 当
关于分段偏差修正的效果分析也可以在原论文中找到。