On vous donne un fichier qui contient tous les nombres possibles sur une architecture 32 bits. Il manque 4 numéros dans ce fichier. Trouvez les 4 numéros manquants

22

Il s'agit d'une question d'entrevue que j'ai rencontrée plusieurs fois, et je ne sais vraiment pas comment la résoudre étant donné que quatre numéros sont manquants. Je connais les algorithmes pour trouver un ou deux nombres manquants, mais je ne vois pas de moyen de généraliser l'un d'eux à quatre.

Tsutarja47
la source

Réponses:

19

Que ce soit pour un entretien ou un travail réel, votre première priorité doit être une solution de travail qui a du sens pour vous . Cela habituellement signifie que vous devez offrir la première solution , vous pouvez penser à qui est simple et facile pour vous d'expliquer.

Pour moi, cela signifie trier les chiffres et rechercher les lacunes. Mais je travaille sur des systèmes d'entreprise et des applications Web. Je ne joue pas avec les morceaux et je ne veux pas que mon équipe le fasse!

Si vous interviewez pour un travail de bas niveau, plus proche du métal, le "tri" sera probablement rencontré avec des regards vides. Ils veulent que vous réfléchissiez à l'aise sur les bits et ainsi de suite. Votre première réponse devrait être: «Oh, j'utiliserais un bitmap». (Ou tableau de bits ou ensemble de bits.)

Et puis, de toute façon - même si vous donnez une «mauvaise» solution, si votre intervieweur (ou patron!) Insiste , vous pouvez suggérer des améliorations ou des alternatives, en vous concentrant sur le domaine de préoccupation spécifique du manager.

  • RAM sévèrement limitée? Moins de 512 Mo?
    Triez-le sur place. Vous pouvez utiliser une quantité de RAM principalement arbitraire pour optimiser et / ou mettre en mémoire tampon les blocs triés.
  • Temps limité?
    Utilisez cette RAM! Le tri est déjà O(n*log(n)). (Ou O (n) pour un tri par nombre entier!)
  • Maintenabilité?
    Quoi de plus simple que de trier?!
  • Ne démontre pas la connaissance des indicateurs / champs de bits? ( BitSet/ BitMap/ BitArray)
    Bon d'accord ... allez-y et utilisez a BitArraypour signaler les "nombres trouvés". Et puis scannez pour 0.
  • Une complexité "en temps réel" prévisible ?
    Utilisez la solution bitmap. C'est une seule passe sur le fichier et une autre passe sur leBitArray/BitSet(pour trouver le0). VoilàO(n), je pense!

Ou peu importe.

Répondez aux préoccupations que vous avez réellement. Résolvez d'abord le problème, en utilisant des solutions naïves si nécessaire. Ne perdez pas le temps de tout le monde à répondre à des préoccupations qui n'existent pas encore.

