如何在数组中找到最常用的数字?阵列可能非常大,例如2GB,我们只有有限的内存,比如100MB。
我正在考虑外部排序,即排序和复制彼此相邻的数字。或者hashma。但不知道如何处理有限的内存。而且我甚至不确定外部排序是否对此有好处。
答案 0 :(得分:2)
在最坏的情况下,除了一个出现两次的数字外,你的所有数字都是截然不同的,除非你有两个重复的数字同时加载到主内存中,否则无法在主内存中检测到这个数字,这是不太可能的如果您的总数据大小远大于主内存大小,则不进行排序。在这种情况下,渐近最好的做法是批量排序数字并保存到文件中的磁盘,然后执行合并排序合并步骤,将所有已排序的文件一次读入内存几行,并输出合并的排序列表到新文件。然后按顺序浏览聚合排序文件,并计算每次看到的数字的次数,跟踪发生次数最多的数字。
如果您认为最常见的数字是50%或更高频率,那么您可以做得更好。只需经过一次数字列表就可以解决持续额外内存的问题。基本上,您首先将最常见的值(MCV)初始化为第一个数字,然后将计数器N初始化为1.然后,您将浏览列表。如果列表中的下一个数字是MCV,则将N增加1。否则,您将N减1.如果N为0且下一个数字不同于MCV,则将MCV设置为新数字并将N设置为1.很容易证明这将以MCV中存储的最常见值终止
答案 1 :(得分:0)
如果您的内存有限,但处理能力合理且速度超快不是问题,根据您的数据集,您可以尝试以下方法:
迭代数组计数1到1000的数字。保持计数最大的数字。然后计算1001到2000.保持最大数量,这是第一批中最大的。重复,直到所有数字都被计算在内。
我确信根据数据集的具体情况,可以对此进行许多优化。
答案 2 :(得分:0)
假设:
让我们做直方图。
histogram
的整数数组,最多可存储26214400个计数器。计数器初始设置为0。x
时,请执行histogram[x]++
。histogram
中找到最大值,迭代一次。如果最大值为histogram[i]
,那么i
是最常用的数字。瓶颈是第2步,迭代2 GB数组,但我们只做了一次。
如果第二个假设不成立(即有超过26214400个不同的整数):
对索引为0到26214399的数字进行直方图。保留直方图中最常用的数字。对索引为26214400到52428798的数字进行直方图。保留直方图中最常用的数字和之前最常用的数字。等等。
在最坏的情况下,使用2 ^ 32个不同的数字,它将在2 GB数组上进行(2 ^ 32/26214400 + 1)= 164次迭代。
一般来说,它会进行(NUMBER_OF_DISTINCT_NUMBERS / 26214400 + 1)次迭代。
答案 3 :(得分:0)
假设4字节整数,您可以将(100/4)= 25MB整数拟合到可用内存中。
读取您的大文件,计算0到25MB-1范围内每次出现的数字和数字。使用大数组来累积计数。
找出最常出现的号码,存储号码及其频率并清除阵列。
仔细阅读大文件,重复25MB ... 50MB-1范围内的数字计数过程。
查找新阵列中最常出现的数字。将其与您在步骤2中存储的数量/频率进行比较。存储频率较高的数字/频率并清除阵列。
泡沫,冲洗,重复。
ETA:如果我们可以假设只有一个答案,那么两个不同的数字具有相同的最高频率,那么如果特定范围的数组显示平局,则可以丢弃所有数字。否则,为每个范围存储获胜者的问题会变得更加复杂。
答案 4 :(得分:0)
根据假设的不同,更好的方法可能是使用MJRTY算法:
http://www.cs.utexas.edu/~moore/best-ideas/mjrty/
或其概括:
这个想法是,只有两个变量(一个计数器和一个值存储),你可以确定,如果存在多数元素(严格地出现超过50%的时间),那么该元素是什么。泛化需要(k + 1)个计数器和值存储来查找出现100 / k%的元素。
因为这些只是大多数的候选者(如果有k个多数元素,那些就是它们;但如果没有k多数元素,那么这些只是随机元素),第二次传递数据可以帮助您获得候选人的确切数量,并确定哪一个是多数元素。
这非常快且内存效率很高。
除了4kb的内存之外,还有其他一些优化,你应该能够很好地找到2GB数据的主要元素 - 这取决于你拥有的数据类型。