HyperLogLog算法是如何工作的?

最近我在业余时间学习了不同的算法,我发现了一个非常有趣的算法,叫做HyperLogLog算法——它可以估计一个列表中有多少个唯一的项目。

这对我来说特别有趣,因为它让我回到了我使用MySQL的日子,当时我看到了“基数”值(直到最近我一直认为它是计算出来的,而不是估计出来的)。

因此,我知道如何在O(n)中编写一个算法,该算法将计算数组中有多少唯一项。我用JavaScript写的:

function countUniqueAlgo1(arr) {
var Table = {};
var numUnique = 0;
var numDataPoints = arr.length;
for (var j = 0; j < numDataPoints; j++) {
var val = arr[j];
if (Table[val] != null) {
continue;
}
Table[val] = 1;
numUnique++;
}
return numUnique;
}

但问题是我的算法,虽然O(n),使用大量的内存(存储值在Table)。

我一直在阅读这篇论文,了解如何在O(n)时间内使用最小内存计算列表中的重复项。

它解释说,通过哈希和计数位或其他东西,人们可以在一定的概率内估计(假设列表是均匀分布的)列表中唯一项的数量。

我看了报纸,但我似乎不懂。谁能给个更外行的解释?我知道哈希是什么,但我不明白它们是如何在这个HyperLogLog算法中使用的。

74812 次浏览

直观地说,如果你的输入是一个大的随机数集(例如哈希值),它们应该均匀地分布在一个范围内。让我们假设范围为10位,以表示1024以下的值。然后观察最小值。假设是10。然后估计基数约为100 (10 × 100≈1024)。

当然,阅读论文是为了了解真正的逻辑。

另一个很好的示例代码解释可以在这里找到:
该死的酷算法:基数估计-尼克的博客 < / p >

该算法背后的主要技巧是,如果你观察一个随机整数流,看到一个二进制表示以某个已知前缀开始的整数,则该流的基数更有可能是前缀的2^(大小)。

也就是说,在一个随机的整数流中,~50%的数字(二进制)以“1”开头,25%以“01”开头,12,5%以“001”开头。这意味着如果你观察一个随机流并看到一个“001”,那么这个流的基数为8的概率更高。

(前缀“00..”“1”没有特殊含义。它的存在只是因为在大多数处理器中很容易找到二进制数的最高位)

当然,如果只观察到一个整数,这个值出错的可能性就很大。这就是为什么该算法将流划分为“m”个独立的子流,并保持所见“00…每个子流的1”前缀。然后,通过取每个子流的平均值来估计最终值。

这就是这个算法的主要思想。有一些细节缺失(例如,对低估计值的修正),但这些都写得很好。对不起,我的英语很糟糕。

HyperLogLog是概率数据结构。它计算列表中不同元素的数量。但与直接的方法(拥有一个集合并向集合中添加元素)相比,它以近似的方式进行此操作。

在了解HyperLogLog算法是如何做到这一点之前,我们必须理解为什么需要它。简单的方法的问题是它会消耗O(distinct elements)的空间。为什么这里有一个大O符号而不是不同的元素?这是因为元素可以有不同的大小。一个元素可以是1,另一个元素是"is this big string"。因此,如果你有一个巨大的列表(或一个巨大的元素流),它将占用大量内存。


概率计算

怎样才能合理地估计出一些独特的元素呢?假设你有一个长度为m的字符串,它由{0, 1}组成,且概率相等。它从0开始,2个0,k个0的概率是多少?它是1/21/41/2^k。这意味着如果你遇到了以k 0开头的字符串,你大概已经浏览了2^k元素。这是一个很好的起点。有一个均匀分布在02^k - 1之间的元素列表,你可以计算二进制表示中最大的0前缀的最大数量,这将给你一个合理的估计。

问题是,假设从02^k-1有均匀分布的数字太难实现了(我们遇到的数据大多不是数字,几乎从来都不是均匀分布的,并且可以在任何值之间。但是使用良好的哈希函数,你可以假设输出位是均匀分布的,并且大多数哈希函数的输出在02^k - 1之间(SHA1给出的值在02^160之间)。因此,到目前为止,我们所实现的是,通过只存储一个log(k)位大小的数字,我们可以估计具有最大基数为k位的唯一元素的数量。缺点是我们的估计有很大的差异。一件很酷的事情,我们几乎创建了2^k-10纸(它的估计有点聪明,但我们仍然接近)。

重对数

在进一步讨论之前,我们必须理解为什么我们的第一个估计不是那么好。其背后的原因是高频0前缀元素的随机出现会破坏所有内容。改进它的一种方法是使用许多哈希函数,为每个哈希函数计算max,最后将它们平均出来。这是一个很好的想法,它将改善估计,但< >强重对数纸< / >强使用了稍微不同的方法(可能是因为散列有点昂贵)。

他们使用一个散列,但将其分为两部分。一个被称为桶(桶的总数是2^x),另一个-基本上与我们的散列相同。我很难搞清楚到底发生了什么,所以我会举个例子。假设你有两个元素,你的哈希函数从02^10给出了两个值:344387。你决定有16个桶。所以你有:

0101 011000  bucket 5 will store 1
0110 000011  bucket 6 will store 4

通过使用更多的桶,可以减小方差(使用的空间稍微多一些,但仍然很小)。使用数学技能,他们能够量化误差(这是1.3/sqrt(number of buckets))。

HyperLogLog

< >强HyperLogLog < / >强没有引入任何新的思想,但主要是使用大量的数学来改进先前的估计。研究人员发现,如果你从桶中移除30%的最大数字,你的估计会显著提高。他们还使用另一种算法对数字求平均。这篇论文数学很重。


我想用最近的一篇论文来结束,它展示了一个hyperLogLog算法的改进版本(到目前为止,我没有时间完全理解它,但也许以后我会改进这个答案)。