svidgen
la source
Je ne suis pas sûr de la faisabilité de trier 4 milliards de numéros avec une approche naïve, sans parler du disque. Je ne l'ai jamais essayé.
Eiko
1
@Eiko Eh bien ... et encore une fois, le point principal est ... ne compliquez pas trop les choses. La première étape consiste simplement à résoudre le problème, de toutes les manières possibles pour le résoudre, même s'il est naïf. Je ne peux même pas souligner le niveau de frustration de votre futur employeur si vous passez du temps à répéter pour vous assurer que vous disposez de la "bonne" solution lorsque l'entreprise a juste besoin d' une solution. Prouvez que vous pouvez faire les deux! Prouvez que vous pouvez résoudre les problèmes rapidement, puis identifiez les problèmes potentiels qui méritent d'être refactorisés et / ou optimisés si nécessaire .
svidgen
1
@Ewan "Parce que vous avez posé la question lors de l'entretien" n'est pas la même chose que "Il y a une réponse spécifique que chaque manager recherche." ... Je ne me soucierais certainement pas de la solution que vous m'avez donnée, tant que vous avez démontré une capacité à résoudre le problème et à ne pas vous laisser résoudre par des problèmes que je ne vous ai jamais donnés!
svidgen
1
Vous manquez le point. Cette question et ses variations se retrouvent dans les livres d'énigmes de programmation et les questions d'entrevue. Il n'a pas été inventé par la personne qui pose la question. le truc 32 bits est censé le rendre impossible en gardant une trace des numéros ou du tri. Ses seuls ordinateurs sont devenus plus rapides / plus gros depuis qu'il a été écrit.
Ewan
1
@Ewan: vous supposez toujours que votre instance de la question a les mêmes contraintes que les PO. L'OP n'a pas dit que son algorithme doit fonctionner sur une machine 32 bits, il n'a même pas dit qu'il doit fonctionner sur un ordinateur du tout, un algorithme conceptuel pourrait convenir. Il n'indique pas non plus ce que signifie "tous les nombres possibles", car des mathématiques entières de taille arbitraire sont possibles sur des microcontrôleurs même à 8 bits. Beaucoup d'hypothèses que vous faites pour donner des déclarations absolues.
whatsisname
19

Puisqu'il s'agit d'un fichier, je suppose que vous êtes autorisé à effectuer plusieurs passes. Créez d'abord un tableau de 256 compteurs, parcourez le fichier et pour chaque numéro incrémentez le compteur indexé comme premier octet du numéro. Lorsque vous avez terminé, la plupart des compteurs doivent être à 2 ^ 24, mais 1 à 4 compteurs doivent avoir des valeurs inférieures. Chacun de ces indices représente un premier octet d'un des nombres manquants (s'il y en a moins de 4, c'est parce que plusieurs nombres manquants partagent le même premier octet).

Pour chacun de ces indices, créez un autre tableau de 256 compteurs et effectuez une deuxième passe sur le fichier. Cette fois, si le premier octet est l'une des valeurs précédentes, incrémentez un compteur dans son tableau en fonction du deuxième octet. Lorsque vous avez terminé, recherchez à nouveau les compteurs inférieurs à 2 ^ 16, et vous aurez le deuxième octet des nombres manquants, chacun correspondant à son premier octet.

Recommencez pour le troisième octet (notez que vous avez besoin d'un maximum de 4 tableaux dans chaque passage, même si chaque octet peut être suivi par jusqu'à 4 octets différents) et pour le quatrième octet, et vous avez trouvé tous les nombres manquants.

Complexité temporelle - Complexité O(n * log n)
spatiale - constante !

Modifier:

En fait, je considérais le n=2^32comme le paramètre, mais le nombre de nombres manquants k=4est aussi un paramètre. En supposant que k<<ncela signifie que la complexité de l'espace est O(k).

Mise à jour:

Juste pour le plaisir (et parce que j'essaie actuellement d'apprendre Rust), je l'ai implémenté dans Rust: https://gist.github.com/idanarye/90a925ebb2ea57de18f03f570f70ea1f . J'ai choisi d'avoir une représentation textuelle, car tout le monde va l'exécuter avec ~ 2 ^ 32 chiffres ...

Idan Arye
la source
La conservation de tous les nombres en mémoire (pour plusieurs passes) nécessite 4 octets * 2 ^ 32 de mémoire, ce qui pousse les choses. Il est donc plus probable que vous fassiez toutes les E / S quatre fois. Mais l'autre mémoire utilisée est extrêmement petite, donc excellent travail là-bas.
user949300
1
@ user949300 Je suppose que cette solution lit le fichier morceau par morceau plutôt que de charger le tout en mémoire à la fois
Richard Tingle
"la plupart des compteurs doivent être à 2 ^ 24, mais 1 à 4 compteurs doivent avoir des valeurs plus faibles" - faux: peut être 0, toutes les valeurs manquantes partageant le premier octet (les deuxième et troisième sont également possibles). Suivant: combien de tableaux créez-vous lors de la deuxième passe? 256, 1 à 4 fois 256, 256 fois 256? Et puis au troisième et quatrième passage?
Bernhard Hiller
3
@BernhardHiller Le fichier contient tous les nombres possibles dans l'espace 32 bits, sauf pour 4 nombres distincts. En tant que tel, tous les premiers octets se produiront, seulement 1 à 4 d'entre eux auront moins de hits.
Lasse V. Karlsen
@ LasseV.Karlsen merci, maintenant je comprends l'algorithme.
Bernhard Hiller du
6

