Ponieważ plik dziennika jest dość duży, należy odczytać plik dziennika za pomocą czytnika strumienia. Nie czytaj wszystkiego w pamięci. Oczekuję, że możliwe będzie posiadanie liczby możliwych różnych połączeń w pamięci podczas pracy nad plikiem dziennika.
// 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
czas przebiegu wynosi O (n) + O (M * log (m)), gdzie n jest rozmiar pliku dziennika w linii i w którym m oznacza liczbę odrębnych znalezione linki.
Oto C# implementacja pseudokodu. Rzeczywisty czytnik plików i plik dziennika nie są dostarczane. Zamiast tego dostarczana jest prosta emulacja odczytu pliku dziennika przy użyciu listy w pamięci.
Algorytm używa hashmap do przechowywania znalezionych linków. Algorytm sortowania znajduje później 100 najlepszych linków. Do algorytmu sortowania wykorzystywana jest prosta struktura danych kontenera danych.
Złożoność pamięci zależy od oczekiwanych, odrębnych łączy. Histop musi zawierać różne znalezione linki, inaczej ten algorytm nie zadziała.
// 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));
}
}
Edit: Ta odpowiedź została zaktualizowana na podstawie uwag
- dodania: czas i analizę złożoności pamięci
- dodaną działa: pseudo-kod
- dodania: wyjaśnić, w jaki sposób zarządzaj dość dużym plikiem rejestru
Twoja druga alternatywa to droga. Podejście "dziel i rządź" jest łatwe do wdrożenia, a otrzymasz liniowy czas (jeśli używasz hashmap do liczenia w mniejszych plikach). Sortowanie pliku jest bardzo powolne w porównaniu. –
@ EmilVikström, Zgadzam się z tobą. Napisałem trzecią, jako że przychodzi mi do głowy pierwsza. W każdym razie, w wywiadzie często zdarza się, że ankieter skonstruuje środowisko, które sprawi, że poprzednie rozwiązanie będzie nieprawidłowe, aby sprawdzić, czy możesz wymyślić coś innego. – Haozhun
"Możesz bezpiecznie rzucić wszystko, czego nie robi się w top5." -> Nie, to nie tak. – Thomash