元素出现多次,希望找到最频繁的元素
n 不同元素的数量
m 数据流中元素个数
Misra Gries算法
我们提出如下的方法:
(1)处理一个新到来的元素x时
(2)If已经为其分配计数器,增加之
(3)Else If没有相应的计数器,但计数器个数少于k,为x分配计数器k,并设为1
(4)Else所有的计数器值减1,删除值为0的计数器
这个算法称为Misra Gries(MG)算法。第一种情况,如果内存中已经有新到来元素的计数器,则只需要将其值加1即可;第二种情况,如果还没有为新到来的元素提供计数器,并且内存没有被填满时,则可以为这个元素的计数器开辟新的空间;第三种情况,当新到来的元素没有被分配计数器,同时内存中的计数器个数已经达到了k个,也就是分配的内存空间已经被填满时,则将所有的计数器值减1,删除值为0的计数器,此时内存中就重新有位置了,我们再为这个新到达的元素分配一个计数器即可。当然,别忘了要将其置为1。
我们用前面的这组数字举个例子:32,12,14,32,7,12,32,7,6。
假设内存中有3个存放计数的空间。
前3个数据进入内存时,都符合情况二,将它们加入内存中。
抵达数据:32 内存:[32:1]
抵达数据:12 内存:[32:1][12:1]
抵达数据:14 内存:[32:1][12:1][14:1]
第4个数据32到来时,将32的计数值加1。
抵达数据:32 内存:[32:2][12:1][14:1]
当第5个数据7抵达时,符合情况三,也就是频繁元素统计的大数据处理的关键,我们将所有的计数器值减1,并删除那些值为0的计数器,然后为新到来的7建立计数器,并置为1。
抵达数据:7 内存:[32:1][12:0][14:0]
内存:[32:1]
内存:[32:1][7:1]
然后是12、32和7,分别属于情况二和情况一。
抵达数据:12 内存:[32:1][7:1][12:1]
抵达数据:32 内存:[32:2][7:1][12:1]
抵达数据:7 内存:[32:2][7:2][12:1]
当6 到达时,内存已经被填满,我们执行算法的第三种情况。
抵达数据:6 内存:[32:1][7:1][12:0]
内存:[32:1][7:1]
内存:[32:1][7:1][6:1]
使用该算法求出的几个频繁元素就是32、7和6。就原数据来说,最频繁的3个元素分别是32、7、12。我们成功地找出了32和7,虽然没有找出最频繁的3个元素,但整体来说已经是一个不错的结果了。如果只需要最频繁的元素,那么该算法已经在这组数字中找出了32这个最频繁的元素。不过在最后对频繁元素的计数值一般是不准确的,所以还要对它的计数进行分析,估计它所记录的数值误差如何。当我们使用近似手段处理问题时,一定要对近似解进行误差分析,研究所得到的近似解是否在我们可以接受的误差范围之内,误差太大的近似解也同样达不到解决问题的目的。内存中的频繁元素的计数器不断地由于内存被占满而被削减,显然是低估了。
没错,我们不能仅仅知道结果是低估了准确值,最好还要能根据算法分析出究竟低估了多少。想要知道低估了多少,我们首先要考虑的就是一个计数器被减小了几次。这就需要我们考虑到在整个算法的执行过程中,执行过多少个减少计数器的步骤。假如把整个结构的权重(也就是计数器的和)记作m',整个数据流的权重(全部元素的数量)记作m。每当计数器需要降低时,由于内存中有k个计数器,我们也就减少了k个计数器,但是这时新到来的元素x并未计入内存中的计数器,它的到来只是标志着该削减计数器了,所以我们少加了k+1个计数器,,我们可以得到一个好的频繁元素的估计。
所以最多 有 (m-m') / (k+1) 减少 步骤 估计值和真实值最多相差 (m-m') / (k+1),下限是 整个结构 的计数器的值
可以看出,错误的界限是与k成反比的。这说明什么?
k 是内存中计数器的个数,也就是内存的大小,这说明内存越大,结果就越准确。
利用数据概要来计算错误的界限,只需要记录m、m' 和k就可以了。
不过不难看出,如果数据集合中每个元素的数量都相差不多的话,这个算法求出的结果会具有很大的随机性,好在我们一般需要处理的数据都满足Zipf法则。
Zipf法则:典型的频率分布是高度偏斜的,只有少数频繁元素,最多10%的元素占元素总个数的90%。这个定律说明,只有少数的元素是大量重复出现的,而绝大多数元素的出现是不频繁的。
根据Zipf法则我们知道,频繁元素的种类只有少数,而其数量往往是非常大的,在算法执行的过程中,不断地削减内存中的计数器对于频繁元素最终被保留在内存里不会有太大程度的影响。
《大数据算法》 王宏志