找到集合中最常见的元素

时间:2012-11-15 11:24:41

标签: algorithm

让我们为您的网站构建一个分析程序,每次访问时都会记录该页面的地址,因此您的log.txt可能是

x.com/a
x.com/b
x.com/a
x.com/c
x.com/a

没有计数器,它只是一个日志文件,并且没有使用sql,因为这有数千个元素是你的一千个左右的唯一域名地址(x.com/a x.com/b),什么是通过此列表并吐出前10名网址的最有效方式。

我最好的解决方案是通过日志文件,然后如果哈希表中不存在该域,则将其添加为键,并递增它的值;然后在散列中搜索最大的10个值。

我不相信这是最好的解决方案,不仅仅是因为空间复杂性(如果独特域从几千到几百万,会发生什么),而且因为我需要在哈希表上进行另一次搜索找到最大的值。

3 个答案:

答案 0 :(得分:2)

即使是数千或数百万条目 - 您的方法也很好 - 它的运行时间平均线性(O(n)) - 所以它并没有那么糟糕。

但是,如果您想要更高的可伸缩性,可以使用map-reduce方法。

map(file):
   for each entry in file:
         emitIntermediate(getDomainName(entry),"1"))
reduce(domain,list):
   emit(domain,size(list))

以上内容将有效地为您提供(domain,count) tupples列表,您只需选择前10名。

选择前10名可以使用map-reduce(分布式)排序来实现可伸缩性 - 或者使用最小堆(在保持堆中遇到的前10个元素时进行迭代)。第二部分在this thread

中详细解释

关于空间复杂性:如果您使用的是64位系统,您可以将其用作RAM,并让操作系统尽其所能(通过在需要时将元素交换到磁盘),您不太可能需要更多那么你在64位机器上的Virtual Memory的数量。另一种方法是使用针对文件系统优化的哈希表(或B+ tree)并对其执行相同的算法。


但是,如果情况确实如此 - 并且数据不适合RAM,并且您无法使用map-reduce,我怀疑排序和迭代 - 尽管O(nlogn)将会{更高效(使用外部排序) - 因为DISK ACCESSES的数量将被最小化,并且磁盘访问比RAM访问慢得多。

答案 1 :(得分:1)

我建议每次检查文件都是错误的方法。一个更好的解决方案可能是解析文件并将数据推送到数据库(ravendb,或者其他no-sql应该是最简单的)。在那里,即使数据量非常大,查询数据也变得微不足道。

答案 2 :(得分:1)

不要重新发明轮子。 Coreutils'sortuniq可以处理您的日志文件

sort log.txt | uniq -c | sort -n -r

Coreutils可在* nix系统上使用,并已移植到Windows。

如果您确实需要在自己的代码中汇总此处理,请查阅您的语言的可用库以获取其multiset版本。例如,Python是Counter类,它很乐意告诉你most_common([n])