2012-05-24 5 views
5

내 친구 중 한 명이 인터뷰에서 다음 질문을 받았습니다. 아무도 그것을 해결하는 방법을 말해 줄 수 있습니까?로그 파일에서 상위 100 개 URL 얻기

매우 큰 로그 파일 인 약 5GB가 있습니다. 로그 파일의 각 행에는 사용자가 사이트에서 방문한 URL이 들어 있습니다. 우리는 사용자가 방문한 가장 인기있는 100 개의 URL이 무엇인지 파악하려고합니다. 그것을하는 방법?

답변

7

10GB 이상의 RAM이있는 경우 해시 맵을 사용하면 간단하게 처리 할 수 ​​있습니다.

그렇지 않으면 해시 함수를 사용하여 여러 파일로 분리하십시오. 그런 다음 각 파일을 처리하고 상위 5 개를 얻습니다. 각 파일에 대해 "상위 5 개"를 사용하면 전체 상위 5 개를 쉽게 얻을 수 있습니다.

또 다른 솔루션은 외부 정렬 방법을 사용하여 정렬 할 수 있습니다. 그런 다음 파일을 한 번 스캔하여 각각의 발생을 계산하십시오. 이 과정에서 카운트를 추적 할 필요가 없습니다. top5에없는 물건은 안전하게 버릴 수 있습니다.

+0

두 번째 대안은 갈 방법입니다. Divide-and-conquer 방식은 쉽게 구현할 수 있으며 작은 파일에서 계산할 때 해시 맵을 사용하는 경우 선형 시간이 필요합니다. 파일을 정렬하는 것은 상대적으로 느립니다. –

+0

@ EmilVikström, 나는 당신과 동의합니다. 나는 그것이 내 마음 속으로 들어오는 첫 번째 것이므로 세 번째 것을 썼다. 그러나 어쨌든 인터뷰에서 면접관이 이전 솔루션을 무효로 만드는 환경을 구성하여 다른 것을 생각해 낼 수 있는지 확인하는 경우가 종종 있습니다. – Haozhun

+1

"top5 거리에 들어 가지 않는 물건은 안전하게 던질 수 있습니다." - 아냐, 틀렸어. – Thomash

2

URL에 따라 로그 파일을 정렬하고 (힙 정렬 또는 빠른 정렬과 같은 알고리즘을 선택한 경우 일정한 공간이 필요함) 각 URL을 몇 번 표시하는지 계산합니다 (쉽게 URL이 같은 줄은 서로 옆에).

전체적인 복잡성은 O (n * Log (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으로하지 않습니다하지만입니다 : 많은 파일의 분할 및 각 파일에 대해 (또는 상위 5 개 또는 상위 N) (3) 상단에만 유지가 잘못 왜

전반적으로 가장 좋습니다.

+0

5GB 파일의 경우 일정 공간이 너무 많습니다. – Haozhun

+0

아무런 문제없이 5GB를 메인 메모리에 저장할 수 있습니다. 그러나 RAM이 N GB 밖에 없다고 생각하면 N GB 블록으로 로그 파일을 분할하고 각 솔루션에 솔루션을 적용한 다음 결과를 집계 할 수 있습니다. – Thomash

+1

분할 및 집계 방법에 대해 자세히 설명 할 수 있습니까? – Haozhun

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의 * 로그 (m)) n은 라인의 로그 파일의 크기 및 여기서 m은 별개의 볼 연결의 개수이다.

다음은 의사 코드의 C# 구현입니다. 실제 파일 판독기 및 로그 파일은 제공되지 않습니다. 대신 메모리의 목록을 사용하여 로그 파일을 읽는 간단한 에뮬레이션이 제공됩니다.

알고리즘은 해시 맵을 사용하여 발견 된 링크를 저장합니다. 정렬 알고리즘은 이후 상위 100 개의 링크를 찾습니다. 간단한 데이터 컨테이너 데이터 구조가 정렬 알고리즘에 사용됩니다.

메모리 복잡성은 예상되는 개별 링크에 따라 다릅니다. 해시 맵은 발견 된 고유 링크 인 을 포함 할 수 있어야합니다. 그렇지 않으면이 알고리즘이 작동하지 않습니다.

// 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)); 
    } 
} 

편집 :이 답변은 코멘트를 기반으로 업데이트되었습니다

  • 추가 :
  • 추가 시간과 메모리 복잡도 분석을 실행 : 의사 코드를
  • 추가 : 우리는 방법을 설명 꽤 큰 로그 파일을 관리하십시오.
+3

이 답변은 C#을 알고 있지만 아무도 그것에 관심이 없다는 것을 보여줍니다. 질문의 태그는'C# '이 아니라'알고리즘'과'데이터 구조'입니다. – Thomash

+1

C#을 알고 있다는 것을 보여주기위한 의도는 아니지만 의도는 코드를 통해 알고리즘을 '표시'하는 것이 었습니다. – Kunukn

+0

@ Thomash가 지적하고자하는 것은 귀하가 귀하의 답변을 간결하게 제공해야한다는 것입니다. 긴 코드에 대한 짧은 대답을 선호합니다. 그렇지? 그건 그렇고, OP의 질문에 키워드는 "상당히 큰"하지만 당신은 분명히 그것을 무시. – Haozhun