On nous donne un générateur de nombres aléatoires RandNum50
qui génère uniformément un entier aléatoire compris entre 1 et 50. Nous pouvons utiliser uniquement ce générateur de nombres aléatoires pour générer et imprimer tous les entiers de 1 à 100 dans un ordre aléatoire. Chaque nombre doit arriver exactement une fois, et la probabilité qu'un nombre se produise à n'importe quel endroit doit être égale.
Quel est l'algorithme le plus efficace pour cela?
algorithms
integers
randomness
random-number-generator
Raj Wadhwa
la source
la source
RandNum100 = (RandNum50() * 2) - (RandNum50 > 25) ? 0 : 1)
.Réponses:
J'ai pensé (donc ça peut être faux :-) de cette solution qui utilise le shuffle de Fisher-Yates . Afin de conserver une distribution uniforme avec une bonne approximation (voir la section EDITER ci-dessous) à chaque itération, vous pouvez utiliser cette astuce pour produire une valeur entre 0 et k - 1 :O(N2) 0 k−1
krand
L'algorithme de Fisher-Yates devient:
ÉDITER:
Comme l'a souligné Erick, lam=⌈log2(k)⌉ r k
krand
fonction ci-dessus ne renvoie pas une distribution vraiment uniforme. Il existe d'autres méthodes qui peuvent être utilisées pour obtenir une meilleure approximation (arbitrairement meilleure) et plus rapide; mais (à ma connaissance) la seule façon d'obtenir une distribution vraiment uniforme est d'utiliser l' échantillonnage de rejet : choisir bits aléatoires et si le nombre r obtenu est inférieur à k le retourner, sinon générer un autre nombre aléatoire; une mise en œuvre possible:la source
Étant donné que d'autres personnes ont donné des solutions approximatives et des solutions impliquant la prise d'un nombre indéterminé de déviations, que diriez-vous d'une preuve qu'il n'y a pas un tel algorithme qui ne garantisse de ne nécessiter qu'un nombre fini d'
RandNum50()
appels?Comme d'autres l'ont noté, l'impression des nombres de 1 à 100 dans un ordre aléatoire équivaut à l'impression d'une permutation aléatoire de ces nombres; il y en a 100! de ces permutations, et donc toute permutation particulière doit être sortie avec une probabilité .1100!
Mais si nous savions que notre algorithme utilisait au plus appels pour certains k , alors nous pourrions argumenter comme suit: premièrement, supprimez les chemins de calcul qui font moins de k appels pour faire des appels fictifs supplémentaires (c'est-à-dire des appels où le la valeur retournée n'est pas pertinente), de sorte que tous les chemins de calcul effectuent précisément k appels. Toute séquence donnée de k résultats de nos appels à doit entraîner une certaine permutation de sortie, et nous pouvons donc construire une «table de résultats» qui mappe toute séquence donnée ( r 1 , r 2 , … , r k )k k k k k (r1,r2,…,rk) des résultats de nos appels dans une permutation de sortie particulière. Étant donné que chacun de ces résultats est également probable (chacun a la probabilité ), alors la probabilité d'obtenir une permutation particulière de notre algorithme doit être de la formec150k pour certainsc. Mais1c50k c ne peut pas être de cette forme, car100! ne divise pas50kpour n'importe quelk(par exemple, 3 divise100!mais ne peut diviser aucun nombre de la forme50k). Cela signifie qu'aucune distribution possible des résultats aux appels de nombres aléatoires ne peut produire une permutation uniforme.1100! 100! 50k k 100! 50k
RandNum50
RandNum50
RandNum50
la source
Si vous ne savez pas comment générer un uniforme, comme suggéré dans ce post, à partir d'un bit aléatoire, vous pouvez également générer une approximation de l'uniforme directement, de cette manière (qui est équivalente au "trulyrand" de Vor, mais plus rapide):
la source
Je n'ai pas fait l'analyse pour confirmer à quel point cela serait uniforme (ou non), et cela pourrait être ajusté pour être un vrai mélange, mais pourriez-vous simplement choisir, à partir d'un tableau de départ du
i
th index =i + 1
, l'(k + RandNum50() + RandNum50() - 1) mod (100 - k)
index, avec suppression, pourk
= 0..99?Cela "pousse"
RandNum50() + RandNum50()
uniformément le pic de la distribution vers l'avant.Je suis à peu près sûr que ce n'est pas tout à fait raison, comme je l'ai dit, car l'index 0 (1) ne peut pas être obtenu à partir du premier choix et je ne peux pas voir rapidement un autre ajustement 1..50 + 1..50 qui produit 0 ..99.
Mise à jour
Pour résoudre le problème que j'ai noté, j'ai effectivement utilisé
RandNum100
comme mentionné dans les commentaires de la question pour initialiser au hasard le premierk
décalage.Cela produit une distribution avec une onde importante à l'avant.
Au lieu d'avancer de 1, j'en ai utilisé un autre
RandNum50
pour l'incrémenter en premierk
. Cela produit un résultat qui est assez aléatoire pour moi, mais ce n'est toujours pas "vraiment" aléatoire, comme on peut le voir facilement si vous changez K en 2.Test du code VB.NET où je répondais à tout K. pair. Notez que c'est O (K), 6K + 2 en fait.
la source