web-dev-qa-db-fra.com

Comment puis-je obtenir les 100 chiffres les plus fréquents sur 4 000 000 000 numéros?

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?

83
阿尔曼

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 :

  • Une fois que nous avons commencé à avoir un top 100 décent, vous pouvez ignorer des godets entiers dont la taille est inférieure à notre 100e entrée. Pour cela, nous pouvons utiliser une valeur spéciale (telle -1) dans la matrice de comptage, pour indiquer qu'il n'y a pas d'index. Selon les données, cela peut réduire considérablement le nombre de passes requises.
  • Lors du comptage d'éléments identiques dans le lot de tri, nous pouvons faire des sauts de la taille de votre 100e entrée (puis prendre quelques étapes à l'envers. Je peux partager de pseudo-code si nécessaire)

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:

  1. Vous pouvez essayer une autre sélection de n bits à la place (par exemple, les bits les moins significatifs). Notez que cela ne vous aidera toujours pas si les mêmes numéros apparaissent un milliard de fois.
  2. Si l'entrée est des entiers 32 bits, la plage de valeurs possibles est limitée et il ne peut y avoir que quelques milliers de numéros différents dans chaque godet. Donc, si un godet est vraiment important, nous pouvons traiter ce seau différemment: gardez simplement un compteur pour chaque valeur unique dans cette plage. Nous pouvons réutiliser le tableau de lots pour cela.
0
Kris Van Bael