12
votes

Quelle est la valeur o pour la sélection aléatoire naïve de l'ensemble fini?

Cette question sur obtenir des valeurs aléatoires d'un ensemble fini got m'a pensé ... < / p>

Il est assez courant que les gens veulent récupérer x valeurs uniques à partir d'un ensemble de valeurs y. Par exemple, je souhaite peut-être traiter une main d'un jeu de cartes. Je veux 5 cartes et je veux qu'ils soient tous uniques.

Maintenant, je peux le faire naïvement, en choisissant une carte aléatoire 5 fois et réessayez chaque fois que je reçois un duplicata, jusqu'à ce que je reçoive 5 cartes. Ce n'est pas si génial, cependant, pour un grand nombre de valeurs à partir de grands ensembles. Si je voulais 999 9999 valeurs d'un ensemble de 1 000 000, par exemple, cette méthode est très mauvaise.

La question est: quelle est la mauvaise? Je cherche quelqu'un pour expliquer une valeur O (). Obtenir le Xème numéro prendra vos tentatives ... mais combien? Je sais comment comprendre cette valeur pour une valeur donnée, mais existe-t-il un moyen simple de généraliser ceci pour toute la série et d'obtenir une valeur O () O ()?

(la question n'est pas: "Comment puis-je m'améliorer cela?" Parce que c'est relativement facile à réparer, et je suis sûr que cela a été couvert plusieurs fois ailleurs.)


5 commentaires

Je vous suggère de repousser la question comme Big-o au lieu de la valeur O, car c'est la balise sous laquelle la majorité des questions sur la notation Big-O se trouvent.


Merci, fait. (Pour une raison quelconque, je ne me souvenais pas de la bonne balise.)


Malheureusement, la réponse actuellement acceptée d'Ambuoroko est fausse. Plus de commentaires sur la réponse elle-même.


J'ai retiré "réponse acceptée" pour le moment, jusqu'à ce que je puisse lire et évaluer les autres réponses.


Juste pour être précis, la question devrait poser une question sur l'estimation moyenne des performances. Pour n'importe quelle réponse O () et constante de temps, Constant TC on peut toujours fournir une séquence de test qui prendra plus de temps que TC * O ().


8 Réponses :


2
votes

Votre question actuelle est effectivement beaucoup plus intéressante que ce que j'ai répondu (et plus difficile). Je n'ai jamais été bon à Statistitcs (et cela fait longtemps que je l'ai fait), mais intuitivement, je dirais que la complexité du temps d'exécution de cet algorithme serait probablement quelque chose comme une exponentielle. Tant que le nombre d'éléments cueillés est suffisamment petit par rapport à la taille de la matrice, le taux de collision sera si petit qu'il sera proche du temps linéaire, mais à un moment donné, le nombre de collisions augmentera probablement rapidement et la course -Te temps va tomber dans le drain.

Si vous voulez prouver cela, je pense que vous devriez faire quelque chose de modérément intelligent avec le nombre de collisions attendu en fonction du nombre recherché d'éléments. Il pourrait être possible de faire à l'induction également, mais je pense que cela nécessiterait plus d'intelligence que la première alternative.

Edit: Après avoir donné une idée, voici ma tentative:

Compte tenu d'un tableau de M éléments et recherchez n éléments aléatoires et différents. Il est alors facile de voir que lorsque nous voulons choisir le i ème élément, les chances de choisir un élément que nous avons déjà visitées sont (i-1) / m . C'est alors le nombre attendu de collisions pour ce choix particulier. Pour cueillir des éléments n , le nombre de collisions attendu sera la somme du nombre de collisions attendues pour chaque choix. Nous branchons cela dans Wolfram Alpha (Somme (I-1) / M, I = 1 à N) et nous obtenons la réponse (n ** 2 - N) / 2M . Le nombre moyen de pics pour notre algorithme naïf est alors n + (n ** 2-N) / 2m .

Sauf si ma mémoire ne me manque complètement (qui est entièrement possible, effectivement), cela donne une période moyenne d'exécution O (n ** 2) . .


2 commentaires

Il a expressément déclaré que la question n'était pas la manière dont il pourrait améliorer l'algorithme. La question était de savoir quelle est la complexité de la solution naïve, dans un sens de la Big-o. Veuillez lire toute la question avant de répondre.


Oui, je me suis rendu compte que juste après que j'ai posté ma réponse. Cette question était plus intéressante de toute façon, j'ai donc tué la réponse originale et l'a remplacée par quelque chose de pertinent.



2
votes

Il y a un bel algorithme O (n) pour cela. Cela va comme suit. Dites que vous avez n articles, à partir desquels vous voulez choisir M Articles. Je suppose que la fonction rand () donne un nombre réel aléatoire entre 0 et 1. Voici l'algorithme:

items_left=n
items_left_to_pick=m
for j=1,...,n
    if rand()<=(items_left_to_pick/items_left)
        Pick item j
        items_left_to_pick=items_left_to_pick-1
    end
    items_left=items_left-1
end


1 commentaires

Asker ne demande pas une meilleure façon, mais pour une caractérisation de la complexité de temps de l'approche actuelle (mauvaise). BTW, une preuve d'exactitude de cette (bonne) approche est à Knuth, je pense.



3
votes

Si vous êtes prêt à supposer que votre générateur de nombres aléatoires trouvera toujours une valeur unique avant de revenir à une valeur de cyclage à une valeur précédemment vue pour un tirage donné, cet algorithme est O (m ^ 2), où M est le Nombre de valeurs uniques que vous dessinez.

Donc, si vous dessinez des valeurs M à partir d'un ensemble de n valeurs N, la 1ère valeur nécessitera de dessiner au plus 1 pour obtenir une valeur unique. Le 2e requiert au plus 2 (vous voyez la 1ère valeur, puis une valeur unique), le 3ème 3, ... le MTH m. Par conséquent, vous avez besoin de 1 + 2 + 3 + ... + m = [m * (m + 1)] / 2 = (m ^ 2 + m) / 2 tirages. C'est O (m ^ 2). P>

Sans cette hypothèse, je ne sais pas comment vous pouvez même garantir l'algorithme terminer. C'est tout à fait possible (surtout avec un générateur de nombres pseudo-aléatoires pouvant avoir un cycle), que vous continuerez à voir les mêmes valeurs encore et encore et que vous n'allez jamais à une autre valeur unique. p>

== Edit == p>

pour le cas moyen: p>

sur votre premier tirage, vous ferez exactement 1 tirage au sort. Sur votre 2e tirage, vous vous attendez à faire 1 (le tirage au sort fructueux) + 1 / N (le dessin "partiel" qui représente vos chances de dessiner une répétition) Sur votre 3ème dessin, vous vous attendez à faire 1 (le tirage réussi) + 2 / N (le dessin "partiel" ...) ... Sur votre mth dessin, vous vous attendez à faire des dessins de 1 + (m-1) / n. P>

Ainsi, vous ferez 1 + (1 + 1 / N) + (1 + 2 / N) + (1 + 2 / N) + ... + (1 + (m-1) / n) attire tout à fait dans le boîtier moyen. P>

Ceci est égal à la somme de i = 0 à (m-1) de [1 + i / n ]. Notons cette somme (1 + I / N, I, 0, M-1). P>

ALORS: P>

sum(1 + i/n, i, 0, m-1) = sum(1, i, 0, m-1) + sum(i/n, i, 0, m-1)
                        = m + sum(i/n, i, 0, m-1)
                        = m + (1/n) * sum(i, i, 0, m-1)
                        = m + (1/n)*[(m-1)*m]/2
                        = (m^2)/(2n) - (m)/(2n) + m 


14 commentaires

Je ne suis pas sûr de comprendre le premier paragraphe. Pourquoi dites-vous O (m ^ 2)? Je ne vois pas la logique pour cela.


Si vous prenez 1 + 2 + 3 + ... + m (résumez tous les nombres de 1 à m), vous obtenez [m * (m + 1)] / 2 = (m ^ 2 + m) / 2. Donc, lorsque vous déposez toutes les constantes et les termes de la plus petite commande, vous obtenez O (m ^ 2)


De plus, je ne pense pas que la première hypothèse est valide. Si vous excluez des chiffres, ce n'est pas vraiment aléatoire. Les nombres aléatoires pourraient, imaginer, incluent une série de seulement 9 (comme le dessin animé de Dilbert.) Pour dire que frapper un nombre deux fois avant d'obtenir le bon n'est autorisé à éviter le problème. Ce n'est pas parce qu'un nombre a été touché deux fois, cela ne signifie pas que le générateur a commencé à répéter un cycle. En particulier, je suis concerné par un générateur idéal, sans cyclisme.


Il est définitivement vrai qu'il n'ya aucune garantie que l'algorithme sera terminé. Mais dans le cas moyen, ce sera le cas plus intéressant.


Je suis à peu près terminé avec une preuve du comportement moyen des cas. Je vais éditer ma réponse pour y inclure une fois que je me suis convaincu que c'est correct. :)


Si le générateur aléatoire ne génère que des nombres uniques, il devrait être O (n). Le deuxième nombre généré est garanti de ne pas être identique à celui précédent, de sorte que votre séquence de choix possibles serait de 1 + 1 + 1 + ... + 1 m de fois. La partie intéressante de la question, cependant, était le fait que des collisions peuvent survenir au hasard et que choisir "999 999 valeurs d'un ensemble de 1 000 000" entraîneront une très longue période de fonctionnement.


Alderath - J'allais à l'origine que le générateur de nombres aléatoires a généré des nombres uniques pour une étape particulière du problème. Ainsi, cela pourrait choisir "9" pour le premier numéro, puis "9" pour le 2e numéro, mais après avoir choisi "9" pour le deuxième numéro, vous ne pouvez pas voir "9" à nouveau jusqu'à ce que le 3ème nombre, etc., sinon il y avait un problème pratique avec une boucle infinie. Voir ma réponse modifiée maintenant pour une analyse moyenne du temps d'exécution.


Cela me semble correct. Je ne vois rien de mal avec les maths.


J'aime cette preuve. Essentiellement la même chose que mon argument ci-dessus, mais indiqué beaucoup plus clairement. Incidemment, je dois admettre que je m'attendais à ce que l'affaire moyenne soit pire que n au carré.


Malheureusement, cette réponse est également fausse - le terme 1 + i / n est incorrect. Pensez-y - quand je = n, vous ferez assez de plus que les 2 tirons que la formule prédit. Voir la réponse d'Accipitridae pour la bonne solution.


Ce n'est pas la solution elle-même qui ne va pas. C'est l'hypothèse que le générateur aléatoire fonctionnera de manière à ce que, à chaque étape, il peut générer des éléments qui sont égaux aux éléments précédemment sélectionnés une fois que cela ne va pas. Cela a un impact crucial sur le résultat. La question portait sur un générateur aléatoire idéal et le résultat de cette réponse est faux dans ce cas. Deuxièmement, si vous avez en quelque sorte un générateur NR aléatoire qui fonctionne de cette façon, cela n'a aucun sens pour permettre aux collisions. Ce type de générateur pourrait facilement accéder à la non-collision résultant d'une complexité de temps de 1 + 1 + 1 + ... + 1 = m


Je ressemble cependant au commentaire indiquant que si les générateurs de nombres aléatoires de mauvais pseudo sont utilisés, l'algorithme naïf pourrait boucler infiniment, si M et N sont presque égaux. Ce point est pertinent pour la question, même si le reste de l'analyse est faux.


Pour le moment, je retire la "réponse acceptée" de cette réponse jusqu'à ce que je puisse évaluer les autres réponses à la lumière de ces commentaires.


(J'espère aussi que je ne suis pas hors de ma ligue, math-sage. Je ferai de mon mieux pour évaluer tout correctement.)



4
votes

Si vous avez déjà choisi I valeurs I, la probabilité que vous choisissez une nouvelle à partir d'un ensemble de valeurs Y est

y (ln(y) - ln(y-x)) + O(y/(y-x)).


2 commentaires

Vous êtes complètement correct. Mon commentaire était basé sur une faute stupide par moi mal interpréter les supports comme Y (ln (y) - ln (y-x) + o (y / (y-x))). J'envisage de supprimer mon commentaire précédent afin de ne pas discréditer de manière incorrecte votre résultat, ce qui est en fait correct. Devrais-je faire ça?


Alderath, n'hésitez pas à changer ou supprimer des commentaires que vous n'êtes pas à l'aise. Ma dernière section indique que le résultat ne contredit pas vos estimations. Étant donné que les résultats ne sont pas forts, je envisage également de supprimer ou de remplacer cette section. Après tout, l'OP était principalement intéressé par le cas où X est grand.



5
votes

Variables

n = la quantité totale d'éléments dans l'ensemble
m = la quantité de valeurs uniques à extraire de l'ensemble des éléments N
d (i) = la quantité attendue des essais nécessaires pour atteindre une valeur à l'étape i
i = dénote une étape spécifique. I ∈ [0, N-1]
t (m, n) = montant total attendu d'essais pour la sélection d'éléments uniques à partir d'un ensemble d'éléments N à l'aide de l'algorithme naïf

raisonnement

La première étape, i = 0, est triviale. Peu importe la valeur que nous choisissons, nous en avons un unique à la première tentative. Par conséquent:

D (0) = 1

Dans la deuxième étape, I = 1, nous avons au moins besoin d'essayer (l'essai où nous choisissons une valeur unique valide). En plus de cela, il y a une chance que nous choisissions la mauvaise valeur. Cette chance est (quantité d'éléments préalablement choisis) / (quantité totale d'éléments). Dans ce cas 1 / N. Dans le cas où nous avons choisi le mauvais article, il y a une chance de 1 / n que nous puissions choisir le mauvais article. Multiplier ceci par 1 / N, car c'est la probabilité combinée que nous choisissons de mauvais moments, donne (1 / N) 2 . Pour comprendre cela, il est utile de dessiner un Arbre de décision . Après avoir choisi un article non unique deux fois, il y a une probabilité que nous le ferons à nouveau. Cela se traduit par l'ajout de (1 / N) 3 au total des quantités attendues d'essais à l'étape i = 1. Chaque fois que nous choisissons le mauvais numéro, il y a une chance que nous puissions choisir le mauvais numéro. Il en résulte:

D (1) = 1 + 1 / N + (1 / N) 2 + (1 / N) 3 + (1 / N) 4 + ...

De même, dans le général I: Th Step, la chance de choisir le mauvais article dans un seul choix est I / N, ce qui compte:

d (i) = 1 + i / n + (i / n) 2 + (i / n) 3 + (i / n) 4 + ... =
= somme ((i / n) k ), où k ∈ [0, ∞]

Ceci est un séquence géométrique et il est donc facile de calculer sa somme:

d (i) = (1 - i / n) -1

La complexité globale est ensuite calculée en additionnant la quantité attendue des essais à chaque étape:

t (m, n) = somme (d (i)), où je ∈ [0, m-1] =
= 1 + (1 - 1 / N) -1 + (1 - 2 / n) -1 + (1 - 3 / n) -1 ... + (1 - (m-1) / n) -1

Extension des fractions dans la série ci-dessus par N, nous obtenons:

T (m, n) = N / N + N / (N-1) + N / (N-2) + N / (N-3) + ... + N / (N-M + 2 ) + N / (N-M + 1)

Nous pouvons utiliser le fait que:

N / N ≤ N / (N-1) ≤ N / (N-2) ≤ N / (N-3) ≤ ... ≤ N / (N-M + 2) ≤ N / (N- m + 1)

Puisque la série a m termes, et chaque terme satisfait à l'inégalité ci-dessus, nous obtenons:

T (m, n) ≤ N / (N-M + 1) + N / (N-M + 1) + N / (N-M + 1) + N / (N-M + 1) + ... + N / (N-M + 1) + N / (N-M + 1) =
= M * N / (N-M + 1)

Cela pourrait être (et probablement) possible d'établir une limite supérieure légèrement plus stricte en utilisant une technique pour évaluer la série au lieu de la bornisation par la méthode approximative de (quantité de termes) * (terme plus élevé)

Conclusion

Cela signifierait que la commande Big-o est o (m * n / (n-m + 1)) . Je ne vois aucun moyen possible de simplifier cette expression de la façon dont c'est.

Retour en arrière à la suite de Vérifiez si cela a du sens , nous voyons que, si N est constant, et m est plus proche et plus proche de N, les résultats augmenteront rapidement, car le dénominateur augmente, car le dénominateur devient rapidement. très petit. C'est ce que nous attendions, si nous considérons par exemple l'exemple donné dans la question de la sélection de «999 999 valeurs d'un ensemble de 1 000 000». Si nous, que nous soyons constants et n grandir vraiment, vraiment grand, la complexité convergera vers O (m) dans la limite n → ∞. C'est aussi ce que nous attendions, car tout en choisissant un nombre constant d'éléments d'une taille "proche de" de taille infini, la probabilité de choisir une valeur précédemment choisie est essentiellement 0. I.e. Nous avons besoin de m essais indépendamment de n car il n'y a pas de collision.


1 commentaires

Big-o n'est pas tout à fait ce que vous avez calculé; qui décrit la croissance du pire des cas. Dans le pire des cas, un générateur de nombres aléatoires pessimaux n'est pas très aléatoire et que vous perdez. Des algorithmes aléatoires doivent être décrits par un comportement de cas moyen / attendu, et vous l'avez fait splendidement.



0
votes

Avant de pouvoir répondre à cette question en détail, permet de définir le cadre. Supposons que vous ayez une collection {A1, A2, ..., un} d'objets distincts, et souhaitez choisir des objets distincts de cet ensemble, de sorte que la probabilité d'un objet donné AJ apparaissant dans le résultat est égal à tous les objets. .

Si vous avez déjà choisi K articles et choisissez radieusement un élément de l'ensemble complet {A1, A2, ..., an}, la probabilité que l'élément n'a pas été cueilli avant IS (nk) / n. Cela signifie que le nombre d'échantillons que vous devez prendre avant d'obtenir un nouvel objet est (en supposant l'indépendance de l'échantillonnage aléatoire) géométrique avec paramètre (nk) / n. Ainsi, le nombre attendu d'échantillons pour obtenir un élément supplémentaire est N / (N-K), qui est proche de 1 si k est petit par rapport à n.

conclusion, si vous avez besoin de M d'objets uniques, sélectionné au hasard, cet algorithme vous donne

N / N + N / (N-1) + N / (N-2) + n / (n-3) + .... + n / (n- (m-1))

qui, comme l'a montré ALDERATH, peut être estimé par

m * N / (N-M + 1).

Vous pouvez voir un peu plus de cette formule: * Le nombre attendu d'échantillons pour obtenir un nouvel élément unique augmente car le nombre d'objets déjà choisi augmente (ce qui semble logique). * Vous pouvez vous attendre à des temps de calcul vraiment longs lorsque M est proche de N, surtout si N est grand.

Pour obtenir M membres uniques de l'ensemble, utilisez une variante de Algorithme de David Knuth pour obtenir une permutation aléatoire. Ici, je suppose que les n objets sont stockés dans un tableau. xxx

ici, Randint échantillonne un entier de {i, i + 1, ... n} et l'échange retourne deux membres de la matrice. Vous n'avez besoin que de mélanger m de fois, de sorte que le temps de calcul est O (m), alors que la mémoire est O (n) (bien que vous puissiez l'adapter pour sauvegarder uniquement les entrées telles que [i] <> i, ce qui donnerait vous O (m) sur le temps et la mémoire, mais avec des constantes plus élevées).


0 commentaires

2
votes

Le pire des cas de cet algorithme est clairement lorsque vous choisissez l'ensemble complet des n articles. Cela équivaut à demander: en moyenne, combien de fois dois-je rouler un dé bout à face avant que chaque partie ne soit arrivée au moins une fois?

Réponse: N * H N , où H N est le NTH Numéro harmonique ,

 text alt
une valeur bien approchée par journal (n) .

Cela signifie que l'algorithme en question est n journal n .

Par exemple amusant, si vous lancez une matrice à 6 face ordinaire jusqu'à ce que vous voyiez un de chaque numéro, il prendra en moyenne 6 h 6 = 14,7 rouleaux.


1 commentaires

Génial, c'est exactement une réponse que vous pouvez relier. '' LOGN N '' est une très bonne limite supérieure, lorsque '' M '' est proche de '' N '', ce qui est bien sûr la situation intéressante / pire des cas.



0
votes

La plupart des gens oublient que la levée de la recherche, si le nombre est déjà exécuté, il faut également un certain temps.

Le nombre d'essais Nessesary peut, comme décrit précédemment, être évalué à partir de: xxx

qui va à n * ln (n) pour des valeurs intéressantes de m

Cependant, pour chacun de ces "essais" devra faire une recherche. Cela pourrait être un simple O (n) runthrough ou quelque chose comme un arbre binaire. Cela vous donnera une performance totale de n ^ 2 * ln (n) ou n * ln (n) ^ 2 .

pour des valeurs plus petites de m ( m ), vous pouvez faire une très bonne approximation pour t (n, m) à l'aide du Ha -Enquation, produisant la formule: xxx

comme m va sur n , cela donne un limité inférieure de O (n) essais et performances O (n ^ 2) ou O (n * ln (n)) .

Tous les résultats sont cependant bien meilleurs, que j'aurais jamais attendu, ce qui montre que l'algorithme pourrait vraiment être très bien dans de nombreux cas non critiques, où vous pouvez accepter des temps d'exécution occasionnels (lorsque vous êtes malchanceux) .


0 commentaires