web-dev-qa-db-fra.com

Ecrivez un programme pour trouver les 100 plus grands nombres sur un tableau de 1 milliard

J'ai récemment assisté à une interview au cours de laquelle on m'a demandé "d'écrire un programme pour trouver les 100 plus grands nombres sur un tableau de 1 milliard".

Je n'ai pu que donner une solution de force brute qui consistait à trier le tableau en complexité de temps O(nlogn) et à prendre les 100 derniers nombres. 

Arrays.sort(array);

L'intervieweur cherchait une meilleure complexité temporelle, j'ai essayé plusieurs solutions mais je n'ai pas réussi à lui répondre. Existe-t-il une meilleure solution de complexité temporelle?

295
Gsab

Vous pouvez conserver une file d'attente prioritaire des 100 plus grands nombres, parcourir des milliards de numéros, chaque fois que vous rencontrez un nombre supérieur au plus petit nombre de la file d'attente (tête de la file d'attente), supprimer la tête de la file d'attente et ajouter le nouveau numéro. à la file d'attente.

EDIT: Comme le notait Dev, avec une file d'attente prioritaire implémentée avec un tas, la complexité de l'insertion dans la file d'attente est de O(logN)

Dans le pire des cas, vous obtenez billionlog2(100) qui est meilleur que billionlog2(billion)

En général, si vous avez besoin des K nombres les plus grands d'un ensemble de N nombres, la complexité est O(NlogK) plutôt que O(NlogN); cela peut être très important lorsque K est très petit comparé à N.

EDIT2:

Le temps attendu de cet algorithme est assez intéressant, car à chaque itération, une insertion peut ou non avoir lieu. La probabilité que le douzième nombre soit inséré dans la file d'attente est la probabilité qu'une variable aléatoire soit supérieure à au moins i-K variables aléatoires de la même distribution (les k premiers nombres sont automatiquement ajoutés à la file d'attente). Nous pouvons utiliser les statistiques d’ordre (voir link ) pour calculer cette probabilité. Par exemple, supposons que les nombres ont été choisis au hasard de manière uniforme dans {0, 1}, la valeur attendue du (i-K) ème nombre (sur i nombres) est (i-k)/i et qu'il est possible qu'une variable aléatoire soit supérieure à cette valeur est 1-[(i-k)/i] = k/i

Ainsi, le nombre d'insertions attendu est:

enter image description here

Et le temps d'exécution prévu peut être exprimé par:

enter image description here

(k heure pour générer la file d'attente avec les premiers éléments k, puis les comparaisons n-k et le nombre prévu d'insertions décrites ci-dessus, chacune prend un temps moyen log(k)/2)

Notez que lorsque N est très grand comparé à K, cette expression est beaucoup plus proche de n plutôt que NlogK. Ceci est quelque peu intuitif, car dans le cas de la question, même après 10000 itérations (ce qui est très petit par rapport à un milliard), la probabilité qu'un nombre soit inséré dans la file d'attente est très faible.

323
Ron Teller

Si cela est demandé dans une interview, je pense que l'intervieweur veut probablement voir votre processus de résolution de problème, pas seulement votre connaissance des algorithmes. 

La description est assez générale, alors vous pouvez peut-être lui demander quelle est la portée ou la signification de ces chiffres afin de clarifier le problème. Cela peut impressionner un intervieweur. Si, par exemple, ces chiffres représentent l’âge de la population d’un pays (par exemple, la Chine), le problème est beaucoup plus simple. Avec une hypothèse raisonnable que personne en vie n’a plus de 200 ans, vous pouvez utiliser un tableau int de 200 (peut-être 201) pour compter le nombre de personnes du même âge en une seule itération. Ici, l'index signifie l'âge. Après cela, c'est un morceau de gâteau de trouver 100 plus grand nombre. En passant, cet algo s'appellecomptage sort

Quoi qu'il en soit, rendre la question plus précise et plus claire est une bonne chose pour une interview.

133
jin

Vous pouvez parcourir les nombres qui prennent O (n)

Chaque fois que vous trouvez une valeur supérieure au minimum actuel, ajoutez la nouvelle valeur à une file d'attente circulaire de taille 100.

Le min de cette file d'attente circulaire est votre nouvelle valeur de comparaison. Continuez à ajouter à cette file d'attente. S'il est plein, extrayez le minimum de la file d'attente.

70
Regenschein

Je me suis rendu compte que cela est étiqueté avec «algorithme», mais jetterai quelques autres options, car il devrait probablement aussi être étiqueté «interview».

Quelle est la source des 1 milliard de chiffres? S'il s'agit d'une base de données, alors 'sélectionner une valeur dans l'ordre de la table par valeur desc limite 100' suffirait parfaitement - il pourrait y avoir des différences de dialecte.

Est-ce un cas unique ou quelque chose qui va se répéter? Si répété, à quelle fréquence? S'il s'agit d'un fichier unique et que les données sont dans un fichier, alors 'cat srcfile | trier (options au besoin) | head -100 'vous permettra d'accomplir rapidement un travail productif pour lequel vous êtes payé, tandis que l'ordinateur gère cette corvée triviale.

Si cela se répète, nous vous conseillons de choisir une approche décente pour obtenir la réponse initiale et stocker/mettre en cache les résultats afin que vous puissiez en permanence afficher les 100 meilleurs résultats.

Enfin, il y a cette considération. Êtes-vous à la recherche d'un poste de débutant et d'un entretien avec un manager geek ou un futur collègue? Si tel est le cas, vous pouvez lancer toutes sortes d'approches décrivant les avantages et inconvénients techniques relatifs. Si vous recherchez un poste plus managérial, abordez-le comme un gestionnaire, préoccupé par les coûts de développement et de maintenance de la solution, et dites "merci beaucoup" et quittez-le si l'enquêteur souhaite se concentrer sur CS . Lui et vous n'auriez probablement pas beaucoup de potentiel d'avancement là-bas.

Meilleure chance pour la prochaine interview.

32
Fred Mitchell

Ma réaction immédiate à cela serait d'utiliser un tas, mais il y a moyen d'utiliser QuickSelect sans conserver toutes les valeurs d'entrée à la fois.

Créez un tableau de taille 200 et remplissez-le avec les 200 premières valeurs d'entrée. Exécutez QuickSelect et éliminez le plus bas 100, vous laissant ainsi 100 places libres. Lisez les 100 valeurs d’entrée suivantes et exécutez QuickSelect à nouveau. Continuez jusqu'à ce que vous ayez exécuté l'intégralité de l'entrée par lots de 100.

À la fin, vous avez les 100 meilleures valeurs. Pour N valeurs, vous avez exécuté QuickSelect environ N/100 fois. Chaque sélection rapide coûte environ 200 fois une constante, le coût total est donc 2N fois une constante. Cela me semble linéaire dans la taille de l'entrée, quelle que soit la taille du paramètre que je suis en train de câbler pour être 100 dans cette explication.

17
mcdowella

Vous pouvez utiliser Algorithme de sélection rapide pour trouver le numéro à l’index (par ordre) [milliard-101] , Puis effectuer une itération sur les nombres et trouver le nombre correspondant à ce nombre.

array={...the billion numbers...} 
result[100];

pivot=QuickSelect(array,billion-101);//O(N)

for(i=0;i<billion;i++)//O(N)
   if(array[i]>=pivot)
      result.add(array[i]);

Le temps de cet algorithme est: 2 X O(N) = O(N) (Performance moyenne du cas)

La deuxième option comme Thomas Jungblut suggère est:

Utilisez Heap si vous construisez le tas MAX, prenez O (N), puis les 100 premiers nombres max seront situés en haut du tas, il vous suffira de les sortir du tas (100 XO (Log (N )).

Cet algorithme Le temps est: O (N) + 100 X O(Log(N)) = O(N)

15
One Man Crew

Bien que l'autre solution quickselect ait été refusée, il reste que quickselect la trouvera plus rapidement que d'utiliser une file d'attente de taille 100. Quickselect a une durée d'exécution attendue de 2n + o (n), en termes de comparaisons. Une mise en œuvre très simple serait

array = input array of length n
r = Quickselect(array,n-100)
result = array of length 100
for(i = 1 to n)
  if(array[i]>r)
     add array[i] to result

Cela prendra 3n + o(n) comparaisons en moyenne. En outre, il est possible de gagner en efficacité grâce au fait que quickselect laisse les 100 éléments les plus volumineux du tableau dans les 100 emplacements les plus à droite. Donc, en fait, le temps d'exécution peut être amélioré à 2n + o (n). 

Le problème est qu’il s’agit de la durée de fonctionnement attendue, et non du pire des cas, mais en utilisant une stratégie de sélection de pivot décent (par exemple, choisissez 21 éléments au hasard et choisissez la médiane de ceux-ci comme pivot), le nombre de comparaisons peut alors être différent. garantie avec une probabilité élevée d’être au plus (2 + c) n pour une constante arbitrairement petite c. 

En fait, en utilisant une stratégie d'échantillonnage optimisée (par exemple, des échantillons de sqrt (n) au hasard et de choisir le 99e centile), le temps d'exécution peut être réduit à (1 + c) n + o(n) pour c arbitrairement petit (en supposant que K, le nombre d'éléments à sélectionner est o (n)).

D'autre part, l'utilisation d'une file d'attente de taille 100 nécessitera des comparaisons O(log(100)n), et la base de journalisation 2 sur 100 est approximativement égale à 6,6. 

Si nous pensons à ce problème dans le sens plus abstrait du choix des K éléments les plus grands parmi un tableau de taille N, où K = o (N) mais K et N vont tous deux à l'infini, le temps d'exécution de la version quickselect sera alors O(N) et la version de la file d'attente sera O (N log K), de sorte qu'en ce sens, la sélection rapide est également asymptotiquement supérieure.

Dans les commentaires, il a été mentionné que la solution de file d'attente allait s'exécuter dans le temps prévu N + K log N sur une entrée aléatoire. Bien entendu, l’hypothèse d’entrée aléatoire n’est jamais valable à moins que la question ne l’énonce explicitement. La solution de file d’attente pourrait être amenée à parcourir le tableau dans un ordre aléatoire, mais cela entraînerait le coût supplémentaire de N appels à un générateur de nombres aléatoires, ainsi que la permutation de l’ensemble du tableau d’entrée ou l’affectation d’un nouveau tableau de longueur N contenant les indices aléatoires. 

Si le problème ne vous permet pas de déplacer les éléments dans le tableau d'origine et que le coût de l'allocation de mémoire est élevé, la duplication du tableau n'est pas une option, mais la question est différente. Mais strictement en termes de temps d'exécution, c'est la meilleure solution.

10
mrip

prenez les 100 premiers numéros du milliard et triez-les. Maintenant, il suffit de parcourir le milliard, si le nombre source est supérieur au plus petit de 100, insérer dans l'ordre de tri. Vous obtenez quelque chose de beaucoup plus proche de O(n) que la taille de l’ensemble.

5
Samuel Thurston

Une solution très simple consisterait à parcourir le tableau 100 fois. Quel est O(n).

Chaque fois que vous extrayez le plus grand nombre (et que vous modifiez sa valeur sur la valeur minimale, afin que vous ne le voyiez pas à la prochaine itération, ou que vous gardiez une trace des index des réponses précédentes (en gardant une trace des index du tableau d'origine multiple du même nombre)). Après 100 itérations, vous avez les 100 plus grands nombres.

4
James Oravec

Deux options:

(1) Tas (prioritéQueue)

Conservez un min-tas de taille 100. Parcourez le tableau. Une fois que l'élément est plus petit que le premier élément du tas, remplacez-le. 

InSERT ELEMENT INTO HEAP: O(log100)
compare the first element: O(1)
There are n elements in the array, so the total would be O(nlog100), which is O(n)

(2) Modèle de carte-réduction. 

Ceci est très similaire à l'exemple de nombre de mots dans hadoop. Travail de la carte: compte la fréquence ou les heures de chaque élément. Réduire: obtenir le premier élément K. 

En général, je donnerais deux réponses au recruteur. Donnez-leur ce qu'ils veulent. Bien sûr, le codage de réduction de carte serait laborieux, car vous devez connaître tous les paramètres exacts. Pas de mal à le pratiquer. Bonne chance. 

4
Chris Su

On répondrait à cette question avec une complexité de N log (100) (au lieu de N log N) avec une seule ligne de code C++.

 std::vector<int> myvector = ...; // Define your 1 billion numbers. 
                                 // Assumed integer just for concreteness 
 std::partial_sort (myvector.begin(), myvector.begin()+100, myvector.end());

La réponse finale serait un vecteur où les 100 premiers éléments sont garantis comme étant les 100 plus grands nombres de votre tableau tandis que les éléments restants ne sont pas ordonnés.

C++ STL (bibliothèque standard) est assez pratique pour résoudre ce type de problèmes.

Note: Je ne dis pas que c'est la solution optimale, mais cela aurait sauvé votre interview.

1
Vivian Miranda

Inspiré par la réponse de @ron teller, voici un programme en C de base qui vous permet de faire ce que vous voulez.

#include <stdlib.h>
#include <stdio.h>

#define TOTAL_NUMBERS 1000000000
#define N_TOP_NUMBERS 100

int 
compare_function(const void *first, const void *second)
{
    int a = *((int *) first);
    int b = *((int *) second);
    if (a > b){
        return 1;
    }
    if (a < b){
        return -1;
    }
    return 0;
}

int 
main(int argc, char ** argv)
{
    if(argc != 2){
        printf("please supply a path to a binary file containing 1000000000"
               "integers of this machine's wordlength and endianness\n");
        exit(1);
    }
    FILE * f = fopen(argv[1], "r");
    if(!f){
        exit(1);
    }
    int top100[N_TOP_NUMBERS] = {0};
    int sorts = 0;
    for (int i = 0; i < TOTAL_NUMBERS; i++){
        int number;
        int ok;
        ok = fread(&number, sizeof(int), 1, f);
        if(!ok){
            printf("not enough numbers!\n");
            break;
        }
        if(number > top100[0]){
            sorts++;
            top100[0] = number;
            qsort(top100, N_TOP_NUMBERS, sizeof(int), compare_function);
        }

    }
    printf("%d sorts made\n"
    "the top 100 integers in %s are:\n",
    sorts, argv[1] );
    for (int i = 0; i < N_TOP_NUMBERS; i++){
        printf("%d\n", top100[i]);
    }
    fclose(f);
    exit(0);
}

Sur ma machine (Core i3 avec un SSD rapide), cela prend 25 secondes et 1724 tris . J'ai généré un fichier binaire avec dd if=/dev/urandom/ count=1000000000 bs=1 pour cette exécution.

Évidemment, la lecture de 4 octets à la fois pose problème pour les performances, mais sur disque, par exemple. Sur le plan positif, très peu de mémoire est nécessaire.

1
ldrumm

La solution simple consisterait à utiliser une file d'attente prioritaire, à ajouter les 100 premiers numéros à la file d'attente et à garder trace du plus petit numéro de la file d'attente, puis à parcourir le milliard restant, et à chaque fois nous en trouverons un qui soit plus grand que le plus grand Dans la file d'attente prioritaire, nous supprimons le plus petit nombre, ajoutons le nouveau numéro et, à nouveau, gardons une trace du plus petit numéro de la file d'attente. 

Si les nombres étaient dans un ordre aléatoire, cela fonctionnerait bien, car si nous parcourons un milliard de nombres aléatoires, il serait très rare que le prochain nombre figure parmi les 100 plus grands à ce jour. Mais les nombres pourraient ne pas être aléatoires. Si le tableau était déjà trié par ordre croissant, nous insérerions toujours un élément dans la file d'attente prioritaire. 

Nous choisissons donc d'abord 100 000 random dans le tableau. Pour éviter un accès aléatoire qui pourrait être lent, ajoutez 400 groupes aléatoires de 250 numéros consécutifs. Avec cette sélection aléatoire, nous pouvons être pratiquement sûrs que très peu des nombres restants font partie des cent premiers, de sorte que le temps d'exécution sera très proche de celui d'une simple boucle comparant un milliard de nombres à une valeur maximale. 

1
gnasher729

Il est préférable de rechercher le top 100 sur un milliard de nombres avec min-heap of 100 elements.

Amorcez d'abord le min-tas avec les 100 premiers nombres rencontrés. min-heap stockera le plus petit des 100 premiers nombres à la racine (en haut).

Au fur et à mesure que vous avancez, comparez les autres chiffres à la racine (la plus petite des 100).

Si le nouveau nombre rencontré est plus grand que la racine de min-heap, remplacez la racine par ce nombre, sinon ignorez-le.

Dans le cadre de l'insertion du nouveau nombre dans min-tas, le plus petit nombre du tas va atteindre le sommet (racine).

Une fois que nous aurons parcouru tous les chiffres, nous aurons les 100 plus gros nombres du tas. 

1
imsaar
 Although in this question we should search for top 100 numbers, I will 
 generalize things and write x. Still, I will treat x as constant value.

Algorithme Les plus grands éléments x de n:

J'appellerai la valeur de retourLISTE. C'est un ensemble de x éléments (à mon avis ça devrait être une liste chaînée)

  • Les premiers x éléments sont pris dans le pool "comme ils viennent" et triés dans la liste (cela se fait en temps constant car x est traité comme une constante - O (x log (x))
  • Pour chaque élément suivant, nous vérifions s'il est plus grand que le plus petit élément de la liste et si nous supprimons le plus petit et insérons l'élément en cours dans la liste. Comme cette liste est ordonnée, chaque élément devrait trouver sa place en temps logarithmique (recherche binaire) et, comme il est ordonné, l'insertion d'une liste ne pose pas de problème. Chaque étape est également effectuée en temps constant (O(log(x)) temps). 

Alors, quel est le pire scénario?

x log (x) + (n-x) (log (x) +1) = nlog (x) + n - x

C'est donc O(n) heure du pire des cas. Le +1 est la vérification si le nombre est supérieur au plus petit dans la liste. Le temps prévu pour le cas moyen dépendra de la distribution mathématique de ces n éléments. 

Améliorations possibles

Cet algorithme peut être légèrement amélioré pour le pire des cas mais IMHO (je ne peux pas prouver cette affirmation) qui dégradera le comportement moyen. Le comportement asymptotique sera le même. 

L’amélioration de cet algorithme consistera à ne pas vérifier si l’élément est plus grand que le plus petit. Nous essaierons de l'insérer pour chaque élément et nous l'ignorerons s'il est plus petit que le plus petit. Bien que cela semble absurde si nous ne considérons que le pire des cas, nous aurons 

x log (x) + (n-x) log (x) = nlog (x)

opérations. 

Pour ce cas d'utilisation, je ne vois pas d'autres améliorations. Cependant, vous devez vous demander si je dois le faire plus que log (n) fois et pour différents x-es. Nous allons évidemment trier ce tableau dans O (n log (n)) et prendre notre élément x chaque fois que nous en avons besoin.

1
Rouz

La solution la plus simple est d’analyser le grand tableau de milliards de chiffres et de conserver les 100 valeurs les plus grandes trouvées jusqu’à présent dans un petit tampon de tableau sans tri et de garder la plus petite valeur de ce tampon. Tout d’abord, j’imaginais que cette méthode avait été proposée par fordprefect mais, dans un commentaire, il a supposé qu’il supposait que la structure de données à 100 chiffres était implémentée sous forme de tas. Chaque fois qu'un nouveau nombre est trouvé qui est plus grand que le minimum dans le tampon est écrasé par la nouvelle valeur trouvée et le tampon est à nouveau recherché pour le minimum actuel. Si les nombres en milliards de nombres sont distribués de manière aléatoire la plupart du temps, la valeur du grand tableau est comparée au minimum du petit tableau et est ignorée. Seulement pour une très petite fraction du nombre, la valeur doit être insérée dans le petit tableau. Ainsi, la différence de manipulation de la structure de données contenant les petits nombres peut être négligée. Pour un petit nombre d'éléments, il est difficile de déterminer si l'utilisation d'une file d'attente prioritaire est réellement plus rapide que d'utiliser mon approche naïve.

Je veux estimer le nombre d'insertions dans le petit tampon de tableau de 100 éléments lorsque le tableau de 10 ^ 9 éléments est analysé. Le programme analyse les 1000 premiers éléments de ce grand tableau et doit insérer au maximum 1000 éléments dans la mémoire tampon. La mémoire tampon contient 100 éléments sur 1000 analysés, soit 0,1 élément analysé. Nous supposons donc que la probabilité qu'une valeur du grand tableau soit supérieure au minimum actuel du tampon est d'environ 0,1. Un tel élément doit être inséré dans le tampon. Le programme analyse maintenant les 10 ^ 4 prochains éléments du grand tableau. Parce que le minimum de la mémoire tampon augmentera chaque fois qu'un nouvel élément est inséré. Nous avons estimé que le rapport des éléments supérieurs à notre minimum actuel est d'environ 0,1; il y a donc 0,1 * 10 ^ 4 = 1000 éléments à insérer. En réalité, le nombre d'éléments insérés dans le tampon sera plus petit. Après l'analyse de ces 10 ^ 4 éléments, une fraction des nombres dans la mémoire tampon correspondra à environ 0,01 des éléments analysés jusqu'à présent. Ainsi, lors de la numérisation des 10 ^ 5 numéros suivants, nous supposons que pas plus de 0,01 * 10 ^ 5 = 1000 ne seront insérés dans la mémoire tampon. En poursuivant cette argumentation, nous avons inséré environ 7 000 valeurs après avoir numérisé 1000 + 10 ^ 4 + 10 ^ 5 + ... + 10 ^ 9 ~ 10 ^ 9 éléments du grand tableau . Ainsi, lors de l'analyse d'un tableau avec 10 ^ 9 les éléments de taille aléatoire ne doivent pas contenir plus de 10 ^ 4 (= 7 000 arrondis) dans le tampon. Après chaque insertion dans le tampon, le nouveau minimum doit être trouvé. Si le tampon est un tableau simple, nous avons besoin de 100 comparaisons pour trouver le nouveau minimum. Si le tampon est une autre structure de données (comme un tas), nous avons besoin d'au moins 1 comparaison pour trouver le minimum. Pour comparer les éléments du grand tableau, nous avons besoin de 10 ^ 9 comparaisons. En résumé, nous avons besoin d'environ 10 ^ 9 + 100 * 10 ^ 4 = 1,001 * 10 ^ 9 comparaisons lors de l'utilisation d'un tableau comme tampon et d'au moins 1 000 * 10 ^ 9 comparaisons lors de l'utilisation d'un autre type de structure de données (comme un tas). . L'utilisation d'un segment de mémoire n'apporte donc qu'un gain de 0,1% si les performances sont déterminées par le nombre de comparaisons . Mais quelle est la différence de temps d'exécution entre l'insertion d'un élément dans un segment de mémoire de 100 éléments et le remplacement d'un élément dans un tableau de 100 éléments et trouver son nouveau minimum? 

  • Au niveau théorique: combien de comparaisons sont nécessaires pour insérer dans un tas. Je sais que c'est O(log(n)) mais quelle est la taille du facteur constant? je 

  • Au niveau de la machine: quel est l’impact de la mise en cache et de la prédiction de branche sur le temps d’exécution d’une insertion de tas et d’une recherche linéaire dans un tableau.

  • Au niveau de la mise en œuvre: quels coûts supplémentaires sont cachés dans une structure de données de tas fournie par une bibliothèque ou un compilateur?

Je pense que ce sont certaines des questions auxquelles il faut répondre avant de pouvoir essayer d’estimer la différence réelle entre les performances d’un tas de 100 éléments ou d’un tableau de 100 éléments. Il serait donc logique de faire une expérience et de mesurer la performance réelle.

1
miracle173
  1. Utilisez le nième élément pour obtenir le 100ème élément O (n)
  2. Répéter la deuxième fois mais une fois seulement et sortir chaque élément supérieur à cet élément spécifique.

S'il vous plaît noter esp. la deuxième étape peut être facile à calculer en parallèle! Et ce sera également efficace lorsque vous aurez besoin d'un million d'éléments les plus importants.

0
math
Time ~ O(100 * N)
Space ~ O(100 + N)
  1. Créer une liste vide de 100 emplacements vides

  2. Pour chaque numéro dans la liste d'entrée:

    • Si le nombre est inférieur au premier, ignorez

    • Sinon, remplacez-le par ce numéro

    • Poussez ensuite le numéro dans le swap adjacent; jusqu'à ce qu'il soit plus petit que le suivant

  3. Renvoyer la liste


Remarque: si log(input-list.size) + c < 100, la méthode optimale consiste à trier la liste d'entrées, puis à diviser les 100 premiers éléments.

0
Khaled.K

J'ai écrit une solution simple en Python au cas où quelqu'un serait intéressé. Il utilise le module bisect et une liste de retours temporaires qu'il conserve triés. Ceci est similaire à une implémentation de file d'attente prioritaire.

import bisect

def kLargest(A, k):
    '''returns list of k largest integers in A'''
    ret = []
    for i, a in enumerate(A):
        # For first k elements, simply construct sorted temp list
        # It is treated similarly to a priority queue
        if i < k:
            bisect.insort(ret, a) # properly inserts a into sorted list ret
        # Iterate over rest of array
        # Replace and update return array when more optimal element is found
        else:
            if a > ret[0]:
                del ret[0] # pop min element off queue
                bisect.insort(ret, a) # properly inserts a into sorted list ret
    return ret

Utilisation avec 100 000 000 d'éléments et entrée dans le cas le plus défavorable qui est une liste triée:

>>> from so import kLargest
>>> kLargest(range(100000000), 100)
[99999900, 99999901, 99999902, 99999903, 99999904, 99999905, 99999906, 99999907,
 99999908, 99999909, 99999910, 99999911, 99999912, 99999913, 99999914, 99999915,
 99999916, 99999917, 99999918, 99999919, 99999920, 99999921, 99999922, 99999923,
 99999924, 99999925, 99999926, 99999927, 99999928, 99999929, 99999930, 99999931,
 99999932, 99999933, 99999934, 99999935, 99999936, 99999937, 99999938, 99999939,
 99999940, 99999941, 99999942, 99999943, 99999944, 99999945, 99999946, 99999947,
 99999948, 99999949, 99999950, 99999951, 99999952, 99999953, 99999954, 99999955,
 99999956, 99999957, 99999958, 99999959, 99999960, 99999961, 99999962, 99999963,
 99999964, 99999965, 99999966, 99999967, 99999968, 99999969, 99999970, 99999971,
 99999972, 99999973, 99999974, 99999975, 99999976, 99999977, 99999978, 99999979,
 99999980, 99999981, 99999982, 99999983, 99999984, 99999985, 99999986, 99999987,
 99999988, 99999989, 99999990, 99999991, 99999992, 99999993, 99999994, 99999995,
 99999996, 99999997, 99999998, 99999999]

Cela a pris environ 40 secondes pour calculer cela pour 100 000 000 d'éléments, alors j'ai peur de le faire pour un milliard. Pour être honnête cependant, je lui fournissais la pire des entrées (ironiquement un tableau déjà trié).

0
Shashank

Prenez d’abord 1000 éléments et ajoutez-les dans un maximum. Maintenant, sortez les 100 premiers éléments maximum et stockez-les quelque part. Maintenant, choisissez les 900 prochains éléments du fichier et ajoutez-les dans le tas avec les 100 derniers éléments les plus hauts. 

Continuez à répéter ce processus de sélection de 100 éléments du tas et d’ajout de 900 éléments du fichier.

La sélection finale de 100 éléments nous donnera le maximum de 100 éléments parmi un milliard de nombres.

0
Juvenik

j'ai fait mon propre code, pas sûr si c'est ce que "l'interviewer" il cherche

private static final int MAX=100;
 PriorityQueue<Integer> queue = new PriorityQueue<>(MAX);
        queue.add(array[0]);
        for (int i=1;i<array.length;i++)
        {

            if(queue.peek()<array[i])
            {
                if(queue.size() >=MAX)
                {
                    queue.poll();
                }
                queue.add(array[i]);

            }

        }
0
Javier

Un autre algorithme O(n) -

L'algorithme trouve le plus grand 100 par élimination

considérer tous les millions de nombres dans leur représentation binaire. Commencez par le bit le plus significatif. Trouver si le MSB est égal à 1 peut être réalisé par une opération booléenne multipliée par un nombre approprié. S'il y a plus de 100 1 dans ces millions, éliminez les autres nombres avec des zéros. Maintenant, parmi les nombres restants, passez au prochain bit le plus significatif. comptez le nombre de nombres restants après l'élimination et continuez tant que ce nombre est supérieur à 100. 

L’opération booléenne majeure peut être réalisée parallèlement sur des GPU

0
Mystic monk

C’est une question de Google ou de certains autres géants de l’industrie.Le code suivant est peut-être la bonne réponse attendue par votre intervieweur ... Le coût en temps et en espace dépend du nombre maximum dans le tableau en entrée.Pour la saisie en tableau sur 32 bits int , Le coût d’espace maximum est de 4 * 125 Mo, le temps est de 5 * milliards.

public class TopNumber {
    public static void main(String[] args) {
        final int input[] = {2389,8922,3382,6982,5231,8934
                            ,4322,7922,6892,5224,4829,3829
                            ,6892,6872,4682,6723,8923,3492};
        //One int(4 bytes) hold 32 = 2^5 value,
        //About 4 * 125M Bytes
        //int sort[] = new int[1 << (32 - 5)];
        //Allocate small array for local test
        int sort[] = new int[1000];
        //Set all bit to 0
        for(int index = 0; index < sort.length; index++){
            sort[index] = 0;
        }
        for(int number : input){
            sort[number >>> 5] |= (1 << (number % 32));
        }
        int topNum = 0;
        outer:
        for(int index = sort.length - 1; index >= 0; index--){
            if(0 != sort[index]){
                for(int bit = 31; bit >= 0; bit--){
                    if(0 != (sort[index] & (1 << bit))){
                        System.out.println((index << 5) + bit);
                        topNum++;
                        if(topNum >= 3){
                            break outer;
                        }
                    }
                }
            }
        }
    }
}
0
Su Xiang

Je voudrais savoir qui a le temps de mettre un milliard de chiffres dans un tableau et de le renvoyer. Doit travailler pour le gouvernement. Au moins si vous aviez une liste chaînée, vous pourriez insérer un nombre au milieu sans déplacer un demi-milliard de dollars pour faire de la place. Encore mieux, un arbre permet une recherche binaire. Chaque comparaison élimine la moitié de votre total. Un algorithme de hachage vous permettrait de renseigner la structure de données comme un damier, mais pas très efficace pour les données fragmentées. Comme il est préférable d’avoir un tableau de solutions de 100 nombres entiers et de garder trace du nombre le plus bas de votre tableau de solutions, vous pourrez le remplacer lorsque vous rencontrerez un nombre plus élevé dans le tableau d’origine. Vous devrez examiner chaque élément du tableau d'origine en supposant qu'il ne soit pas trié pour commencer.

0

Améliorations possibles.

Si le fichier contient 1 milliard de dollars, sa lecture pourrait être vraiment longue ...

Pour améliorer ce travail, vous pouvez:

  • Divisez le fichier en n parties, créez n threads, faites en sorte que n threads recherchent chacun les 100 plus gros nombres de leur partie du fichier (à l'aide de la file d'attente prioritaire), et obtenez enfin les 100 plus gros nombres de toutes les sorties.
  • Utilisez un cluster pour effectuer une telle tâche, avec une solution telle que hadoop. Ici, vous pouvez diviser encore plus le fichier et obtenir une sortie plus rapide pour un fichier de 1 milliard (ou 10 ^ 12).
0
Maxime B.

Vous pouvez le faire dans O(n) time. Parcourez simplement la liste et gardez une trace des 100 plus gros nombres que vous avez vus à un moment donné et de la valeur minimale de ce groupe. Lorsque vous trouvez un nouveau nombre plus grand que le plus petit de votre dix, remplacez-le et mettez à jour votre nouvelle valeur minimale de 100 (cela peut prendre un temps constant de 100 pour le déterminer à chaque fois que vous le faites, mais cela n’affecte pas l’analyse globale ).

0
James Oravec

Je vois beaucoup de discussions O(N), alors je propose quelque chose de différent juste pour l'exercice de réflexion.

Existe-t-il des informations connues sur la nature de ces chiffres? Si c'est de nature aléatoire, n'allez pas plus loin et examinez les autres réponses. Vous n'obtiendrez pas de meilleurs résultats qu'eux.

Toutefois! Vérifiez si le mécanisme de remplissage de liste a renseigné cette liste dans un ordre particulier. Sont-ils dans un schéma bien défini où vous pouvez savoir avec certitude que la plus grande magnitude des nombres sera trouvée dans une certaine région de la liste ou à un certain intervalle? Il peut y avoir un motif à cela. Si tel est le cas, par exemple, s’ils ont la garantie d’être dans une sorte de distribution normale avec la bosse caractéristique au milieu, ont toujours des tendances à la hausse répétitives parmi des sous-ensembles définis, ont un pic prolongé à un moment donné T au milieu des données Définissez comme une incidence de délit d'initié ou une défaillance d'équipement, ou peut-être simplement un "pic" tous les Nièmes, comme dans l'analyse des forces après une catastrophe, vous pouvez réduire considérablement le nombre d'enregistrements à vérifier.

De toute façon, il y a matière à réflexion. Cela vous aidera peut-être à donner une réponse réfléchie aux futurs intervieweurs. Je sais que je serais impressionné si quelqu'un me posait une telle question en réponse à un problème comme celui-ci - cela me dirait qu'ils pensent à l'optimisation. Il suffit de reconnaître qu’il n’est pas toujours possible d’optimiser.

0
djdanlib

La complexité est O(N) 

Commencez par créer un tableau de 100 ints initialiaze le premier élément de ce tableau en tant que premier élément des N valeurs, Gardez une trace de l'index de l'élément en cours avec une autre variable, appelez-le CurrentBig.

Itérer si les N valeurs 

if N[i] > M[CurrentBig] {

M[CurrentBig]=N[i]; ( overwrite the current value with the newly found larger number)

CurrentBig++;      ( go to the next position in the M array)

CurrentBig %= 100; ( modulo arithmetic saves you from using lists/hashes etc.)

M[CurrentBig]=N[i];    ( pick up the current value again to use it for the next Iteration of the N array)

} 

lorsque vous avez terminé, imprimez le tableau M à partir de CurrentBig 100 fois modulo 100 :-) Pour l'étudiant: assurez-vous que la dernière ligne du code ne remplace pas les données valides juste avant la sortie du code

0