J'ai eu une expérience d'embauche intéressante il y a quelque temps. La question a commencé très facilement:
Q1 : Nous avons un sac contenant des nombres
1
,2
,3
, ...,100
. Chaque numéro apparaît exactement une fois, il y a donc 100 numéros. Maintenant, un numéro est choisi au hasard dans le sac. Trouvez le numéro manquant.
J'ai entendu cette question d'entrevue avant, bien sûr, alors j'ai répondu très rapidement dans le sens de:
A1 : Eh bien, la somme des nombres
1 + 2 + 3 + … + N
est(N+1)(N/2)
(voir Wikipedia: somme des séries arithmétiques ). CarN = 100
, la somme est5050
.Ainsi, si tous les numéros sont présents dans le sac, la somme sera exactement
5050
. Puisqu'un nombre est manquant, la somme sera inférieure à cela et la différence est ce nombre. Nous pouvons donc trouver ce nombre manquant dans leO(N)
temps et l'O(1)
espace.
À ce stade, je pensais avoir bien fait, mais tout d'un coup, la question a pris une tournure inattendue:
Q2 : C'est exact, mais maintenant comment feriez-vous si DEUX numéros sont manquants?
Je n'avais jamais vu / entendu / envisagé cette variation auparavant, alors j'ai paniqué et je n'ai pas pu répondre à la question. L'intervieweur a insisté pour connaître mon processus de réflexion, j'ai donc mentionné que nous pourrions peut-être obtenir plus d'informations en comparant avec le produit attendu, ou peut-être faire un deuxième passage après avoir recueilli des informations lors du premier passage, etc., mais je ne faisais que tirer dans l'obscurité plutôt que d'avoir réellement un chemin clair vers la solution.
L'enquêteur a essayé de m'encourager en disant qu'avoir une deuxième équation est en effet une façon de résoudre le problème. À ce stade, j'étais un peu contrarié (de ne pas avoir connu la réponse à l'avance) et j'ai demandé s'il s'agissait d'une technique de programmation générale (lire: "utile"), ou s'il s'agissait simplement d'une réponse astucieuse.
La réponse de l'intervieweur m'a surpris: vous pouvez généraliser la technique pour trouver 3 nombres manquants. En fait, vous pouvez le généraliser pour trouver k nombres manquants.
Qk : Si exactement k nombres manquent dans le sac, comment le trouveriez-vous efficacement?
C'était il y a quelques mois, et je n'arrivais toujours pas à comprendre quelle était cette technique. De toute évidence , il y a une Ω(N)
limite de temps inférieure puisque nous devons analyser tous les chiffres au moins une fois, mais l'intervieweur a insisté pour que le TIME et SPACE complexité de la technique de résolution (moins le O(N)
balayage d'entrée de temps) est défini dans k pas N .
La question ici est donc simple:
- Comment résoudriez-vous Q2 ?
- Comment résoudriez-vous Q3 ?
- Comment résoudriez- vous Qk ?
Clarifications
- Généralement, il y a N nombres de 1 .. N , pas seulement 1..100.
- Je ne cherche pas la solution évidente basée sur un ensemble , par exemple en utilisant un ensemble de bits , en codant la présence / absence de chaque nombre par la valeur d'un bit désigné, donc en utilisant des
O(N)
bits dans un espace supplémentaire. Nous ne pouvons pas d'espace supplémentaire proportionnel à N . - Je ne recherche pas non plus l'approche évidente du tri en premier. Cela et l'approche basée sur les ensembles méritent d'être mentionnés dans une interview (ils sont faciles à mettre en œuvre et, selon N , peuvent être très pratiques). Je recherche la solution Holy Graal (qui peut ou non être pratique à mettre en œuvre, mais qui a néanmoins les caractéristiques asymptotiques souhaitées).
Encore une fois, bien sûr, vous devez scanner l'entrée O(N)
, mais vous ne pouvez capturer qu'une petite quantité d'informations (définies en termes de k et non de N ), et devez ensuite trouver les k nombres manquants d'une manière ou d'une autre.
XOR
de tous les nombres de1
àn
, puis le résultat de xoring avec tous les nombres dans le tableau donné. À la fin, vous avez votre numéro manquant. Dans cette solution, vous n'avez pas à vous soucier du débordement comme pour résumer.Réponses:
Voici un résumé du lien de Dimitris Andreou .
Rappelez-vous la somme des puissances i-ème, où i = 1,2, .., k. Cela réduit le problème de la résolution du système d'équations
a 1 + a 2 + ... + a k = b 1
a 1 2 + a 2 2 + ... + a k 2 = b 2
...
a 1 k + a 2 k + ... + a k k = b k
En utilisant les identités de Newton , connaître b i permet de calculer
c 1 = a 1 + a 2 + ... a k
c 2 = a 1 a 2 + a 1 a 3 + ... + a k-1 a k
...
c k = a 1 a 2 ... a k
Si vous développez le polynôme (xa 1 ) ... (xa k ) les coefficients seront exactement c 1 , ..., c k - voir les formules de Viète . Étant donné que tous les facteurs polynomiaux sont uniques (l'anneau de polynômes est un domaine euclidien ), cela signifie que les i sont déterminés de manière unique, jusqu'à la permutation.
Cela met fin à une preuve que se souvenir des pouvoirs est suffisant pour récupérer les chiffres. Pour k constant, c'est une bonne approche.
Cependant, lorsque k varie, l'approche directe du calcul de c 1 , ..., c k est d'un coût prohibitif, car par exemple c k est le produit de tous les nombres manquants, magnitude n! / (Nk) !. Pour surmonter cela, effectuez des calculs dans le champ Z q , où q est un nombre premier tel que n <= q <2n - il existe par le postulat de Bertrand . La preuve n'a pas besoin d'être modifiée, car les formules sont toujours valables et la factorisation des polynômes est toujours unique. Vous avez également besoin d'un algorithme de factorisation sur des champs finis, par exemple celui de Berlekamp ou Cantor-Zassenhaus .
Pseudocode de haut niveau pour k constant:
Pour faire varier k, trouver un premier n <= q <2n en utilisant par exemple Miller-Rabin, et effectuer les étapes avec tous les nombres réduits modulo q.
EDIT: La version précédente de cette réponse indiquait qu'au lieu de Z q , où q est premier, il est possible d'utiliser un champ fini de caractéristique 2 (q = 2 ^ (log n)). Ce n'est pas le cas, car les formules de Newton nécessitent une division par des nombres jusqu'à k.
la source
q = 2^(log n)
. (Comment avez-vous fait les super et les indices?!)O(N^2)
solution la plus triviale surclassera probablement cette beauté même à un niveau raisonnablement élevéN
. Me fait penser à ceci: tinyurl.com/c8fwgw Néanmoins, un excellent travail! Je n'aurais pas eu la patience de parcourir tous les calculs :)hash set
et itérer sur la1...N
suite à l'aide de recherches pour déterminer s'il manque des nombres, serait lak
solution la plus générique, la plus rapide en moyenne en ce qui concerne les variations, la plus déboguable, la plus maintenable et la plus compréhensible. Bien sûr, la méthode mathématique est impressionnante, mais quelque part en cours de route, vous devez être ingénieur et non mathématicien. Surtout lorsque les affaires sont impliquées.Vous le trouverez en lisant les quelques pages de Muthukrishnan - Algorithmes de flux de données: Puzzle 1: Trouver les nombres manquants . Il montre exactement la généralisation que vous recherchez . C'est probablement ce que votre intervieweur a lu et pourquoi il a posé ces questions.
Maintenant, si seulement les gens commençaient à supprimer les réponses qui sont subsumées ou remplacées par le traitement de Muthukrishnan, et rendraient ce texte plus facile à trouver. :)
Voir également la réponse directement liée de sdcvvc , qui comprend également le pseudocode (hourra! Pas besoin de lire ces formulations mathématiques difficiles :)) (merci, excellent travail!).
la source
Nous pouvons résoudre Q2 en additionnant à la fois les nombres eux-mêmes et les carrés des nombres.
Nous pouvons alors réduire le problème à
Où
x
et dansy
quelle mesure les sommes sont inférieures aux valeurs attendues.La substitution nous donne:
Que nous pouvons ensuite résoudre pour déterminer nos numéros manquants.
la source
Comme l'a souligné @j_random_hacker, cela est assez similaire à la recherche de doublons dans le temps O (n) et l'espace O (1) , et une adaptation de ma réponse y fonctionne également ici.
En supposant que le "sac" est représenté par un tableau
A[]
de taille basé sur 1N - k
, nous pouvons résoudre Qk dans leO(N)
temps etO(k)
espace supplémentaire.Tout d'abord, nous étendons notre tableau
A[]
park
éléments, afin qu'il soit maintenant de tailleN
. Ceci est l'O(k)
espace supplémentaire. Nous exécutons ensuite l'algorithme de pseudo-code suivant:La première boucle initialise les
k
entrées supplémentaires de la même manière que la première entrée du tableau (c'est juste une valeur pratique que nous savons déjà présente dans le tableau - après cette étape, toutes les entrées manquantes dans le tableau de taille initialN-k
sont toujours manquant dans le tableau étendu).La deuxième boucle permute le tableau étendu de sorte que si l'élément
x
est présent au moins une fois, alors l'une de ces entrées sera en positionA[x]
.Notez que bien qu'il ait une boucle imbriquée, il fonctionne toujours en
O(N)
temps - un swap ne se produit que s'il y a unei
telle queA[i] != i
, et chaque jeux de swap au moins un élément tel queA[i] == i
, lorsque cela n'a pas été vrai avant. Cela signifie que le nombre total de swaps (et donc le nombre total d'exécutions duwhile
corps de boucle) est au maximumN-1
.La troisième boucle affiche les index du tableau
i
qui ne sont pas occupés par la valeuri
- cela signifie qu'ilsi
doivent avoir été manquants.la source
A[i]
, ce qui signifie que la prochaine itération ne comparera pas les deux mêmes valeurs que la précédente. Le nouveauA[i]
sera le même que celui de la dernière boucleA[A[i]]
, mais le nouveauA[A[i]]
sera une nouvelle valeur. Essayez-le et voyez.J'ai demandé à un enfant de 4 ans de résoudre ce problème. Il tria les chiffres puis compta. Cela a un espace requis de O (sol de la cuisine), et cela fonctionne tout aussi facilement, mais il manque de nombreuses boules.
la source
Je ne sais pas si c'est la solution la plus efficace, mais je ferais une boucle sur toutes les entrées et utiliserais un ensemble de bits pour me souvenir, quels nombres sont définis, puis je testerais 0 bit.
J'aime les solutions simples - et je crois même que cela pourrait être plus rapide que de calculer la somme, ou la somme des carrés, etc.
la source
O(N)
tri par comptage ni le tri parO(N log N)
comparaison n'est ce que je recherche, bien qu'il s'agisse de solutions très simples.Je n'ai pas vérifié les calculs, mais je soupçonne que le calcul
Σ(n^2)
dans la même passe que nous calculonsΣ(n)
fournirait suffisamment d'informations pour obtenir deux nombres manquants, faitesΣ(n^3)
aussi s'il y en a trois, et ainsi de suite.la source
Le problème avec les solutions basées sur des sommes de nombres est qu'elles ne prennent pas en compte le coût de stockage et de travail avec des nombres avec de grands exposants ... en pratique, pour que cela fonctionne pour de très grands n, une grande bibliothèque de nombres serait utilisée . Nous pouvons analyser l'utilisation de l'espace pour ces algorithmes.
Nous pouvons analyser la complexité temporelle et spatiale des algorithmes sdcvvc et Dimitris Andreou.
Espace de rangement:
Donc
l_j \in \Theta(j log n)
Stockage total utilisé:
\sum_{j=1}^k l_j \in \Theta(k^2 log n)
Espace utilisé: en supposant que le calcul
a^j
prenne duceil(log_2 j)
temps, temps total:Temps total utilisé:
\Theta(kn log n)
Si ce temps et cet espace sont satisfaisants, vous pouvez utiliser un algorithme récursif simple. Soit b! I la ième entrée dans le sac, n le nombre de nombres avant les suppressions et k le nombre de suppressions. Dans la syntaxe Haskell ...
Stockage utilisé:
O(k)
pour la liste,O(log(n))
pour la pile:O(k + log(n))
cet algorithme est plus intuitif, a la même complexité temporelle et utilise moins d'espace.la source
isInRange
est O (log n) , pas O (1) : il compare les nombres dans la plage 1..n, il doit donc comparer O (log n) bits. Je ne sais pas dans quelle mesure cette erreur affecte le reste de l'analyse.Attends une minute. Comme la question est posée, il y a 100 numéros dans le sac. Quelle que soit la taille de k, le problème peut être résolu en temps constant car vous pouvez utiliser un ensemble et supprimer des nombres de l'ensemble en 100 itérations au plus d'une boucle. 100 est constant. L'ensemble des nombres restants est votre réponse.
Si nous généralisons la solution aux nombres de 1 à N, rien ne change sauf que N n'est pas une constante, donc nous sommes en O (N - k) = O (N) temps. Par exemple, si nous utilisons un ensemble de bits, nous définissons les bits à 1 dans le temps O (N), parcourons les nombres, en mettant les bits à 0 au fur et à mesure (O (Nk) = O (N)), puis nous avoir la réponse.
Il me semble que l'intervieweur vous demandait comment imprimer le contenu de l'ensemble final en temps O (k) plutôt qu'en temps O (N). De toute évidence, avec un bit défini, vous devez parcourir tous les N bits pour déterminer si vous devez imprimer le nombre ou non. Cependant, si vous modifiez la façon dont l'ensemble est implémenté, vous pouvez imprimer les nombres en k itérations. Cela se fait en plaçant les nombres dans un objet à stocker à la fois dans un ensemble de hachage et dans une liste doublement liée. Lorsque vous supprimez un objet de l'ensemble de hachage, vous le supprimez également de la liste. Les réponses seront laissées dans la liste qui est maintenant de longueur k.
la source
Pour résoudre la question des 2 (et 3) nombres manquants, vous pouvez modifier
quickselect
, qui s'exécute en moyenneO(n)
et utilise une mémoire constante si le partitionnement est effectué sur place.Partitionnez l'ensemble par rapport à un pivot aléatoire
p
en partitionsl
, qui contiennent des nombres inférieurs au pivot, etr
, qui contiennent des nombres supérieurs au pivot.Déterminez dans quelles partitions se trouvent les 2 nombres manquants en comparant la valeur pivot à la taille de chaque partition (
p - 1 - count(l) = count of missing numbers in l
etn - count(r) - p = count of missing numbers in r
)a) S'il manque un numéro à chaque partition, utilisez l'approche de la différence des sommes pour trouver chaque numéro manquant.
(1 + 2 + ... + (p-1)) - sum(l) = missing #1
et((p+1) + (p+2) ... + n) - sum(r) = missing #2
b) Si une partition manque les deux numéros et la partition est vide, alors les numéros manquants sont soit
(p-1,p-2)
ou(p+1,p+2)
selon la partition qui manque les numéros.Si une partition manque de 2 numéros mais n'est pas vide, alors récursivement sur cette partition.
Avec seulement 2 nombres manquants, cet algorithme supprime toujours au moins une partition, de sorte qu'il conserve
O(n)
la complexité temporelle moyenne de la sélection rapide. De même, avec 3 numéros manquants, cet algorithme supprime également au moins une partition à chaque passage (car comme avec 2 numéros manquants, au plus une seule partition contiendra plusieurs numéros manquants). Cependant, je ne sais pas combien les performances diminuent lorsque davantage de nombres manquants sont ajoutés.Voici une implémentation qui n'utilise pas de partitionnement sur place, donc cet exemple ne répond pas à l'espace requis mais il illustre les étapes de l'algorithme:
Démo
la source
Voici une solution qui utilise k bits de stockage supplémentaire, sans astuces intelligentes et simplement. Temps d'exécution O (n), espace supplémentaire O (k). Juste pour prouver que cela peut être résolu sans lire d'abord la solution ou être un génie:
la source
(data [n - 1 - odd] % 2 == 1) ++odd;
?Pouvez-vous vérifier si chaque numéro existe? Si oui, vous pouvez essayer ceci:
si les nombres manquants le sont
x
,y
puis:Vous vérifiez donc la plage de
1
àmax(x)
et trouvez le nombrela source
max(x)
signifie, quandx
est un nombre?Peut-être que cet algorithme peut fonctionner pour la question 1:
Ou encore mieux:
Cet algorithme peut en effet être étendu pour deux nombres manquants. La première étape reste la même. Lorsque nous appelons GetValue avec deux nombres manquants, le résultat sera un
a1^a2
sont les deux nombres manquants. Disonsval = a1^a2
Maintenant, pour filtrer a1 et a2 de val, nous prenons n'importe quel bit défini dans val. Disons que le
ith
bit est défini sur val. Cela signifie que a1 et a2 ont une parité différente à laith
position du bit. Maintenant, nous faisons une autre itération sur le tableau d'origine et gardons deux valeurs xor. Un pour les nombres qui ont le ième bit et l'autre qui n'a pas le ième bit. Nous avons maintenant deux seaux de nombres, et sa garantiea1 and a2
se trouvera dans différents seaux. Maintenant, répétez la même chose que nous avons fait pour trouver un élément manquant sur chacun des seaux.la source
k=1
, non? Mais j'aime utiliserxor
plus de sommes, cela semble un peu plus rapide.Vous pouvez résoudre Q2 si vous avez la somme des deux listes et le produit des deux listes.
(l1 est l'original, l2 est la liste modifiée)
Nous pouvons l'optimiser car la somme d'une série arithmétique est n fois la moyenne des premier et dernier termes:
Maintenant, nous savons que (si a et b sont les nombres supprimés):
Ainsi, nous pouvons réorganiser pour:
Et multipliez:
Et réorganisez pour que le côté droit soit nul:
Ensuite, nous pouvons résoudre avec la formule quadratique:
Exemple de code Python 3:
Je ne connais pas la complexité des fonctions sqrt, reduction et sum, je ne peux donc pas calculer la complexité de cette solution (si quelqu'un le sait, veuillez commenter ci-dessous.)
la source
x1*x2*x3*...
?Pour Q2, c'est une solution qui est un peu plus inefficace que les autres, mais qui a toujours le temps d'exécution O (N) et prend de l'espace O (k).
L'idée est d'exécuter l'algorithme d'origine deux fois. Dans le premier, vous obtenez un nombre total qui est manquant, ce qui vous donne une limite supérieure des nombres manquants. Appelons ce numéro
N
. Vous savez que les deux nombres manquants vont résumerN
, donc le premier nombre ne peut être que dans l'intervalle[1, floor((N-1)/2)]
tandis que le second va être[floor(N/2)+1,N-1]
.Ainsi, vous bouclez à nouveau sur tous les numéros, en rejetant tous les numéros qui ne sont pas inclus dans le premier intervalle. Ceux qui le sont, vous gardez une trace de leur somme. Enfin, vous connaîtrez l'un des deux numéros manquants et, par extension, le second.
J'ai le sentiment que cette méthode pourrait être généralisée et peut-être plusieurs recherches exécutées en "parallèle" lors d'un seul passage sur l'entrée, mais je n'ai pas encore compris comment.
la source
Je pense que cela peut se faire sans équations et théories mathématiques complexes. Vous trouverez ci-dessous une proposition de solution de complexité en place et O (2n):
Hypothèses du formulaire d'entrée:
Nombre de numéros dans le sac = n
Nombre de nombres manquants = k
Les nombres dans le sac sont représentés par un tableau de longueur n
Longueur du tableau d'entrée pour l'algo = n
Les entrées manquantes dans le tableau (nombres sortis du sac) sont remplacées par la valeur du premier élément du tableau.
Par exemple. Au départ, le sac ressemble à [2,9,3,7,8,6,4,5,1,10]. Si 4 est retiré, la valeur de 4 deviendra 2 (le premier élément du tableau). Par conséquent, après avoir retiré 4 le sac ressemblera à [2,9,3,7,8,6,2,5,1,10]
La clé de cette solution consiste à baliser l'INDEX d'un nombre visité en annulant la valeur à cet INDEX lorsque le tableau est parcouru.
la source
Il existe un moyen général de généraliser des algorithmes de streaming comme celui-ci. L'idée est d'utiliser un peu de randomisation pour, espérons-le, «répartir» les
k
éléments en sous-problèmes indépendants, où notre algorithme d'origine résout le problème pour nous. Cette technique est utilisée, entre autres, dans la reconstruction de signaux clairsemés.a
de tailleu = k^2
.h : {1,...,n} -> {1,...,u}
. (Comme multiplier-shift )i
en1, ..., n
augmentationa[h(i)] += i
x
du flux d'entrée, décrémenteza[h(x)] -= x
.Si tous les nombres manquants ont été hachés dans des compartiments différents, les éléments non nuls du tableau contiendront désormais les nombres manquants.
La probabilité qu'une paire particulière soit envoyée au même compartiment, est moindre que
1/u
par définition d'une fonction de hachage universelle. Puisqu'il y a environ desk^2/2
paires, nous avons que la probabilité d'erreur est au plusk^2/2/u=1/2
. Autrement dit, nous réussissons avec une probabilité d'au moins 50%, et si nous augmentonsu
nous augmentons nos chances.Notez que cet algorithme prend des
k^2 logn
bits d'espace (nous avons besoin delogn
bits par compartiment de tableau.) Cela correspond à l'espace requis par la réponse de @Dimitris Andreou (en particulier l'exigence d'espace de factorisation polynomiale, qui se trouve également être randomisée.) Cet algorithme a également une constante le temps par mise à jour, plutôt que le tempsk
dans le cas des sommes de puissance.En fait, nous pouvons être encore plus efficaces que la méthode de la somme de puissance en utilisant l'astuce décrite dans les commentaires.
la source
xor
dans chaque godet, plutôt quesum
, si c'est plus rapide sur notre machine.k <= sqrt(n)
- au moins siu=k^2
? Supposons que k = 11 et n = 100, alors vous auriez 121 compartiments et l'algorithme finirait par ressembler à un tableau de 100 bits que vous cochez lorsque vous lisez chaque # du flux. Augmenteru
améliore les chances de succès, mais il y a une limite à ce que vous pouvez augmenter avant de dépasser la contrainte d'espace.n
beaucoup plus grand quek
, je pense, mais vous pouvez réellement obtenir de l'espacek logn
avec une méthode très similaire au hachage décrit, tout en ayant des mises à jour constantes. Il est décrit dans gnunet.org/eppstein-set-reconciliation , comme la méthode de la somme des pouvoirs, mais en gros, vous hachez vers 'deux des k' compartiments avec une fonction de hachage forte comme le hachage de tabulation, ce qui garantit qu'un certain compartiment n'aura qu'un seul élément . Pour décoder, vous identifiez ce compartiment et supprimez l'élément de ses deux compartiments, ce qui libère (probablement) un autre compartiment et ainsi de suiteUne solution très simple au Q2 à laquelle je suis surpris, personne n'a déjà répondu. Utilisez la méthode du T1 pour trouver la somme des deux nombres manquants. Notons-le par S, alors l'un des nombres manquants est plus petit que S / 2 et l'autre plus grand que S / 2 (duh). Additionnez tous les nombres de 1 à S / 2 et comparez-le au résultat de la formule (de manière similaire à la méthode du premier trimestre) pour trouver le plus bas entre les nombres manquants. Soustrayez-le de S pour trouver le plus grand nombre manquant.
la source
Très beau problème. Je choisirais d'utiliser une différence définie pour Qk. De nombreux langages de programmation sont même pris en charge, comme dans Ruby:
Ce n'est probablement pas la solution la plus efficace mais c'est celle que j'utiliserais dans la vraie vie si j'étais confronté à une telle tâche dans ce cas (limites connues, limites basses). Si l'ensemble de nombres était très grand, je considérerais un algorithme plus efficace, bien sûr, mais jusque-là, la solution simple me suffirait.
la source
Vous pouvez essayer d'utiliser un filtre Bloom . Insérez chaque numéro dans le sac dans la fleur, puis répétez sur l'ensemble complet de 1 k jusqu'à ce que chacun ne soit pas trouvé. Cela peut ne pas trouver la réponse dans tous les scénarios, mais peut être une bonne solution.
la source
Je prendrais une approche différente de cette question et sonderais l'intervieweur pour plus de détails sur le problème plus vaste qu'il essaie de résoudre. Selon le problème et les exigences qui l'entourent, la solution basée sur un ensemble évident pourrait être la bonne chose et l'approche générer-une-liste-et-choisir-après-pas.
Par exemple, il se peut que l'intervieweur envoie des
n
messages et ait besoin de savoir cek
qui n'a pas donné lieu à une réponse et doit le savoir en un minimum de temps d'horloge murale après len-k
arrivée de e réponse. Disons également que la nature du canal de message est telle que même en cours d'exécution, il y a suffisamment de temps pour effectuer un traitement entre les messages sans avoir d'impact sur le temps nécessaire pour produire le résultat final après l'arrivée de la dernière réponse. Ce temps peut être utilisé pour insérer une facette d'identification de chaque message envoyé dans un ensemble et le supprimer à mesure que chaque réponse correspondante arrive. Une fois la dernière réponse arrivée, la seule chose à faire est de supprimer son identifiant de l'ensemble, ce qui, dans les implémentations typiques, prendO(log k+1)
. Après cela, l'ensemble contient la liste desk
éléments manquants et aucun traitement supplémentaire n'est à effectuer.Ce n'est certainement pas l'approche la plus rapide pour le traitement par lots de sacs de nombres pré-générés car tout fonctionne
O((log 1 + log 2 + ... + log n) + (log n + log n-1 + ... + log k))
. Mais cela fonctionne pour n'importe quelle valeur dek
(même si elle n'est pas connue à l'avance) et dans l'exemple ci-dessus, elle a été appliquée de manière à minimiser l'intervalle le plus critique.la source
Vous pouvez motiver la solution en y réfléchissant en termes de symétries (groupes, en langage mathématique). Peu importe l'ordre de l'ensemble des nombres, la réponse doit être la même. Si vous allez utiliser des
k
fonctions pour aider à déterminer les éléments manquants, vous devriez penser aux fonctions qui ont cette propriété: symétrique. La fonctions_1(x) = x_1 + x_2 + ... + x_n
est un exemple de fonction symétrique, mais il y en a d'autres de degré supérieur. En particulier, considérons les fonctions symétriques élémentaires . La fonction symétrique élémentaire du degré 2 ests_2(x) = x_1 x_2 + x_1 x_3 + ... + x_1 x_n + x_2 x_3 + ... + x_(n-1) x_n
la somme de tous les produits de deux éléments. De même pour les fonctions élémentaires symétriques de degré 3 et supérieur. Ils sont évidemment symétriques. En outre, il s'avère que ce sont les éléments constitutifs de toutes les fonctions symétriques.Vous pouvez construire les fonctions symétriques élémentaires au fur et à mesure en notant cela
s_2(x,x_(n+1)) = s_2(x) + s_1(x)(x_(n+1))
. Une réflexion plus approfondie devrait vous convaincre de celas_3(x,x_(n+1)) = s_3(x) + s_2(x)(x_(n+1))
et ainsi de suite, afin qu'ils puissent être calculés en un seul passage.Comment savoir quels éléments manquaient dans le tableau? Pensez au polynôme
(z-x_1)(z-x_2)...(z-x_n)
. Il évalue0
si vous entrez l'un des nombresx_i
. L'élargissement du polynôme, vous obtenezz^n-s_1(x)z^(n-1)+ ... + (-1)^n s_n
. Les fonctions symétriques élémentaires apparaissent ici aussi, ce qui n'est vraiment pas surprenant, car le polynôme devrait rester le même si nous appliquons une permutation aux racines.Nous pouvons donc construire le polynôme et essayer de le factoriser pour déterminer quels nombres ne sont pas dans l'ensemble, comme d'autres l'ont mentionné.
Enfin, si nous sommes préoccupés par le débordement de la mémoire avec de grands nombres (le nième polynôme symétrique sera de l'ordre
100!
), nous pouvons faire ces calculsmod p
oùp
est un nombre premier supérieur à 100. Dans ce cas, nous évaluons le polynômemod p
et constatons qu'il évalue à nouveau à0
lorsque l'entrée est un nombre dans l'ensemble, et il évalue à une valeur non nulle lorsque l'entrée est un nombre non dans l'ensemble. Cependant, comme d'autres l'ont souligné, pour extraire les valeurs du polynôme dans le temps qui dépendk
, nonN
, nous devons factoriser le polynômemod p
.la source
Encore une autre façon utilise le filtrage de graphe résiduel.
Supposons que nous ayons les numéros 1 à 4 et que 3 soit manquant. La représentation binaire est la suivante,
1 = 001b, 2 = 010b, 3 = 011b, 4 = 100b
Et je peux créer un organigramme comme celui-ci.
Notez que le graphe de flux contient x nœuds, tandis que x est le nombre de bits. Et le nombre maximum d'arêtes est (2 * x) -2.
Ainsi, pour un entier 32 bits, il faudra un espace O (32) ou O (1).
Maintenant, si je supprime la capacité de chaque nombre à partir de 1,2,4, il me reste un graphique résiduel.
Enfin, je vais exécuter une boucle comme la suivante,
Maintenant, le résultat est en
result
contient des nombres qui ne manquent pas également (faux positif). Mais le k <= (taille du résultat) <= n lorsqu'ilk
manque des éléments.Je vais parcourir la liste donnée une dernière fois pour marquer le résultat manquant ou non.
La complexité temporelle sera donc O (n).
Enfin, il est possible de réduire le nombre de faux positifs (et l'espace requis) en prenant des nœuds
00
,01
,11
, au10
lieu de simplement0
et1
.la source
Vous auriez probablement besoin d'éclaircissements sur ce que signifie O (k).
Voici une solution triviale pour k arbitraire: pour chaque v de votre ensemble de nombres, accumulez la somme de 2 ^ v. À la fin, boucle i de 1 à N. Si la somme AND au niveau du bit avec 2 ^ i est nulle, alors i est manquant. (Ou numériquement, si le plancher de la somme divisée par 2 ^ i est pair. Ou
sum modulo 2^(i+1)) < 2^i
.)Facile, non? O (N) temps, O (1) de stockage, et il prend en charge arbitraire k.
Sauf que vous calculez d'énormes nombres qui, sur un ordinateur réel, nécessiteraient chacun un espace O (N). En fait, cette solution est identique à un vecteur de bits.
Vous pourriez donc être intelligent et calculer la somme et la somme des carrés et la somme des cubes ... jusqu'à la somme de v ^ k, et faire le calcul de fantaisie pour extraire le résultat. Mais ce sont aussi de grands nombres, ce qui pose la question: de quel modèle de fonctionnement abstrait parlons-nous? Combien tient dans l'espace O (1) et combien de temps faut-il pour résumer les nombres de la taille dont vous avez besoin?
la source
Voici une solution qui ne repose pas sur des mathématiques complexes comme le font les réponses de sdcvvc / Dimitris Andreou, ne change pas le tableau d'entrée comme l'ont fait caf et le colonel Panic, et n'utilise pas le jeu de bits de taille énorme comme Chris Lercher, JeremyP et beaucoup d'autres l'ont fait. Fondamentalement, j'ai commencé avec l'idée de Svalorzen / Gilad Deutch pour Q2, je l'ai généralisée au cas courant Qk et implémentée en Java pour prouver que l'algorithme fonctionne.
L'idée
Supposons que nous ayons un intervalle arbitraire I dont nous savons seulement qu'il contient au moins un des nombres manquants. Après un passage à travers le réseau d'entrée, ne regardant que les chiffres de I , on peut obtenir à la fois la somme S et la quantité Q de chiffres manquants I . Nous faisons cela en décrémentant simplement la longueur de I à chaque fois que nous rencontrons un nombre de I (pour obtenir Q ) et en diminuant la somme pré-calculée de tous les nombres de I par ce nombre rencontré à chaque fois (pour obtenir S ).
Maintenant , nous regardons S et Q . Si Q = 1 , cela signifie que alors je ne compte qu'un des numéros manquants, et ce nombre est clairement S . Nous marquons I comme terminé (il est appelé "sans ambiguïté" dans le programme) et le laissons de côté. D'autre part, si Q> 1 , on peut calculer la moyenne A = S / Q des nombres manquants contenus dans I . Comme tous les nombres sont distincts, au moins l' un de ces nombres est strictement inférieur à A et au moins un est strictement supérieur à un . Maintenant, nous avons divisé I en Aen deux intervalles plus petits dont chacun contient au moins un nombre manquant. Notez que peu importe à quels intervalles nous attribuons A au cas où il s'agit d'un entier.
Nous faisons la passe de tableau suivante calculant S et Q pour chacun des intervalles séparément (mais dans la même passe) et après cela, marquons les intervalles avec Q = 1 et divisons les intervalles avec Q> 1 . Nous continuons ce processus jusqu'à ce qu'il n'y ait pas de nouveaux intervalles "ambigus", c'est-à-dire que nous n'avons rien à diviser car chaque intervalle contient exactement un nombre manquant (et nous connaissons toujours ce nombre parce que nous connaissons S ). Nous partons du seul intervalle "plage entière" contenant tous les nombres possibles (comme [1..N] dans la question).
Analyse de la complexité du temps et de l'espace
Le nombre total de passes p que nous devons effectuer jusqu'à l'arrêt du processus n'est jamais supérieur au nombre manquant de k . L'inégalité p <= k peut être rigoureusement démontrée. D'autre part, il existe également une borne supérieure empirique p <log 2 N + 3 qui est utile pour les grandes valeurs de k . Nous devons effectuer une recherche binaire pour chaque numéro du tableau d'entrée pour déterminer l'intervalle auquel il appartient. Cela ajoute le multiplicateur log k à la complexité temporelle.
Au total, la complexité temporelle est O (N ᛫ min (k, log N) ᛫ log k) . Notez que pour les grands k , c'est nettement mieux que celui de la méthode de sdcvvc / Dimitris Andreou, qui est O (N ᛫ k) .
Pour son travail, l'algorithme nécessite O (k) d' espace supplémentaire pour le stockage à la plupart des k intervalles, ce qui est nettement meilleur que O (N) dans les solutions de "bits".
Implémentation Java
Voici une classe Java qui implémente l'algorithme ci-dessus. Il renvoie toujours un tableau trié de nombres manquants. En plus de cela, il ne nécessite pas le nombre k manquant car il le calcule lors de la première passe. L'ensemble des nombres est donné par les paramètres
minNumber
etmaxNumber
(par exemple 1 et 100 pour le premier exemple de la question).Par souci d'équité, cette classe reçoit des données sous forme d'
NumberBag
objets.NumberBag
ne permet pas la modification du tableau et l'accès aléatoire et compte également le nombre de fois que le tableau a été demandé pour une traversée séquentielle. Il est également plus approprié pour les tests de grands tableaux queIterable<Integer>
parce qu'il évite la mise en boîte deint
valeurs primitives et permet d'envelopper une partie d'un grandint[]
pour une préparation de test pratique. Il n'est pas difficile de remplacer, si vous le souhaitez,NumberBag
parint[]
ou deIterable<Integer>
taper lafind
signature, en y changeant deux boucles for en boucles foreach.Les tests
Des exemples simples démontrant l'utilisation de ces classes sont donnés ci-dessous.
Les tests de grande baie peuvent être effectués de cette façon:
Essayez-les sur Ideone
la source
Je crois que j'ai un algorithme de
O(k)
temps et d'O(log(k))
espace, étant donné que vous avez les fonctionsfloor(x)
etlog2(x)
pour les entiers arbitrairement grands disponibles:Vous avez un
k
entier long -bit (d'où l'log8(k)
espace) où vous ajoutez lex^2
, où x est le prochain numéro que vous trouvez dans le sac:s=1^2+2^2+...
cela prend duO(N)
temps (ce qui n'est pas un problème pour l'intervieweur). À la fin, vous obtenezj=floor(log2(s))
le plus grand nombre que vous recherchez. Ensuites=s-j
et vous recommencez ce qui précède:Maintenant, vous n'avez généralement pas de fonctions floor et log2 pour les
2756
entiers -bit mais à la place pour les doubles. Donc? Simplement, pour chaque 2 octets (ou 1, ou 3 ou 4), vous pouvez utiliser ces fonctions pour obtenir les nombres souhaités, mais cela ajoute unO(N)
facteur à la complexité temporellela source
Cela peut sembler stupide, mais, dans le premier problème qui vous est présenté, vous devrez voir tous les nombres restants dans le sac pour les additionner afin de trouver le nombre manquant à l'aide de cette équation.
Donc, puisque vous pouvez voir tous les chiffres, recherchez simplement le numéro manquant. Il en va de même lorsque deux numéros sont manquants. Assez simple je pense. Inutile d'utiliser une équation lorsque vous voyez les nombres restants dans le sac.
la source
Je pense que cela peut être généralisé comme ceci:
Notons S, M comme valeurs initiales pour la somme des séries arithmétiques et de la multiplication.
Je devrais penser à une formule pour calculer cela, mais ce n'est pas le point. Quoi qu'il en soit, s'il manque un numéro, vous avez déjà fourni la solution. Cependant, si deux nombres manquent alors, notons la nouvelle somme et le multiple total par S1 et M1, qui seront les suivants:
Puisque vous connaissez S1, M1, M et S, l'équation ci-dessus est résoluble pour trouver a et b, les nombres manquants.
Maintenant, pour les trois numéros manquants:
Maintenant, votre inconnu est 3 alors que vous n'avez que deux équations à résoudre.
la source
M1 = M / (a * b)
(voir cette réponse ). Ensuite, cela fonctionne bien.Je ne sais pas si c'est efficace ou pas mais je voudrais suggérer cette solution.
4. Obtenez la somme des Nos manquants avec votre approche habituelle du formule de somme diff et disons que le diff est d.
Exécutez maintenant une boucle pour obtenir les paires possibles (p, q) qui se trouvent toutes les deux dans [1, 100] et additionnent à d.
Lorsqu'une paire est obtenue, vérifiez si (résultat de 3) XOR p = q et si oui, nous avons terminé.
Veuillez me corriger si je me trompe et commenter également la complexité du temps si cela est correct
la source
On peut faire le Q1 et le Q2 en O (log n) la plupart du temps.
Supposons que notre se
memory chip
compose d'un tableau den
nombre detest tubes
. Et un certain nombrex
dans le tube à essai est représenté parx
milliliter
du liquide chimique.Supposons que notre processeur soit un
laser light
. Lorsque nous allumons le laser, il traverse tous les tubes perpendiculairement à sa longueur. Chaque fois qu'il traverse le liquide chimique, la luminosité est réduite de1
. Et passer la lumière à une certaine marque de millilitre est une opération deO(1)
.Maintenant, si nous allumons notre laser au milieu du tube à essai et obtenons la sortie de luminosité
n/2
.n/2
. On peut également vérifier si la luminosité est réduite de1
ou2
. s'il est réduit de1
alors un nombre manquant est plus petit quen/2
et l'autre est plus grand quen/2
. Si elle est réduite de, les2
deux nombres sont inférieurs àn/2
.Nous pouvons répéter le processus ci-dessus encore et encore en réduisant notre domaine de problème. À chaque étape, nous réduisons de moitié le domaine. Et enfin, nous pouvons arriver à notre résultat.
Des algorithmes parallèles qui méritent d'être mentionnés (car ils sont intéressants),
O(log^3 n)
temps. Et puis le nombre manquant peut être trouvé par recherche binaire dans leO(log n)
temps.n
processeurs, chaque processus peut vérifier l'une des entrées et définir un indicateur qui identifie le nombre (commodément dans un tableau). Et à l'étape suivante, chaque processus peut vérifier chaque indicateur et finalement sortir le nombre qui n'est pas signalé. L'ensemble du processus prendra duO(1)
temps. Il aO(n)
besoin d'espace / mémoire supplémentaire .Notez que les deux algorithmes parallèles fournis ci-dessus peuvent avoir besoin d'espace supplémentaire comme mentionné dans le commentaire .
la source
O(logn)
sur un ordinateur.N
, et plus deO(N)
temps (en termes de dépendanceN
), que nous avons l'intention de faire mieux que.