S'il s'agissait de Java, vous pourriez utiliser un BitSet. Eh bien, deux d'entre eux, car ils ne peuvent pas tout à fait contenir tous les nombres 32 bits. Code squelettique, peut-être buggé:

BitSet bitsetForPositives = new Bitset(2^31);  // obviously not 2^31 but you get the idea
BitSet bitsetForNegatives = new Bitset(2^31);

for (int value: valuesTheyPassInSomehow) {
  if ((value & 0x80000000) == 0)
     bitsetForPositives.set(value );
  else
     bitsetForNegatives.set(value & ~0x80000000);
}

Utilisez ensuite BitSet.nextClearBit()pour trouver qui manque.

Note ajoutée bien plus tard:

Notez qu'avec cet algorithme, il est assez facile d'exécuter la partie chronophage en parallèle . Supposons que le fichier d'origine ait été divisé en quatre parties à peu près égales. Allouez 4 paires de BitSets (2 Go, toujours gérables).

  1. Avoir quatre threads, en parallèle, chacun traite un fichier dans sa propre paire de BitSets.
  2. Une fois terminé, revenez à un seul thread, ou aux Bitsets (temps trivial), puis appelez nextClearBit quatre fois (également temps assez trivial).

Je m'attendrais à ce que les E / S soient toujours l'étape de limitation de débit, mais si par magie tous les nombres étaient en mémoire, vous pourriez vraiment accélérer les choses.

user949300
la source
3
@Idan Ayre. Cette solution nécessite peu de code, donc moins de risques d'erreurs de codage. Je suis jolie c'est le moment O (n). Il ne suppose pas / ne nécessite pas non plus plusieurs passages dans un énorme fichier, il utilise donc moins d'espace qu'un algorithme nécessitant plusieurs passes. Veuillez préciser ce que vous entendez par «Oh mon cher».
user949300
2
Ne gère pas Integer.MIN_VALUEcorrectement. Vous pouvez masquer le bit de signe au lieu de le nier pour le réparer.
CodesInChaos
1
Cette approche naïve nécessite 2 ^ 32 bits = 4 Gib = 512 MiB pour les bitsets, ce qui est une quantité modeste de RAM, même sur un système 32 bits.
CodesInChaos
Si la langue de choix n'a pas de jeux de bits intégrés, émulez-les en utilisant un tableau d'octets. Par exemple en C #:bool GetBit(byte[] byteArray, uint index) { var byteIndex = index >> 3; var bitInByte = index & 7; return (byteArray[byteIndex] >> bitInByte) & 1 != 0; }
CodesInChaos
1
@JoulinRouge (et JacquesB) Donc, nous convenons que cela est linéaire dans le temps, utilise une RAM modeste (1/2 Gig) et ne prend qu'une seule passe d'E / S. Travaille pour moi.
user949300
5

Cette question peut être résolue en utilisant un tableau de bits (vrai / faux). Cela devrait être la structure la plus efficace pour conserver les réponses de tous les nombres en utilisant l'index du tableau pour savoir si ce nombre particulier a été trouvé.

C #

var bArray = new BitArray(Int32.MaxValue);

//Assume the file has 1 number per line
using (StreamReader sr = File.OpenText(fileName))
{
        string s = String.Empty;
        while ((s = sr.ReadLine()) != null)
        {
            var n = int32.Parse(s);
            bArray[n] = true;
        }
}

