我的一位朋友在接受采访时被问到以下问题。谁能告诉我如何解决它?
我们有一个相当大的日志文件,大约5GB。日志文件的每一行都包含用户在我们网站上访问过的URL。我们想弄清楚用户访问过的最受欢迎的100个网址是什么。怎么做?
答案 0 :(得分:7)
如果我们有超过10GB的RAM,只需使用hashmap直接进行。
否则,使用哈希函数将其分成几个文件。然后处理每个文件并获得前5名。每个文件的“前5名”,将很容易获得前5名。
另一种解决方案是使用任何外部排序方法对其进行排序。然后扫描文件一次以计算每次出现的次数。在此过程中,您无需跟踪计数。你可以安全地扔掉任何没有进入top5的东西。
答案 1 :(得分:2)
只需根据URL对日志文件进行排序(如果选择堆排序或快速排序等算法,则需要常量空间),然后计算每个URL显示的次数(简单,具有相同URL的行是下一个彼此)。
总体复杂度为O(n * Log(n))。
为什么要拆分多个文件并且只为每个文件保留前3个(或前5个或前N个)是错误的:
File1 File2 File3
url1 5 0 5
url2 0 5 5
url3 5 5 0
url4 5 0 0
url5 0 5 0
url6 0 0 5
url7 4 4 4
url7从未进入单个文件中的前3名,但总体上是最好的。
答案 2 :(得分:1)
由于日志文件相当大,您应该使用流阅读器读取日志文件。不要在内存中全部阅读。 我希望在我们处理日志文件时,在内存中包含可能不同的链接数是可行的。
// Pseudo
Hashmap map<url,count>
while(log file has nextline){
url = nextline in logfile
add url to map and update count
}
List list
foreach(m in map){
add m to list
}
sort the list by count value
take top n from the list
运行时为O(n)+ O(m * log(m)),其中n是行中日志文件的大小,其中m是不同找到链接的数量。
这是伪代码的C#实现。未提供实际的文件读取器和日志文件。 提供了使用存储器中的列表读取日志文件的简单模拟。
该算法使用散列映射来存储找到的链接。排序算法之后创建了前100个链接。一个简单的数据容器数据结构用于排序算法。
内存复杂性取决于预期的不同链接。 hashmap必须能够包含找到的不同链接, 否则这个算法将不起作用。
// Implementation
using System;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public static void Main(string[] args)
{
RunLinkCount();
Console.WriteLine("press a key to exit");
Console.ReadKey();
}
class LinkData : IComparable
{
public string Url { get; set; }
public int Count { get; set; }
public int CompareTo(object obj)
{
var other = obj as LinkData;
int i = other == null ? 0 : other.Count;
return i.CompareTo(this.Count);
}
}
static void RunLinkCount()
{
// Data setup
var urls = new List<string>();
var rand = new Random();
const int loglength = 500000;
// Emulate the log-file
for (int i = 0; i < loglength; i++)
{
urls.Add(string.Format("http://{0}.com", rand.Next(1000)
.ToString("x")));
}
// Hashmap memory must be allocated
// to contain distinct number of urls
var lookup = new Dictionary<string, int>();
var stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
// Algo-time
// O(n) where n is log line count
foreach (var url in urls) // Emulate stream reader, readline
{
if (lookup.ContainsKey(url))
{
int i = lookup[url];
lookup[url] = i + 1;
}
else
{
lookup.Add(url, 1);
}
}
// O(m) where m is number of distinct urls
var list = lookup.Select(i => new LinkData
{ Url = i.Key, Count = i.Value }).ToList();
// O(mlogm)
list.Sort();
// O(m)
var top = list.Take(100).ToList(); // top urls
stopwatch.Stop();
// End Algo-time
// Show result
// O(1)
foreach (var i in top)
{
Console.WriteLine("Url: {0}, Count: {1}", i.Url, i.Count);
}
Console.WriteLine(string.Format("Time elapsed msec: {0}",
stopwatch.ElapsedMilliseconds));
}
}
修改:此答案已根据评论更新