2012-11-15 56 views
1

让唉,你正在为你的网站分析程序,在其中您登录每次你访问它时页面的地址,让你的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个值。

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

回答

2

即使只有几千或几百万个条目 - 您的方法非常好 - 它的运行时间平均为线性 - 所以它并没有那么糟糕。

但是,如果您想要更高的可伸缩性,则可以使用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,并让OS做它罐(通过交换元素盘如果需要的话),你很可能不会需要在64位机器上的Virtual Memory的数量。另一种方法是使用为文件系统优化的散列表(或B+ tree),并对其执行相同的算法。


但是,如果这确实是这样的 - 和数据不适合RAM,并且你不能使用的map-reduce,我怀疑排序和迭代 - 虽然会O(nlogn)将更有效率(使用外部排序) - 因为磁盘访问的数量将最小化,并且磁盘访问速度比RAM访问慢得多。

1

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

1

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

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

Coreutils的是在* nix系统,并已移植到Windows。

如果您确实需要在自己的代码中汇总此处理,请参阅您的语言的可用库,以了解其版本multiset的版本。例如,Python的Counter类,它会愉快地告诉你most_common([n])