Ensuite, parcourez simplement le tableau et pour les valeurs qui sont toujours fausses, elles ne sont pas dans le fichier.

Vous pouvez diviser le fichier en morceaux plus petits, mais j'ai pu allouer un tableau complet de taille maximale int32 (2147483647) sur mon ordinateur portable de 16,0 Go exécutant Windows 7 (64 bits).

Même si je n'exécutais pas 64 bits, je pouvais allouer des tableaux de bits plus petits. Je pré-traiterais le fichier en créant un ensemble de fichiers plus petits, chacun avec une plage de [0-64000] [64001-128000], etc. des nombres qui conviendraient aux ressources environnementales disponibles. Parcourez le gros fichier et écrivez chaque numéro dans son fichier de jeu correspondant. Ensuite, traitez chaque fichier plus petit. Cela prendrait un peu plus de temps en raison de l'étape de prétraitement, mais cela contournerait les limitations de ressources s'il y avait des ressources limitées.

Jon Raynor
la source
Cela ne semble pas gérer les nombres négatifs. (Ou des entiers non signés avec le bit le plus élevé si c'est l'entrée.) La mémoire du jeu de bits ne devrait pas être un problème, même sur la plupart des systèmes 32 bits.
user949300
@ user949300 - Correct. Je n'ai pas remarqué de grande consommation de mémoire lorsque le tableau a été initialisé avec toutes les fausses valeurs. Il faudrait un BitArray secondaire pour les nombres négatifs. Peut-être que bArrayNegative = new BitArrary (Int32.MaxValue). Lorsque le nombre a été lu, il peut être vérifié pour positif ou négatif, puis placé dans le tableau de bits approprié. Merci pour les commentaires.
Jon Raynor
2

Comme il s'agit d'une question d'entrevue, je montrerais à l'intervieweur une certaine compréhension des contraintes. Que signifie alors "tous les nombres possibles"? Est-ce vraiment 0 ... 2 <(32-1) comme tout le monde le devine? Les architectures 32 bits habituelles peuvent fonctionner avec bien plus que des nombres 32 bits. C'est juste une question de représentation, évidemment.

Doit-il être résolu sur un système 32 bits, ou est-ce plutôt une partie de la restriction sur les nombres? Par exemple, un système 32 bits typique ne pourra pas charger le fichier dans la RAM à la fois. Je mentionnerais également qu'un système 32 bits ne sera souvent pas en mesure d'avoir un fichier contenant tous les nombres en raison de la limitation de la taille du fichier. Eh bien, à moins qu'il n'ait un codage intelligent, comme "Tous les nombres sauf ces quatre", auquel cas le problème est résolu trivialement.

Mais si vous voulez vraiment comprendre la question comme "étant donné un fichier avec tous les nombres de 0 ... 2 ^ (32-1) sauf quelques-uns, donnez-moi ceux qui manquent" (et c'est un gros si !), Alors il existe de nombreuses façons de le résoudre.

Anecdotique mais non réalisable: pour chaque numéro possible, scannez le fichier et voyez s'il s'y trouve.

Avec 512 Mo de RAM et fichier à passage unique: marquez chaque numéro (= bit défini à cet index) lu dans le fichier, puis passez une fois la RAM et voyez ceux qui manquent.

Eiko
la source
1
Quelques bonnes questions, mais que le système 32 bits représente des ints, des flottants ou des huzziwigs, il ne peut toujours représenter que 2 ^ 32 valeurs en 32 bits. Si la question est "oh ouais, nous autorisons les ultra-longs 128 bits", alors la "contrainte" d'architecture 32 bits dans la question est délibérément trompeuse. Pourtant, une grande question à poser à l'intervieweur, car de nombreuses spécifications sont trompeuses ou mal écrites. Votre solution réelle est un BitSet comme le mien.
user949300
@ user949300 Oui - et il est impossible de savoir ce que l'intervieweur recherche. Si la dernière personne qu'ils ont embauchée était un gars de "piratage de pile avant de penser", votre réponse devrait être différente que si c'était un "n'a absolument aucune idée de l'architecture" ou "jouer au jeu d'optimisation". :) J'ai déjà travaillé avec de gros bitsets (mais pas en Java), donc ils me viennent naturellement à l'esprit. Et peut également être adopté pour réduire la mémoire si nécessaire (regroupement). Les bitsets résolvent également le "problème de tri" dans les commentaires ci-dessus en temps linéaire avec 512 Mo de RAM.
Eiko
0

Une approche facile à retenir et à articuler dans une interview serait d'utiliser le fait que si vous regardez tous les nombres en N bits, chaque bit sera défini dans exactement la moitié de ces valeurs et non dans l'autre moitié .

Si vous parcourez toutes les valeurs du fichier et conservez 32 décomptes à la fin, vous vous retrouverez avec 32 valeurs exactement (2 ^ 32/2) ou légèrement inférieures à cette valeur. La différence entre ce maximum (2 ^ 32/2) et le total vous donne le total des bits définis dans chaque position des valeurs manquantes.

Une fois que vous avez cela, vous pouvez déterminer tous les ensembles possibles de 4 valeurs qui pourraient donner ces totaux. Cela étant, vous pouvez ensuite parcourir les valeurs du fichier en vérifiant à nouveau les valeurs qui font partie de ces combinaisons. Lorsque vous en trouvez une, les combinaisons contenant cette valeur sont éliminées en tant que possibilités. Une fois qu'il ne vous reste qu'une seule combinaison possible, vous avez la réponse.

Par exemple, en utilisant un quartet, vous avez les valeurs suivantes:

1010
0110
1111
0111
1101
1001
0100
0101
0001
1011
1100
1110

Le nombre total de bits définis dans chaque position est:

7867

En soustrayant ceux de 8 (4 ^ 2/2), nous obtenons:

1021

Ce qui signifie qu'il existe les ensembles possibles suivants de 4 valeurs:

1000
0000
0011
0010

1010
0001
0010
0000

(pardonne-moi si j'en ai manqué, je fais juste ça de vue)

Et puis en regardant à nouveau les numéros originaux, nous trouvons 1010 tout de suite, ce qui signifie que le premier ensemble était la réponse.

JimmyJames
la source
mais vous devez trouver 4 chiffres, pas un
freedev
@freedev Vous avez raison. Voilà ce que ça fait. Un ensemble de quatre nombres est quatre nombres ... dans un ensemble.
JimmyJames
Intéressant, mais vous passez sous silence determine all the possible sets of 4 values that could give those totals. Je pense vraiment que c'est une partie importante de la solution qui manque dans votre réponse. Il peut également affecter la complexité du temps et de l'espace.
Allon Guralnek
@AllonGuralnek Vous avez raison. J'ai passé un peu de temps à résoudre ce problème et j'avais largement sous-estimé le nombre d'ensembles de 4 chiffres qui correspondraient au même nombre dans le pire des cas. Je pense que c'est une idée récupérable, mais c'est un peu plus compliqué que je ne l'ai exposé ici. Je mettrai à jour avec des détails plus tard. J'apprécie les commentaires.
JimmyJames
0

En supposant que le fichier est trié par nombre croissant:

Assurez-vous qu'il contient bien des nombres (2³²-4).
Maintenant, si le fichier était complet (ou si les 4 nombres manquants étaient les 4 derniers), la lecture de n'importe quel mot du fichier à la position N retournerait la valeur correspondante N.

Utilisez une recherche de dichotomie sur les positions [0..2³²-4-1) pour rechercher le premier nombre non attendu X1.
Une fois trouvé le premier nombre manquant, refaites une recherche de dichtotomie sur les positions [X1 .. (2³²-4-1)] pour trouver le deuxième manquant, X2: Cette fois, la lecture d'un mot à la position N devrait retourner la valeur correspondante N-1 s'il n'y avait plus de numéros manquants (puisque vous avez passé un numéro manquant).
Répétez de même pour les deux nombres restants. À la troisième itération, la lecture du mot à la position N doit renvoyer N-2, et à la quatrième, il doit renvoyer N-3.

Avertissement: je n'ai pas testé cela. Mais je pense que cela devrait fonctionner. :)

Maintenant dans la vraie vie, je suis d'accord avec d'autres réponses: les premières questions porteraient sur l'environnement. Avons-nous de la RAM disponible (combien), le fichier se trouve-t-il sur un périphérique de stockage à accès direct, s'agit-il d'une opération ponctuelle (aucune optimisation requise) ou critique (chaque nombre de cycles), avons-nous un utilitaire de tri externe disponible , etc.
Trouvez ensuite un compromis acceptable pour le contexte. Cela montre au moins que vous commencez à analyser le problème avant de rechercher un algorithme.

filofel
la source
-2

Comme pour toutes les questions standard, la solution consiste à les rechercher sur Google avant l'entretien.

Cette question et ses variantes ont une réponse «correcte» très précise impliquant XORing tous les nombres. Son censé vous montrer comprendre les index dans les bases de données ou quelque chose. Donc, zéro point pour tout «pourrait fonctionner, mais pas ce qu'il dit sur le papier», répond im afriad.

Du côté positif, il y a un ensemble fini de ces questions, une révision de quelques heures vous fera ressembler à un génie. N'oubliez pas de prétendre que vous travaillez dans votre tête.

Modifier. Ahh il semble que pour 4 il y a une approche différente de XOR

http://books.google.com/books?id=415loiMd_c0C&lpg=PP1&dq=muthukrishnan%20data%20stream%20algorithms&hl=el&pg=PA1#v=onepage&q=muthukrishnan%20data%20stream%20algorithms&f=false

Modifier. Downvoters: Ceci est une solution O (n) de manuel publiée au problème exact indiqué dans le PO.

Ewan
la source
1
Notamment, ce livre lié concerne le traitement des flux. En particulier, le traitement de flux dans les limites des contraintes. Cela dit, je doute me crois que c'est à l'origine de la question de l'OP a vu, car il est par ailleurs assez trivial. Plus particulièrement, vous n'avez pas réellement répondu à la question. Vous aurez +1 de ma part si vous pouvez affirmer de manière convaincante que c'est la question "originale" ou "prévue" et expliquer la solution ... mais cela ne répond à rien en l'état.
svidgen
1
Cette réponse (dans une interview) montre simplement que vous avez lu le livre. Rien sur vos compétences ou vos processus de pensée. Et comment "google toutes les questions standard " avant une interview? Y a-t-il une liste finie de «toutes les questions jamais posées lors d'une interview» que j'ai ratées?
user949300
1
@ewan, il souligne également la difficulté d'embaucher un bon candidat! Si les «bons» sont tout simplement bien préparés pour les questions d'entrevue ... Il devient difficile d'embaucher quelqu'un qui peut réellement résoudre mes problèmes commerciaux?
svidgen
1
@ewan Pour être clair, je me moquais de ma ponctuation incorrecte. ... Dans tous les cas, gardez à l'esprit que j'ai également reçu un bon nombre d'offres d'emploi de ma journée, même en sachant ignorer les questions et réponses standard comme celle-ci. Et maintenant, en tant que gestionnaire d'embauche, je peux vous promettre que je ne veux pas de réponses récitées ... Cependant, je comprends que certains gestionnaires auront des besoins différents.
svidgen
1
@Ewan Je dois aussi préciser une chose, si mon ton n'a pas été reçu comme prévu: Vous devriez réviser votre réponse à réellement affirmer que le problème lié au livre est à la « question destinée. » Et puis répondez à la question! ... Vous auriez sans aucun doute mon +1, et bien d'autres, et la satisfaction d'aider le PO à le faire.
svidgen