HyperLogLog算法如何工作?


172

我最近在业余时间一直在学习不同的算法,我遇到的一个看起来很有趣的算法叫做HyperLogLog算法-该算法估计列表中有多少个唯一项。

这对我特别有趣,因为当我看到“基数”值(直到最近,我一直认为直到最近才算出它不是估计值)时,它才使我回到MySQL时代。

因此,我知道如何在On)中编写一种算法,该算法将计算数组中有多少个唯一项。我是用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;
}

但是问题是我的算法虽然为On),却占用了大量内存(将值存储在中Table)。

我一直在阅读本文有关如何在重复计数在列表Øñ)的时间和使用的内存。

它解释说,通过对位进行散列和计数或可以在一定概率(假设列表均匀分布)中估计的某种事物中列表中唯一项的数量。

我读过这篇论文,但似乎看不懂。有人可以给其他人解释吗?我知道什么是哈希,但是我不明白在此HyperLogLog算法中如何使用它们。


4
本文(research.google.com/pubs/pub40671.html)还总结了HyperLogLog算法和一些改进。我认为比原始论文更容易理解。
zhanxw 2013年

11
只是术语上的一个提示:有些人用词集来描述一组独特的物品。对于他们来说,如果您改用术语列表或数组,则可能更有意义。
Paddy3118

Answers:


153

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

也就是说,在整数的随机流中,〜50%的数字(二进制​​)以“ 1”开头,25%的数字以“ 01”开头,12,5%的数字以“ 001”开头。这意味着,如果您观察到随机流并看到“ 001”,则该流的基数为8的可能性更高。

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

当然,如果只观察一个整数,则该值错误的可能性很高。这就是为什么该算法将流划分为“ m”个独立子流,并保留每个子流的可见“ 00 ... 1”前缀的最大长度的原因。然后,通过获取每个子流的平均值来估计最终值。

这就是该算法的主要思想。有一些遗漏的细节(例如,对低估计值的更正),但在本文中都写得很好。对不起,英语不好。


“此流的基数为8的可能性更高”请您解释一下为什么000表示预期的试验次数2 ^ 3。我尝试计算试验次数的数学期望,假设我们至少有一个运行有3个零并且没有运行有4个零...
yura 2014年

5
在我读完这篇文章之前,我不太了解这篇论文。现在有道理了。
josiah 2014年

5
@yura我知道这是一个非常古老的评论,但对其他人可能有用。他说:“也就是说,在随机整数流中,(...)12.5%的数字以“ 001”开头。” 可能的基数为8,因为12.5%代表整个流的八分之一。
braunmagrin

111

HyperLogLog是一种概率数据结构。它计算列表中不同元素的数量。但是,与一种简单的方法(具有一个集合并向该集合中添加元素)相比,它的实现方式近似。

在查看HyperLogLog算法如何做到这一点之前,必须先了解为什么需要它。一种直接方式的问题是它占用O(distinct elements)了空间。为什么在这里有一个大的O表示法而不是单独的元素?这是因为元素可以具有不同的大小。一个元素可以是1另一个元素"is this big string"。因此,如果您有大量列表(或大量元素流),则会占用大量内存。


概率计数

一个人如何才能对许多独特元素做出合理的估计?假设您有长度的字符串m它由{0, 1}概率相同。以0开头,以2个零开头,以k个零开头的概率是多少?这是1/21/41/2^k。这意味着,如果遇到带k零的字符串,则可以大致浏览2^k元素。因此,这是一个很好的起点。具有之间有均匀分布的元素的列表02^k - 1你可以指望零的二进制表示的最大前缀的最大数量,这会给你一个合理的估计。

问题在于,很难假设从0t 均匀分布数字2^k-1(我们遇到的数据大多不是数字,几乎永远不会均匀分布,并且可以在任何值之间。但是使用良好的哈希函数,您可以假设输出位将平均分配,并且大多数散列函数的输出在0和之间2^k - 1SHA1为您提供介于0和之间的值2^160),因此到目前为止,我们可以k通过仅存储位的最大基数来估计唯一元素的数量一个大小的log(k)比特。缺点是我们的估算值有很大的差异。1984年的概率计数纸(估计值稍微有点聪明,但我们仍很接近)。

日志日志

在继续之前,我们必须了解为什么我们的第一个估算值没有那么大。其背后的原因是高频0前缀元素的随机出现会破坏所有内容。改善它的一种方法是使用许多哈希函数,为每个哈希函数计数最大值,最后将它们平均。这是一个绝妙的主意,它将改善估计值,但是LogLog论文使用的方法略有不同(可能是因为哈希值昂贵)。

他们使用了一个哈希,但将其分为两部分。一个称为存储桶(存储桶总数为2^x),另一个称为-与哈希基本相同。我很难了解发生了什么,所以我举一个例子。假设您有两个元素,并且您的哈希函数将值形式0赋予2^10了两个值:344387。您决定有16个水桶。所以你有了:

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

通过具有更多的存储桶,您可以减少方差(您使用更多的空间,但是仍然很小)。通过使用数学技能,他们能够量化误差(为1.3/sqrt(number of buckets))。

超级日志

HyperLogLog不会引入任何新的想法,但是通常会使用大量的数学方法来改进以前的估计。研究人员发现,如果您从存储桶中删除了最大数量的30%,则会大大提高估算值。他们还使用了另一种算法来求平均数。这篇论文数学很重。


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


2
我假设理论上k zeroes不是什么特别的事情。您可以改为寻找k ones与逻辑相同的方法,甚至寻找的k length字符串,{0,1}但采用一个这样的字符串并坚持下去?因为在这样的二进制字符串的情况下,它们都有1/2 ^ k的相等概率?
user881300

3
HyperLogLog不会删除最大数目的30%。这就是在LogLog论文中也介绍了SuperLogLog算法的思想。HyperLogLog算法的主要思想是使用谐波均值而不是SuperLogLog和LogLog所使用的几何均值来平均2的幂。
otmar

21

直觉是,如果您的输入是一大组随机数(例如,哈希值),则它们应在一个范围内均匀分布。假设范围最大为10位,代表最大值为1024。然后观察最小值。假设它是10。则基数估计约为100(10×100≈1024)。

阅读本文,了解真正的逻辑。

可以在此处找到带有示例代码的另一个很好的解释:
该死的酷算法:基数估计-Nick的博客


3
支持该死的酷算法博客文章的链接。确实帮助我掌握了算法。
Igor Serebryany,
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.