Hier dans une entrevue de codage, on m'a demandé comment obtenir les 100 chiffres les plus fréquents sur 4 000 000 000 entiers (peut contenir des doublons), par exemple:
813972066
908187460
365175040
120428932
908187460
504108776
La première approche qui me suis venu à l'esprit utilisait HashMap:
static void printMostFrequent100Numbers() throws FileNotFoundException {
// Group unique numbers, key=number, value=frequency
Map<String, Integer> unsorted = new HashMap<>();
try (Scanner scanner = new Scanner(new File("numbers.txt"))) {
while (scanner.hasNextLine()) {
String number = scanner.nextLine();
unsorted.put(number, unsorted.getOrDefault(number, 0) + 1);
}
}
// Sort by frequency in descending order
List<Map.Entry<String, Integer>> sorted = new LinkedList<>(unsorted.entrySet());
sorted.sort((o1, o2) -> o2.getValue().compareTo(o1.getValue()));
// Print first 100 numbers
int count = 0;
for (Map.Entry<String, Integer> entry : sorted) {
System.out.println(entry.getKey());
if (++count == 100) {
return;
}
}
}
Mais cela organiserait probablement une exception exceptionnelle pour l'ensemble de données de 4 000 000 000 numéros. De plus, puisque 4 000 000 000 dépassent la longueur maximale de A Java matray, disons que les chiffres sont dans un fichier texte et ils ne sont pas triés. Je suppose que la multithreading ou la carte est plus appropriée pour un ensemble de données de grande taille. ?
Comment calculer les 100 valeurs top 100 lorsque les données ne correspondent pas à la mémoire disponible?
Je suppose que le point du défi consiste à traiter cette grande quantité de données sans consommer trop de mémoire et évitez d'analyser l'entrée trop de fois.
Voici un algorithme qui nécessiterait deux tableaux pas trop gros. Je ne sais pas sur Java, mais je suis convaincu que cela peut être fait pour exécuter très rapide en C:
Créer un comptez matrice de taille 2 ^ n pour compter le nombre de numéros d'entrée en fonction de leurs nœuds les plus significatifs. Cela nécessitera une première analyse sur les données d'entrée mais est vraiment simple à faire. Je vais d'abord essayer avec n = 20 (environ un million de godets).
Évidemment, nous ne traiterons pas les données un seau à une époque, car cela nécessiterait la lecture de l'entrée un million de fois, nous choisissons plutôt notre taille de lot optimale B et allouer une matrice A Batch de taille B. B pourrait être comme 40m, afin que nous visions à lire l'entrée environ 100 fois. (Tout dépend de la mémoire disponible).
Ensuite, nous parcourons la matrice de comptage pour regrouper la première gamme de godets pour que la somme soit proche, mais ne dépasse pas B.
Pour chaque gamme de ce type, nous analysons les données d'entrée, recherchez des numéros dans la plage et copiez ces numéros au tableau de lots. Étant donné que nous connaissons déjà la taille de chaque seau, nous pouvons les copier immédiatement regroupés par seau, de sorte que nous ne devons donc pas les trier seau par seau (vous pouvez réutiliser le réseau de comptage pour stocker les index de l'endroit où écrire la prochaine entrée). Ensuite, nous comptons les éléments identiques dans le tableau de lots triés et gardez une trace du top 100 jusqu'à présent.
Activez la prochaine gamme de godets pour lesquels la somme des comptes est sous la taille B, etc ...
Optimisations :
problèmes potentiels avec cette approche : Les numéros d'entrée pourraient être concentrés dans une petite gamme, vous pourriez obtenir un ou plusieurs godets simples plus grands que B. Solutions possibles: