C'est une question d'entrevue:
Avec un fichier d'entrée contenant quatre milliards d'entiers, fournissez un algorithme pour générer un entier qui n'est pas contenu dans le fichier. Supposons que vous avez 1 Go de mémoire. Faites un suivi de ce que vous feriez si vous n’avez que 10 Mo de mémoire.
Mon analyse:
La taille du fichier est de 4 × 109× 4 octets = 16 Go.
Nous pouvons faire un tri externe, ainsi nous apprenons à connaître la plage des nombres entiers. Ma question est la suivante: quel est le meilleur moyen de détecter l’entier manquant dans les grands ensembles d’entiers triés?
Ma compréhension (après avoir lu toutes les réponses):
En supposant que nous parlons d’entiers 32 bits. Il y a 2 ^ 32 = 4 * 109 entiers distincts.
Solution: si nous utilisons un bit représentant un entier distinct, cela suffit. nous n'avons pas besoin de trier. La mise en oeuvre:
int radix = 8;
byte[] bitfield = new byte[0xffffffff/radix];
void F() throws FileNotFoundException{
Scanner in = new Scanner(new FileReader("a.txt"));
while(in.hasNextInt()){
int n = in.nextInt();
bitfield[n/radix] |= (1 << (n%radix));
}
for(int i = 0; i< bitfield.lenght; i++){
for(int j =0; j<radix; j++){
if( (bitfield[i] & (1<<j)) == 0) System.out.print(i*radix+j);
}
}
}
Solution: Pour tous les préfixes 16 bits possibles, il y a 2 ^ 16 nombres d'entiers = 65 536, il faut 2 ^ 16 * 4 * 8 = 2 millions de bits. Nous avons besoin de construire 65536 seaux. Pour chaque compartiment, nous avons besoin de 4 octets contenant toutes les possibilités, car dans le pire des cas, les 4 milliards d'entiers appartiennent au même compartiment.
- Construisez le compteur de chaque compartiment lors du premier passage dans le fichier.
- Parcourez les seaux et trouvez le premier qui a moins de 65 536 occurrences.
- Construisez de nouveaux compartiments dont les préfixes 16 bits élevés se trouvent à l'étape 2 lors de la deuxième passe du fichier.
- Scannez les seaux construits à l’étape 3 et trouvez le premier seau qui n’a pas de succès.
Le code est très similaire à celui ci-dessus.
Conclusion: nous diminuons la mémoire en augmentant le nombre de passages de fichiers.
Une clarification pour ceux qui arrivent en retard: la question, telle que posée, ne dit pas qu'il y a exactement un entier qui ne figure pas dans le fichier - du moins, ce n'est pas la façon dont la plupart des gens l'interprètent. Cependant, de nombreux commentaires dans le fil de commentaire are concernent cette variante de la tâche. Malheureusement, le commentaire que introduit dans le fil de commentaire a été supprimé ultérieurement par son auteur, il semble donc que les réponses orphelines à ce dernier ont tout simplement mal compris. C'est très déroutant. Désolé.
En supposant que "entier" signifie 32 bits: Avoir 10 Mo d’espace est plus que suffisant pour compter le nombre de nombres présents dans le fichier d’entrée avec un préfixe de 16 bits donné, pour tous les 16 possibles. Les préfixes -bit en une passe dans le fichier d'entrée. Au moins un des seaux aura été touché moins de 2 fois 16 fois. Faites un deuxième passage pour trouver lequel des nombres possibles dans ce compartiment est déjà utilisé.
Si cela signifie plus de 32 bits, mais toujours de taille limitée: Procédez comme ci-dessus, en ignorant tous les numéros d'entrée qui se trouvent en dehors de la plage 32 bits (signée ou non signée; votre choix).
Si "entier" signifie un entier mathématique: Lire l’entrée une fois et garder une trace de la le plus grand nombre longueur du nombre le plus long que vous ayez jamais vu. Lorsque vous avez terminé, sortie le maximum plus un un nombre aléatoire qui a encore un chiffre. (L’un des nombres du fichier peut être un bignum nécessitant plus de 10 Mo pour être exactement, mais si l’entrée est un fichier, vous pouvez au moins représenter la longueur de tout ce qui y rentre).
Les algorithmes statistiquement informés résolvent ce problème en utilisant moins de passes que les approches déterministes.
Si de très grands nombres entiers sont autorisés , il est alors possible de générer un nombre susceptible d'être unique en O(1) temps. Un entier pseudo-aléatoire de 128 bits tel qu'un GUID n'entrera en collision qu'avec l'un des quatre milliards d'entiers existants de l'ensemble dans moins de 64 milliards de milliards de cas.
Si les nombres entiers sont limités à 32 bits , il est alors possible de générer un nombre susceptible d'être unique en un seul passage, avec beaucoup moins de 10 Mo. La probabilité qu'un entier pseudo-aléatoire de 32 bits entre en collision avec l'un des 4 milliards d'entiers existants est d'environ 93% (4e9/2 ^ 32). La probabilité que 1000 entiers pseudo-aléatoires entrent en collision est inférieure à un sur 12 000 milliards de milliards de dollars (probabilité d'une collision ^ 1 000). Ainsi, si un programme conserve une structure de données contenant 1 000 candidats pseudo-aléatoires et effectue une itération à travers les entiers connus, en éliminant les correspondances des candidats, il est pratiquement certain de trouver au moins un entier qui ne se trouve pas dans le fichier.
Une discussion détaillée sur ce problème a été discutée dans Jon Bentley "Colonne 1. Cracking the Oyster" Programmation de perles Addison-Wesley pp.3-1
Bentley discute de plusieurs approches, y compris le tri externe, le tri par fusion en utilisant plusieurs fichiers externes, etc., mais la meilleure méthode suggérée par Bentley est un algorithme en un seul passage utilisant champs de bits , qu'il appelle avec humour "Wonder Sort" :) En venant au problème, 4 milliards de nombres peuvent être représentés dans:
4 billion bits = (4000000000 / 8) bytes = about 0.466 GB
Le code pour implémenter le bitet est simple: (extrait de page solutions )
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];
void set(int i) { a[i>>SHIFT] |= (1<<(i & MASK)); }
void clr(int i) { a[i>>SHIFT] &= ~(1<<(i & MASK)); }
int test(int i){ return a[i>>SHIFT] & (1<<(i & MASK)); }
L'algorithme de Bentley effectue un seul passage sur le fichier, set
mettant le bit approprié dans le tableau, puis examine ce tableau à l'aide de la macro test
pour rechercher le nombre manquant.
Si la mémoire disponible est inférieure à 0,466 Go, Bentley suggère un algorithme k-pass, qui divise l’entrée en plages en fonction de la mémoire disponible. Pour prendre un exemple très simple, si seulement 1 octet (mémoire pour gérer 8 chiffres) était disponible et que la plage allait de 0 à 31, nous divisons cela en plages de 0 à 7, 8-15, 16-22, etc. et gérer cette plage dans chacun des 32/8 = 4
passes.
HTH.
Comme le problème ne spécifie pas que nous devons trouver le plus petit nombre possible qui ne se trouve pas dans le fichier, nous pourrions simplement générer un nombre plus long que le fichier d'entrée lui-même. :)
Pour la variante de 1 Go RAM, vous pouvez utiliser un vecteur de bits. Vous devez allouer 4 milliards de bits == 500 Mo tableau d'octets. Pour chaque numéro lu à partir de l'entrée, définissez le bit correspondant sur "1". Une fois que vous avez terminé, parcourez les bits, trouvez le premier qui est toujours '0'. Son index est la réponse.
S'ils sont des entiers 32 bits (probablement du choix d'environ 4 milliards de nombres proches de 232), votre liste de 4 milliards de nombres occupera au plus 93% des entiers possibles (4 * 109 / (232)). Donc, si vous créez un tableau de bits de 232 bits avec chaque bit initialisé à zéro (ce qui prendra 229 octets ~ 500 Mo de RAM; rappelez-vous un octet = 23 bits = 8 bits), lisez votre liste d'entiers et définissez pour chaque entier l'élément de tableau de bits correspondant de 0 à 1; et ensuite, parcourez votre tableau de bits et renvoyez le premier bit qui reste 0.
Si vous avez moins de RAM (~ 10 Mo), cette solution doit être légèrement modifiée. 10 Mo ~ 83886080 bits sont encore suffisants pour faire un tableau de bits pour tous les nombres compris entre 0 et 83886079. Vous pouvez donc parcourir votre liste d'ints; et ne notez que les numéros compris entre 0 et 83886079 dans votre tableau de bits. Si les nombres sont distribués au hasard; avec une probabilité écrasante (il diffère de 100% d'environ 10-2592069 ) vous trouverez un int manquant). En fait, si vous ne choisissez que les numéros 1 à 2048 (avec seulement 256 octets de RAM), vous trouverez toujours un nombre manquant dans un pourcentage écrasant (99,99999999999999999999999999999999999999999999999999999995%) du temps.
Mais disons au lieu d’avoir environ 4 milliards de chiffres; tu avais quelque chose comme 232 - 1 chiffres et moins de 10 Mo de RAM; de sorte que toute petite gamme d'ints n'a qu'une faible possibilité de ne pas contenir le nombre.
Si on vous garantissait que chaque int de la liste était unique, vous pouvez additionner les nombres et soustraire la somme avec un # manquant à la somme complète (½) (232) (232 - 1) = 9223372034707292160 pour trouver l'int manquant. Cependant, si un int s'est produit deux fois, cette méthode échouera.
Cependant, vous pouvez toujours diviser et conquérir. Une méthode naïve consisterait à parcourir le tableau et à compter le nombre de nombres figurant dans la première moitié (0 à 2).31-1) et la seconde moitié (231, 232). Choisissez ensuite la plage avec moins de nombres et répétez-la en la divisant par deux. (Dites s'il y avait deux nombres en moins dans (231, 232), votre recherche suivante comptera les nombres compris dans la plage (231, 3 * 230-1), (3 * 230, 232). Répétez jusqu'à ce que vous trouviez une plage sans chiffres et que vous ayez votre réponse. Devrait prendre O (lg N) ~ 32 lectures à travers le tableau.
Cette méthode était inefficace. Nous n'utilisons que deux entiers à chaque étape (ou environ 8 octets de RAM avec un entier de 4 octets (32 bits)). Une meilleure méthode serait de diviser en sqrt (232) = 216 = 65536 cases, chacune avec 65536 numéros dans une case. Chaque bac nécessite 4 octets pour stocker son compte, il en faut donc 218 octets = 256 Ko. Donc, bin 0 est (0 à 65535 = 216-1), le bac 1 est (216= 65536 à 2 * 216-1 = 131071), le bac 2 est (2 * 216= 131072 à 3 * 216-1 = 196607). Dans python vous auriez quelque chose comme:
import numpy as np
nums_in_bin = np.zeros(65536, dtype=np.uint32)
for N in four_billion_int_array:
nums_in_bin[N // 65536] += 1
for bin_num, bin_count in enumerate(nums_in_bin):
if bin_count < 65536:
break # we have found an incomplete bin with missing ints (bin_num)
Lisez la liste d'environ 4 milliards d'entiers; et compte combien d’ints tombent dans chacun des 216 bin et trouvez un incomplet qui n'a pas tous les 65536 numéros. Ensuite, vous relisez la liste des 4 milliards d’entiers; mais cette fois, notez seulement quand les entiers sont dans cette plage; retourner un peu quand vous les trouvez.
del nums_in_bin # allow gc to free old 256kB array
from bitarray import bitarray
my_bit_array = bitarray(65536) # 32 kB
my_bit_array.setall(0)
for N in four_billion_int_array:
if N // 65536 == bin_num:
my_bit_array[N % 65536] = 1
for i, bit in enumerate(my_bit_array):
if not bit:
print bin_num*65536 + i
break
Pourquoi le rendre si compliqué? Vous demandez un entier non présent dans le fichier?
Selon les règles spécifiées, la seule chose que vous devez stocker est le plus grand entier que vous avez rencontré jusqu'à présent dans le fichier. Une fois que le fichier entier a été lu, renvoyez un nombre 1 supérieur à celui.
Il n'y a aucun risque de frapper maxint ou quoi que ce soit, car, conformément aux règles, il n'y a aucune restriction quant à la taille de l'entier ou au nombre renvoyé par l'algorithme.
Cela peut être résolu dans très peu d'espace en utilisant une variante de la recherche binaire.
Commencez avec la plage de nombres autorisée, 0
à 4294967295
.
Calculez le point médian.
Parcourez le fichier en comptant combien de nombres étaient égaux, inférieurs ou supérieurs à la valeur du point médian.
Si aucun nombre n'était égal, vous avez terminé. Le numéro du milieu est la réponse.
Sinon, choisissez la plage qui a eu le moins de nombres et répétez à partir de l'étape 2 avec cette nouvelle plage.
Cela nécessitera jusqu'à 32 analyses linéaires dans le fichier, mais n'utilisera que quelques octets de mémoire pour stocker la plage et les comptes.
Ceci est essentiellement identique à solution de Henning , sauf qu'il utilise deux bacs au lieu de 16k.
Si vous avez un seul entier manquant dans l'intervalle [0, 2 ^ x - 1], il suffit de les supprimer tous ensemble. Par exemple:
>>> 0 ^ 1 ^ 3
2
>>> 0 ^ 1 ^ 2 ^ 3 ^ 4 ^ 6 ^ 7
5
(Je sais que cela ne répond pas à la question exactement , mais c'est une bonne réponse à une question très similaire.)
Sur la base du libellé actuel de la question initiale, la solution la plus simple est la suivante:
Recherchez la valeur maximale dans le fichier, puis ajoutez-y 1.
Ils cherchent peut-être à savoir si vous avez entendu parler d'un probabiliste filtre de Bloom qui peut très efficacement déterminer de manière absolue si une valeur ne fait pas partie d'un ensemble volumineux (mais ne peut déterminer avec une probabilité élevée qu'il s'agit d'un membre de l'ensemble.)
Utilisez un BitSet
. 4 milliards d'entiers (en supposant jusqu'à 2 ^ 32 entiers) regroupés dans un BitSet à 8 octets correspondent à 2 ^ 32/2 ^ 3 = 2 ^ 29 = environ 0,5 Gb.
Pour ajouter un peu plus de détails - à chaque fois que vous lisez un numéro, définissez le bit correspondant dans le BitSet. Ensuite, passez sur le BitSet pour trouver le premier numéro qui n'est pas présent. En fait, vous pouvez le faire tout aussi efficacement en choisissant plusieurs fois un nombre aléatoire et en vérifiant s’il est présent.
En réalité, BitSet.nextClearBit (0) vous indiquera le premier bit non défini.
En ce qui concerne l'API BitSet, elle semble ne prendre en charge que 0..MAX_INT. Il est donc possible que vous ayez besoin de 2 BitSet - un pour les numéros "plus" et un pour les numéros "à avoir" - mais les exigences en matière de mémoire ne changent pas.
S'il n'y a pas de limite de taille, le moyen le plus rapide est de prendre la longueur du fichier et de générer la longueur du fichier + 1 nombre de chiffres aléatoires (ou simplement "11111 ..."). Avantage: vous n'avez même pas besoin de lire le fichier et vous pouvez réduire l'utilisation de la mémoire presque à zéro. Inconvénient: vous imprimerez des milliards de chiffres.
Cependant, si le seul facteur était de minimiser l'utilisation de la mémoire et que rien d'autre n'était important, ce serait la solution optimale. Cela pourrait même vous rapporter le prix du "pire abus des règles".
Si nous supposons que la plage de nombres sera toujours 2 ^ n (une puissance paire de 2), alors exclusif-ou fonctionnera (comme le montre une autre affiche). En ce qui concerne pourquoi, prouvons-le:
Étant donné toute plage d'entiers basée sur 0 et contenant 2^n
éléments avec un élément manquant, vous pouvez trouver cet élément manquant simplement en combinant les valeurs connues pour obtenir le nombre manquant.
Regardons n = 2. Pour n = 2, nous pouvons représenter 4 entiers uniques: 0, 1, 2, 3. Ils ont un motif binaire de:
Maintenant, si nous regardons, chaque bit est défini exactement deux fois. Par conséquent, étant donné qu'il est défini un nombre pair de fois et que l'un des numéros exclus-ou donnera un zéro 0. Par conséquent, le nombre manquant et le nombre résultant en exclusivité sont exactement les mêmes. Si nous retirons 2, le xor résultant sera 10
(ou 2).
Maintenant, regardons n + 1. Appelons le nombre de fois que chaque bit est défini dans n
, x
et le nombre de fois que chaque bit est défini dans n+1
y
. La valeur de y
sera égale à y = x * 2
car il existe des éléments x
dont le bit n+1
est défini sur 0 et des éléments x
avec le n+1
bit mis à 1. Et puisque 2x
sera toujours pair, n+1
aura toujours chaque bit défini un nombre pair de fois.
Par conséquent, puisque n=2
fonctionne et que n+1
fonctionne, la méthode xor fonctionnera pour toutes les valeurs de n>=2
.
C'est assez simple. Il utilise 2 * n bits de mémoire. Ainsi, pour toute plage <= 32, 2 entiers 32 bits fonctionneront (en ignorant toute mémoire utilisée par le descripteur de fichier). Et cela fait un seul passage du fichier.
long supplied = 0;
long result = 0;
while (supplied = read_int_from_file()) {
result = result ^ supplied;
}
return result;
Cet algorithme fonctionnera pour les plages de tout nombre commençant à tout nombre final, tant que la plage totale est égale à 2 ^ n ... Ceci refond fondamentalement la plage pour avoir le minimum à 0. Mais cela nécessite 2 passages. à travers le fichier (le premier à saisir le minimum, le second à calculer l’int manquant).
long supplied = 0;
long result = 0;
long offset = INT_MAX;
while (supplied = read_int_from_file()) {
if (supplied < offset) {
offset = supplied;
}
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
result = result ^ (supplied - offset);
}
return result + offset;
Nous pouvons appliquer cette méthode modifiée à un ensemble de plages arbitraires, étant donné que toutes les plages vont croiser une puissance de 2 ^ n au moins une fois. Cela ne fonctionne que s'il y a un seul bit manquant. Il faut 2 passages d'un fichier non trié, mais il trouvera le numéro manquant à chaque fois:
long supplied = 0;
long result = 0;
long offset = INT_MAX;
long n = 0;
double temp;
while (supplied = read_int_from_file()) {
if (supplied < offset) {
offset = supplied;
}
}
reset_file_pointer();
while (supplied = read_int_from_file()) {
n++;
result = result ^ (supplied - offset);
}
// We need to increment n one value so that we take care of the missing
// int value
n++
while (n == 1 || 0 != (n & (n - 1))) {
result = result ^ (n++);
}
return result + offset;
Fondamentalement, la plage autour de 0 est refondue. Ensuite, il compte le nombre de valeurs non triées à ajouter lors du calcul de l'exclusivité-ou. Ensuite, il ajoute 1 au nombre de valeurs non triées pour prendre en charge la valeur manquante (comptez la valeur manquante). Continuez ensuite à mémoriser la valeur n, incrémentée de 1 à chaque fois, jusqu'à ce que n soit une puissance de 2. Le résultat est ensuite recalculé sur la base d'origine. Terminé.
Voici l'algorithme que j'ai testé dans PHP (en utilisant un tableau au lieu d'un fichier, mais le même concept):
function find($array) {
$offset = min($array);
$n = 0;
$result = 0;
foreach ($array as $value) {
$result = $result ^ ($value - $offset);
$n++;
}
$n++; // This takes care of the missing value
while ($n == 1 || 0 != ($n & ($n - 1))) {
$result = $result ^ ($n++);
}
return $result + $offset;
}
Alimenté dans un tableau contenant n’importe quelle plage de valeurs (j’ai testé, y compris les négatifs) et dont une dans cette plage est manquante, la valeur correcte a été trouvée à chaque fois.
Puisque nous pouvons utiliser le tri externe, pourquoi ne pas simplement rechercher un écart? Si nous supposons que le fichier est trié avant l'exécution de cet algorithme:
long supplied = 0;
long last = read_int_from_file();
while (supplied = read_int_from_file()) {
if (supplied != last + 1) {
return last + 1;
}
last = supplied;
}
// The range is contiguous, so what do we do here? Let's return last + 1:
return last + 1;
Vérifiez la taille du fichier d'entrée, puis indiquez un nombre quelconque qui est trop grand pour être représenté par un fichier de cette taille. Cela peut sembler une astuce peu coûteuse, mais c'est une solution créative à un problème d'interview, elle élimine parfaitement le problème de mémoire et elle est techniquement O (n).
void maxNum(ulong filesize)
{
ulong bitcount = filesize * 8; //number of bits in file
for (ulong i = 0; i < bitcount; i++)
{
Console.Write(9);
}
}
Devrait imprimer 10 nombre de bits - 1, qui sera toujours supérieur à 2 nombre de bits. Techniquement, le nombre que vous devez battre est 2 nombre de bits - (4 * 109 - 1), puisque vous savez qu'il existe (4 milliards - 1) d'autres entiers dans le fichier, et même avec une compression parfaite, ils prendront au moins un bit chacun.
La méthode la plus simple consiste à rechercher le nombre minimal dans le fichier et à renvoyer 1 moins que cela. Ceci utilise O(1) stockage, et O(n) fois pour un fichier de n nombres. Cependant, cela échouera si la plage de numéros est limitée, ce qui pourrait faire min-1 pas-un-nombre.
La méthode simple et directe d'utilisation d'un bitmap a déjà été mentionnée. Cette méthode utilise O(n) le temps et le stockage.
Une méthode en deux passes avec 2 ^ 16 seaux de comptage a également été mentionnée. Il lit 2 * n entiers. Il utilise donc O(n) time et O(1) storage, mais il ne peut pas gérer les ensembles de données comportant plus de 2 ^ 16 nombres. Cependant, il est facilement étendu à (par exemple) 2 ^ 60 nombres entiers de 64 bits en exécutant 4 passes au lieu de 2, et s'adapte facilement à l'utilisation d'une mémoire minuscule en utilisant uniquement le nombre de bacs pouvant être insérés dans la mémoire et en augmentant le nombre de passes de manière correspondante. auquel cas le temps d'exécution n'est plus O(n) mais à la place est O (n * log n).
La méthode de numérotation de tous les nombres ensemble, mentionnée jusqu'à présent par rfrankel et enfin par ircmaxell, répond à la question posée dans stackoverflow # 35185 , comme l'a souligné ltn100. Il utilise O(1) stockage et O(n) temps d'exécution. Si pour le moment nous supposons des entiers 32 bits, XOR a une probabilité de 7% de produire un nombre distinct. Justification: étant donné ~ 4G nombres distincts XOR'd ensemble, et ca. 300 M pas dans le fichier, le nombre de bits définis dans chaque position de bit a la même chance d'être impair ou pair. Ainsi, 2 ^ 32 nombres ont la même probabilité de se produire que le résultat XOR, dont 93% sont déjà dans le fichier. Notez que si les nombres dans le fichier ne sont pas tous distincts, la probabilité de succès de la méthode XOR augmente.
Question piège, à moins que cela ait été mal cité. Il suffit de lire le fichier une fois pour obtenir le nombre entier maximal n
et renvoyer n+1
.
Bien entendu, vous aurez besoin d'un plan de sauvegarde au cas où n+1
provoque un dépassement d'entier.
Pour une raison quelconque, dès que j'ai lu ce problème, j'ai pensé à la diagonalisation. Je suppose des entiers arbitrairement grands.
Lire le premier numéro. Appuyez à gauche avec zéro bit jusqu'à obtenir 4 milliards de bits. Si le premier bit (de poids fort) est 0, la sortie 1; else output 0. (Vous n'avez pas vraiment besoin d'appuyer sur le pad de gauche: vous produisez juste un 1 s'il n'y a pas assez de bits dans le nombre.) Faites la même chose avec le deuxième nombre, sauf utilisez son deuxième bit. Continuez à travers le fichier de cette façon. Vous produirez un nombre de bits de 4 milliards numéro par bit et ce nombre ne sera pas le même que n'importe quel fichier du fichier. La preuve: c’était la même chose que le nième nombre, ils seraient d’accord sur le nième bit, mais ce n’est pas le cas par construction.
Juste pour être complet, voici une autre solution très simple, qui prendra probablement beaucoup de temps, mais utilise très peu de mémoire.
Tous les nombres entiers possibles sont compris entre int_min
et int_max
, et bool isNotInFile(integer)
une fonction qui renvoie true si le fichier ne contient pas un certain nombre entier et false autrement (en comparant ce certain nombre à chaque entier dans le fichier)
for (integer i = int_min; i <= int_max; ++i)
{
if (isNotInFile(i)) {
return i;
}
}
Vous pouvez utiliser des indicateurs de bits pour indiquer si un entier est présent ou non.
Après avoir parcouru l'intégralité du fichier, balayez chaque bit pour déterminer si le numéro existe ou non.
En supposant que chaque entier est 32 bits, ils pourront facilement contenir 1 Go de RAM si le balisage de bits est effectué.
Supprimez les espaces et les caractères non numériques du fichier et ajoutez-en 1. Votre fichier contient maintenant un numéro unique non répertorié dans le fichier d'origine.
De Reddit par Carbonetc.
Pour la contrainte de mémoire de 10 Mo:
Lorsque vous avez terminé, prenez simplement un chemin qui n’a pas été créé auparavant pour créer le numéro demandé.
4 milliards de chiffres = 2 ^ 32, ce qui signifie que 10 Mo pourraient ne pas suffire.
EDIT
Une optimisation est possible. Si deux feuilles d'extrémité ont été créées et ont un parent commun, elles peuvent être supprimées et le parent désigné comme solution. Cela coupe les branches et réduit le besoin de mémoire.
EDIT II
Il n'est pas nécessaire de construire l'arbre aussi. Il suffit de créer des branches profondes si les nombres sont similaires. Si nous coupons aussi des branches, cette solution pourrait en fait fonctionner.
Je vais répondre à la version 1 Go:
Il n’ya pas assez d’informations dans la question, je vais donc énoncer quelques hypothèses:
Le nombre entier est de 32 bits avec une plage allant de -2 147 483 648 à 2 147 483 647.
Pseudo-code:
var bitArray = new bit[4294967296]; // 0.5 GB, initialized to all 0s.
foreach (var number in file) {
bitArray[number + 2147483648] = 1; // Shift all numbers so they start at 0.
}
for (var i = 0; i < 4294967296; i++) {
if (bitArray[i] == 0) {
return i - 2147483648;
}
}
Tant que nous apportons des réponses créatives, en voici une autre.
Utilisez le programme de tri externe pour trier le fichier d'entrée numériquement. Cela fonctionnera pour toute quantité de mémoire que vous pourriez avoir (il utilisera le stockage de fichiers si nécessaire). Lire le fichier trié et sortir le premier numéro manquant.
Comme Ryan l'a dit fondamentalement, triez le fichier, puis passez en revue les entiers et, lorsqu'une valeur est ignorée, vous l'avez :)
EDIT sur le vote descendant: le terminal opérateur a indiqué que le fichier pouvait être trié, ce qui est une méthode valide.
2128 * 1018 + 1 (qui est (28)16 * 1018 + 1) - ne peut-il pas être une réponse universelle pour aujourd'hui? Cela représente un nombre qui ne peut pas être conservé dans un fichier 16 EB, ce qui correspond à la taille de fichier maximale dans tout système de fichiers actuel.
élimination des bits
Une solution consiste à éliminer les bits, mais cela pourrait ne pas donner de résultat (il est probable que cela ne se produira pas). Psuedocode:
long val = 0xFFFFFFFFFFFFFFFF; // (all bits set)
foreach long fileVal in file
{
val = val & ~fileVal;
if (val == 0) error;
}
Nombre de bits
Gardez une trace du nombre de bits; et utilisez les bits avec les plus petites quantités pour générer une valeur. Là encore, cela n’a aucune garantie de générer une valeur correcte.
Range Logic
Gardez une trace d'une liste ordonnée gammes (classés par début). Une plage est définie par la structure:
struct Range
{
long Start, End; // Inclusive.
}
Range startRange = new Range { Start = 0x0, End = 0xFFFFFFFFFFFFFFFF };
Parcourez chaque valeur du fichier et essayez de la supprimer de la plage actuelle. Cette méthode n’a aucune garantie de mémoire, mais elle devrait très bien fonctionner.
Je pense que c'est un problème résolu (voir ci-dessus), mais il convient de garder à l'esprit un cas parallèle intéressant car il peut être demandé:
S'il existe exactement 4 294 967 295 (2 ^ 32 - 1) entiers 32 bits sans répétition et qu'il en manque donc un seul, la solution est simple.
Commencez un total en cours d'exécution à zéro, et pour chaque entier du fichier, ajoutez cet entier avec un dépassement de capacité de 32 bits (effectivement, runningTotal = (runningTotal + nextInteger)% 4294967296). Une fois terminé, ajoutez 4294967296/2 au total cumulé, toujours avec débordement de 32 bits. Soustrayez ceci de 4294967296 et le résultat est le nombre entier manquant.
Le problème "un seul entier manquant" peut être résolu avec une seule exécution et seulement 64 bits de RAM dédiés aux données (32 pour le total cumulé, 32 à lire dans l'entier suivant).
Corollaire: la spécification plus générale est extrêmement simple à faire correspondre si nous ne sommes pas concernés par le nombre de bits que le résultat entier doit avoir. Nous générons simplement un entier assez grand pour qu'il ne puisse pas être contenu dans le fichier qui nous est donné. Encore une fois, cela prend une RAM absolument minimale. Voir le pseudocode.
# Grab the file size
fseek(fp, 0L, SEEK_END);
sz = ftell(fp);
# Print a '2' for every bit of the file.
for (c=0; c<sz; c++) {
for (b=0; b<4; b++) {
print "2";
}
}
Si vous n'assumez pas la contrainte 32 bits, il vous suffit de renvoyer un nombre 64 bits généré aléatoirement (ou 128 bits si vous êtes pessimiste). La probabilité de collision est 1 in 2^64/(4*10^9) = 4611686018.4
(environ 1 sur 4 milliards). Vous auriez raison la plupart du temps!
(Plaisanterie ... genre de.)
Avec un fichier d'entrée contenant quatre milliards d'entiers, fournissez un algorithme pour générer un entier qui n'est pas contenu dans le fichier. Supposons que vous avez 1 GiB de mémoire. Faites un suivi de ce que vous feriez si vous n’avez que 10 Mo de mémoire.
La taille du fichier est de 4 * 109 * 4 octets = 16 Gio
En cas d'entier non signé 32 bits
0 <= Number < 2^32
0 <= Number < 4,294,967,296
Ma solution proposée: C++ sans vérification d'erreur
#include <vector>
#include <fstream>
#include <iostream>
using namespace std;
int main ()
{
const long SIZE = 1L << 32;
std::vector<bool> checker(SIZE, false);
std::ifstream infile("file.txt"); // TODO: error checking
unsigned int num = 0;
while (infile >> num)
{
checker[num] = true ;
}
infile.close();
// print missing numbers
for (long i = 0; i < SIZE; i++)
{
if (!checker[i])
cout << i << endl ;
}
return 0;
}
Complexité
Vous n'avez pas besoin de les trier, mais simplement de les partitionner à plusieurs reprises.
La première étape ressemble à la première passe d'un tri rapide. Choisissez l'un des entiers x et utilisez-le pour faire un passage dans le tableau afin de placer toutes les valeurs inférieures à x à sa gauche et les valeurs supérieures à x à sa droite. Trouvez quel côté de x a le plus grand nombre d'emplacements disponibles (les entiers ne sont pas dans la liste). Ceci est facilement calculable en comparant la valeur de x avec sa position. Répétez ensuite la partition dans la sous-liste de ce côté de x. Répétez ensuite la partition de la sous-liste avec le plus grand nombre d’entiers disponibles, etc. Le nombre total de comparaisons pour arriver à une plage vide devrait être d’environ 4 milliards, à peu près.
Peut-être que je manque complètement le but de cette question, mais vous voulez trouver un entier manquant dans un fichier trié de nombres entiers?
Euh ... vraiment? Pensons à quoi ressemblerait un tel fichier:
1 2 3 4 5 6 ... premier numéro manquant ... etc.
La solution à ce problème semble triviale.
Vous pouvez accélérer la recherche des entiers manquants après avoir lu les entiers existants en stockant des plages d'entiers non consultés dans une arborescence donnée.
Vous commenceriez par stocker [0..4294967295] et chaque fois que vous lirez un entier, vous épisserez la plage dans laquelle elle se trouve, en supprimant une plage lorsqu'elle deviendra vide. À la fin, vous avez le jeu exact d’entiers manquants dans les plages. Donc, si vous voyez 5 comme premier entier, vous aurez [0..4] et [6..4294967295].
Ceci est beaucoup plus lent que le marquage des bits, ce ne serait donc qu'une solution pour le boîtier de 10 Mo à condition que vous puissiez stocker les niveaux inférieurs de l'arborescence dans des fichiers.
Une façon de stocker un tel arbre serait un arbre B avec le début de la plage comme clé et la fin de la plage comme valeur. Dans le pire des cas, vous obtiendrez tous les entiers pairs ou impairs, ce qui signifierait stocker 2 ^ 31 valeurs ou des dizaines de Go pour l’arbre ... Aïe. Le meilleur des cas est un fichier trié dans lequel vous n'utiliseriez que quelques entiers pour l'ensemble de l'arbre.
Donc, pas vraiment la bonne réponse mais je pensais mentionner cette façon de faire. Je suppose que j'échouerais l'entrevue ;-)
Ancienne question, mais je m'interroge sur les exigences "non fonctionnelles". À mon avis, il faudrait donner un indice - si cette question a été posée ailleurs que dans un livre qui discute ensuite de toutes les possibilités avec le pour et le contre. Il semble assez souvent que les entretiens d'embauche donnent l'impression de poser des questions, ce qui me laisse perplexe, car il est impossible de donner une réponse définitive sans connaître les exigences non strictes, à savoir "il doit être très rapide pour rechercher les numéros manquants, car il est utilisé x fois en une seconde. ".
Je pense qu'une telle question pourrait être possible de donner une réponse raisonnable pour.
désavantages:
avantages:
Encore une fois, une très belle question pour un livre. Mais je pense que c’est une question étrange de demander la meilleure solution, quand le problème à résoudre n’est pas complètement connu.
Je lis peut-être cela de trop près, mais la question dit "générer un entier qui n'est pas contenu dans le fichier". Je viens de trier la liste et ajouter 1 à l'entrée max. Bam, un entier qui n'est pas contenu dans le fichier.
Je suis venu avec l'algorithme suivant.
Mon idée: parcourir tout le fichier entier d’entiers une fois et pour chaque position de bit compter ses 0 et ses 1. La quantité de 0 et de 1 doit être de 2 ^ (numOfBits)/2. Par conséquent, si la quantité est inférieure à celle attendue, nous pouvons l'utiliser de notre nombre résultant.
Par exemple, supposons que entier soit 32 bits, alors nous avons besoin de
int[] ones = new int[32];
int[] zeroes = new int[32];
Pour chaque nombre, nous devons parcourir 32 bits et augmenter la valeur de 0 ou 1:
for(int i = 0; i < 32; i++){
ones[i] += (val>>i&0x1);
zeroes[i] += (val>>i&0x1)==1?0:1;
}
Enfin, après le traitement du fichier:
int res = 0;
for(int i = 0; i < 32; i++){
if(ones[i] < (long)1<<31)res|=1<<i;
}
return res;
NOTE: dans certaines langues (ex. Java) 1 << 31 est un nombre négatif, donc (long) 1 << 31 est la bonne façon de le faire.