Étant donné une fonction qui produit un entier aléatoire dans la plage de 1 à 5, écrivez une fonction qui produit un entier aléatoire dans la plage de 1 à 7.
- Qu'est-ce qu'une solution simple?
- Quelle est une solution efficace pour réduire l'utilisation de la mémoire ou fonctionner sur un processeur plus lent?
7 * rand5() / 5
?Réponses:
C'est équivalent à la solution d'Adam Rosenfield, mais peut être un peu plus clair pour certains lecteurs. Il suppose que rand5 () est une fonction qui renvoie un entier statistiquement aléatoire compris entre 1 et 5 inclus.
Comment ça marche? Pensez-y comme ceci: imaginez imprimer ce tableau à double dimension sur du papier, le clouer sur un jeu de fléchettes et lancer des fléchettes au hasard. Si vous atteignez une valeur non nulle, il s'agit d'une valeur statistiquement aléatoire entre 1 et 7, car il existe un nombre égal de valeurs non nulles parmi lesquelles choisir. Si vous frappez un zéro, continuez simplement à lancer la fléchette jusqu'à ce que vous frappiez un non-zéro. C'est ce que fait ce code: les index i et j sélectionnent au hasard un emplacement sur le jeu de fléchettes, et si nous n'obtenons pas un bon résultat, nous continuons à lancer des fléchettes.
Comme Adam l'a dit, cela peut durer éternellement dans le pire des cas, mais statistiquement, le pire des cas ne se produit jamais. :)
la source
rand5
est uniforme, chaque cellule de lavals
grille a une probabilité égale d'être sélectionnée. La grille contient exactement trois copies de chaque entier dans l'intervalle [1, 7], plus quatre zéros. Ainsi, le flux de résultats "brut" tend à un mélange uniforme de valeurs [1, 7], plus quelques zéros qui apparaissent un peu plus fréquemment que toute valeur individuelle autorisée. Mais cela n'a pas d'importance car les zéros sont supprimés, ne laissant qu'un mélange homogène de valeurs [1, 7].Il n'y a pas de solution (exactement correcte) qui fonctionnera dans un laps de temps constant, car 1/7 est une décimale infinie en base 5. Une solution simple serait d'utiliser l'échantillonnage de rejet, par exemple:
Cela a un temps d'exécution prévu de 25/21 = 1,19 itérations de la boucle, mais il y a une probabilité infiniment petite de boucler pour toujours.
la source
N
appelsrand5()
dans le pire des cas. Ensuite, il y a 5 ^ N résultats possibles de la séquence d'appels àrand5
, dont chacun a une sortie de 1-7. Donc, si vous additionnez toutes les séquences d'appels possibles dont la sortie estk
pour chaque 1≤k≤7, alors la probabilité que la sortie soitk
est m / 5 ^ N, où m est le nombre de telles séquences. Donc, m / 5 ^ N = 1/7, mais il n'y a pas de solution entière possible (N, m) à cette contradiction ==>.Je voudrais ajouter une autre réponse, en plus de ma première réponse . Cette réponse tente de minimiser le nombre d'appels vers
rand5()
chaque appelrand7()
, afin de maximiser l'utilisation de l'aléatoire. Autrement dit, si vous considérez le hasard comme une ressource précieuse, nous voulons en utiliser autant que possible, sans jeter de bits aléatoires. Cette réponse présente également certaines similitudes avec la logique présentée dans la réponse d'Ivan .L' entropie d'une variable aléatoire est une quantité bien définie. Pour une variable aléatoire qui prend N états avec des probabilités égales (une distribution uniforme), l'entropie est log 2 N. Ainsi, elle
rand5()
a environ 2,32193 bits d'entropie etrand7()
environ 2,80735 bits d'entropie. Si nous espérons maximiser notre utilisation de l'aléatoire, nous devons utiliser tous les 2,32193 bits d'entropie de chaque appel àrand5()
, et les appliquer pour générer 2,80735 bits d'entropie nécessaires pour chaque appel àrand7()
. La limite fondamentale est donc que nous ne pouvons pas faire mieux que log (7) / log (5) = 1,20906 appelsrand5()
par appel àrand7()
.Notes annexes: tous les logarithmes de cette réponse seront en base 2, sauf indication contraire.
rand5()
sera supposé renvoyer des nombres dans la plage [0, 4], etrand7()
sera supposé renvoyer des nombres dans la plage [0, 6]. Ajuster les plages respectivement à [1, 5] et [1, 7] est trivial.Alors comment le fait-on? Nous générons un nombre réel aléatoire infiniment précis entre 0 et 1 (imaginez pour le moment que nous puissions réellement calculer et stocker un tel nombre infiniment précis - nous le corrigerons plus tard). On peut générer un tel nombre en générant ses chiffres en base 5: on choisit le nombre aléatoire 0.
a
1a
2a
3 ..., où chaque chiffre ai
est choisi par un appel àrand5()
. Par exemple, si notre RNG choisissait ai
= 1 pour tousi
, alors en ignorant le fait que ce n'est pas très aléatoire, cela correspondrait au nombre réel 1/5 + 1/5 2 + 1/5 3 + ... = 1/4 (somme d'une série géométrique).Ok, nous avons donc choisi un nombre réel aléatoire entre 0 et 1. Je prétends maintenant qu'un tel nombre aléatoire est uniformément distribué. Intuitivement, cela est facile à comprendre, car chaque chiffre a été choisi uniformément et le nombre est infiniment précis. Cependant, une preuve formelle de cela est un peu plus impliquée, puisque maintenant nous avons affaire à une distribution continue au lieu d'une distribution discrète, nous devons donc prouver que la probabilité que notre nombre se situe dans un intervalle [
a
,b
] est égale à la longueur de cet intervalle,b - a
. La preuve est laissée en exercice au lecteur =).Maintenant que nous avons un nombre réel aléatoire sélectionné uniformément dans la plage [0, 1], nous devons le convertir en une série de nombres uniformément aléatoires dans la plage [0, 6] pour générer la sortie de
rand7()
. Comment faisons-nous cela? Juste l'inverse de ce que nous venons de faire - nous le convertissons en une décimale infiniment précise en base 7, puis chaque chiffre de base 7 correspondra à une sortie derand7()
.Prenant l'exemple du précédent, si notre
rand5()
produit un flux infini de 1, alors notre nombre réel aléatoire sera 1/4. En convertissant 1/4 en base 7, nous obtenons la décimale infinie 0,15151515 ..., nous produirons donc en sortie 1, 5, 1, 5, 1, 5, etc.Ok, nous avons donc l'idée principale, mais il nous reste deux problèmes: nous ne pouvons pas réellement calculer ou stocker un nombre réel infiniment précis, alors comment pouvons-nous en traiter seulement une partie finie? Deuxièmement, comment pouvons-nous réellement le convertir en base 7?
Une façon de convertir un nombre compris entre 0 et 1 en base 7 est la suivante:
Pour faire face au problème de la précision infinie, nous calculons un résultat partiel, et nous stockons également une limite supérieure sur ce que pourrait être le résultat. Autrement dit, supposons que nous ayons appelé
rand5()
deux fois et qu'il soit retourné 1 fois. Le nombre que nous avons généré jusqu'à présent est de 0,11 (base 5). Quel que soit le reste de la série infinie d'appels àrand5()
produire, le nombre réel aléatoire que nous générons ne sera jamais supérieur à 0,12: il est toujours vrai que 0,11 ≤ 0,11xyz ... <0,12.Donc, en gardant une trace du nombre actuel jusqu'à présent, et de la valeur maximale qu'il pourrait jamais prendre, nous convertissons les deux nombres en base 7. S'ils s'accordent sur les premiers
k
chiffres, alors nous pouvons sortir en toute sécurité lesk
chiffres suivants - quel que soit le flux infini de chiffres de base 5 sont, ils n'affecteront jamais les prochainsk
chiffres de la représentation de base 7!Et c'est l'algorithme - pour générer la prochaine sortie de
rand7()
, nous générons seulement autant de chiffresrand5()
que nécessaire pour nous assurer que nous connaissons avec certitude la valeur du chiffre suivant dans la conversion du nombre réel aléatoire en base 7. Voici une implémentation Python, avec un harnais de test:Notez que
rand7_gen()
renvoie un générateur, car il a un état interne impliquant la conversion du nombre en base 7. Le faisceau de test appellenext(r7)
10 000 fois pour produire 10 000 nombres aléatoires, puis il mesure leur distribution. Seules les mathématiques entières sont utilisées, donc les résultats sont exactement corrects.Notez également que les chiffres ici deviennent très gros, très rapides. Les pouvoirs de 5 et 7 augmentent rapidement. Par conséquent, les performances commenceront à se dégrader sensiblement après avoir généré de nombreux nombres aléatoires, en raison de l'arithmétique du bignum. Mais rappelez-vous ici, mon objectif était de maximiser l'utilisation de bits aléatoires, pas de maximiser les performances (bien que ce soit un objectif secondaire).
En une seule fois, j'ai effectué 12091 appels vers
rand5()
10000 appels versrand7()
, atteignant le minimum d' appels log (7) / log (5) en moyenne à 4 chiffres significatifs, et la sortie résultante était uniforme.Afin de porter ce code dans un langage qui n'a pas de nombres entiers arbitrairement grands intégrés, vous devrez limiter les valeurs de
pow5
etpow7
à la valeur maximale de votre type intégral natif - si elles deviennent trop grandes, puis réinitialisez tout et recommencer. Cela augmentera très légèrement le nombre moyen d'appelsrand5()
par appelrand7()
, mais nous espérons qu'il ne devrait pas augmenter trop, même pour les entiers 32 ou 64 bits.la source
(J'ai volé la réponse d'Adam Rosenfeld et l' ai fait courir environ 7% plus rapidement.)
Supposons que rand5 () retourne l'un de {0,1,2,3,4} avec une distribution égale et le but est de retourner {0,1,2,3,4,5,6} avec une distribution égale.
Nous gardons une trace de la plus grande valeur que la boucle peut faire dans la variable
max
. Si le reult jusqu'à présent se situe entre max% 7 et max-1, le résultat sera uniformément diffusé dans cette plage. Sinon, nous utilisons le reste, qui est aléatoire entre 0 et max% 7-1, et un autre appel à rand () pour faire un nouveau numéro et un nouveau max. Puis nous recommençons.Edit: attendez le nombre de fois d'appeler rand5 () est x dans cette équation:
la source
5 * rand5() + rand5()
.Algorithme:
7 peut être représenté dans une séquence de 3 bits
Utilisez rand (5) pour remplir aléatoirement chaque bit avec 0 ou 1.
Par exemple: appelez rand (5) et
si le résultat est 1 ou 2, remplissez le bit avec 0
si le résultat est 4 ou 5, remplissez le bit avec 1
si le résultat est 3, puis ignorez et recommencez (rejet)
De cette façon, nous pouvons remplir 3 bits au hasard avec 0/1 et ainsi obtenir un nombre de 1-7.
EDIT: Cela semble être la réponse la plus simple et la plus efficace, alors voici un code pour cela:
la source
la source
Edit: Cela ne fonctionne pas tout à fait. Il est d'environ 2 parties sur 1000 (en supposant un parfait rand5). Les seaux obtiennent:
En passant à une somme de
semble gagner un ordre de grandeur pour chaque 2 ajouté
BTW: le tableau des erreurs ci-dessus n'a pas été généré par échantillonnage mais par la relation de récurrence suivante:
la source
la source
ans += (r < 3) << i
Ce qui suit produit une distribution uniforme sur {1, 2, 3, 4, 5, 6, 7} en utilisant un générateur de nombres aléatoires produisant une distribution uniforme sur {1, 2, 3, 4, 5}. Le code est désordonné, mais la logique est claire.
la source
Contrairement à la solution choisie, l'algorithme s'exécutera en temps constant. Il fait cependant 2 appels de plus vers rand5 que le temps d'exécution moyen de la solution choisie.
Notez que ce générateur n'est pas parfait (le nombre 0 a 0,0064% de chances en plus que tout autre nombre), mais pour la plupart des raisons pratiques, la garantie d'un temps constant l'emporte probablement sur cette imprécision.
Explication
Cette solution est dérivée du fait que le nombre 15,624 est divisible par 7 et donc si nous pouvons générer de manière aléatoire et uniforme des nombres de 0 à 15,624 puis prendre le mod 7, nous pouvons obtenir un générateur de rand7 presque uniforme. Les nombres de 0 à 15 624 peuvent être générés uniformément en roulant 6 fois rand5 et en les utilisant pour former les chiffres d'un nombre de base 5 comme suit:
Les propriétés du mod 7 nous permettent cependant de simplifier un peu l'équation:
Donc
devient
Théorie
Le nombre 15624 n'a pas été choisi au hasard, mais peut être découvert en utilisant le petit théorème de fermat, qui stipule que si p est un nombre premier,
Cela nous donne donc,
(5 ^ 6) -1 est égal à
Il s'agit d'un nombre sous forme de base 5 et nous pouvons donc voir que cette méthode peut être utilisée pour passer de n'importe quel générateur de nombres aléatoires à tout autre générateur de nombres aléatoires. Bien qu'un petit biais vers 0 soit toujours introduit lors de l'utilisation de l'exposant p-1.
Pour généraliser cette approche et pour être plus précis, nous pouvons avoir une fonction comme celle-ci:
la source
Les problèmes de devoirs sont-ils autorisés ici?
Cette fonction fait des calculs bruts de "base 5" pour générer un nombre compris entre 0 et 6.
la source
Si nous considérons la contrainte supplémentaire d'essayer de donner la réponse la plus efficace, c'est-à-dire celle qui donne un flux d'entrée
I
, des entiers uniformément distribués de longueurm
de 1-5 sort un fluxO
, des entiers uniformément distribués de 1-7 de la plus longue longueur relative àm
, disonsL(m)
.La manière la plus simple d'analyser cela est de traiter les flux I et
O
comme des nombres à 5 et 7 espaces respectivement. Ceci est réalisé par l'idée de la réponse principale de prendre le fluxa1, a2, a3,... -> a1+5*a2+5^2*a3+..
et de même pour le fluxO
.Ensuite, si nous prenons une section du flux d'entrée de longueur
m choose n s.t. 5^m-7^n=c
oùc>0
et est aussi petite que possible. Ensuite, il y a une carte uniforme du flux d'entrée de longueur m aux entiers de1
à5^m
et une autre carte uniforme des entiers de 1 au7^n
flux de sortie de longueur n où nous pouvons avoir à perdre quelques cas du flux d'entrée lorsque l'entier mappé dépasse7^n
.Cela donne donc une valeur
L(m)
de autourm (log5/log7)
qui est approximativement.82m
.La difficulté avec l'analyse ci-dessus est l'équation
5^m-7^n=c
qui n'est pas facile à résoudre exactement et le cas où la valeur uniforme de1
to5^m
dépasse7^n
et nous perdons en efficacité.La question est de savoir jusqu'à quel point la meilleure valeur possible de m (log5 / log7) peut être atteinte. Par exemple, lorsque ce nombre se rapproche d'un entier, pouvons-nous trouver un moyen d'atteindre ce nombre entier exact de valeurs de sortie?
Si
5^m-7^n=c
ensuite, à partir du flux d'entrée, nous générons effectivement un nombre aléatoire uniforme de0
à(5^m)-1
et n'utilisons aucune valeur supérieure à7^n
. Cependant, ces valeurs peuvent être récupérées et réutilisées. Ils génèrent efficacement une séquence uniforme de nombres de 1 à5^m-7^n
. Nous pouvons donc essayer de les utiliser et les convertir en nombres à 7 zones afin de créer plus de valeurs de sortie.Si nous laissons
T7(X)
la longueur moyenne de la séquence de sortie desrandom(1-7)
entiers dérivée d'une entrée uniforme de tailleX
, et en supposant cela5^m=7^n0+7^n1+7^n2+...+7^nr+s, s<7
.Alors
T7(5^m)=n0x7^n0/5^m + ((5^m-7^n0)/5^m) T7(5^m-7^n0)
puisque nous avons une longueur sans séquence avec probabilité 7 ^ n0 / 5 ^ m avec un résidu de longueur5^m-7^n0
avec probabilité(5^m-7^n0)/5^m)
.Si nous continuons à remplacer, nous obtenons:
Par conséquent
Une autre façon de le dire est:
Le meilleur cas possible est mon cas ci-dessus où
5^m=7^n+s
, oùs<7
.Puis
T7(5^m) = nx(7^n)/(7^n+s) = n+o(1) = m (Log5/Log7)+o(1)
comme avant.Le pire des cas est celui où l'on ne trouve que k et st 5 ^ m = kx7 + s.
D'autres cas se situent quelque part entre les deux. Il serait intéressant de voir dans quelle mesure nous pouvons faire pour de très grands m, c'est-à-dire dans quelle mesure pouvons-nous obtenir le terme d'erreur:
Cela semble impossible à réaliser
e(m) = o(1)
en général, mais j'espère que nous pourrons le prouvere(m)=o(m)
.Le tout repose alors sur la distribution des 7 chiffres de
5^m
pour différentes valeurs dem
.Je suis sûr qu'il y a beaucoup de théories qui couvrent ce sujet.
la source
Voici une implémentation Python fonctionnelle de la réponse d' Adam .
J'aime lancer des algorithmes que je regarde dans Python afin que je puisse jouer avec eux, pensais que je le posterais ici dans l'espoir qu'il soit utile à quelqu'un là-bas, pas qu'il ait fallu longtemps pour lancer ensemble.
la source
rand5()
c'est un PRNG décent, alors la boucle ne sera pas infinie car finalement elle5*(rand5() - 1) + rand5()
sera définitivement <= 21.)Pourquoi ne pas faire simple?
Les chances d'obtenir 1 et 7 dans cette solution sont plus faibles en raison du modulo, cependant, si vous voulez juste une solution rapide et lisible, c'est la voie à suivre.
la source
En supposant que rand (n) signifie ici "entier aléatoire dans une distribution uniforme de 0 à n-1 ", voici un exemple de code utilisant randint de Python, qui a cet effet. Il utilise uniquement randint (5) et des constantes pour produire l'effet de randint (7) . Un peu idiot, en fait
la source
do ... while
. Il aurait pu être1337
, ou12345
, ou n'importe quel nombre> 1.La prémisse derrière la bonne réponse d'Adam Rosenfield est:
Lorsque n est égal à 2, vous avez 4 possibilités de mise au rebut: y = {22, 23, 24, 25}. Si vous utilisez n est égal à 6, vous n'avez qu'un jetable: y = {15625}.
5 ^ 6 = 15625
7 * 2232 = 15624
Vous appelez rand5 plusieurs fois. Cependant, vous avez une chance beaucoup plus faible d'obtenir une valeur jetable (ou une boucle infinie). S'il existe un moyen d'obtenir aucune valeur de rejet possible pour y, je ne l'ai pas encore trouvé.
la source
Voici ma réponse:
C'est un peu plus compliqué que les autres, mais je pense que cela minimise les appels à rand5. Comme avec d'autres solutions, il y a une faible probabilité qu'il puisse boucler pendant une longue période.
la source
Simple et efficace:
(Inspiré par Quel est votre dessin animé "programmeur" préféré? ).
la source
Tant qu'il ne reste plus sept possibilités, choisissez un autre nombre aléatoire, qui multiplie le nombre de possibilités par cinq. En Perl:
la source
$possibilities
faut toujours passer à 25 pour sortir de la boucle et revenir. Donc, votre premier résultat est[0-124] % 7
, qui n'est pas uniformément distribué car125 % 7 != 0
(c'est 6, en fait).Je n'aime pas les plages à partir de 1, donc je vais commencer à 0 :-)
la source
from collections import defaultdict def r7(n): if not n: yield [] else: for i in range(1, 6): for j in r7(n-1): yield [i] + j def test_r7(): d = defaultdict(int) for x in r7(6): s = (((((((((x[5] * 5) + x[4]) * 5) + x[3]) * 5) + x[2]) * 5) + x[1]) * 5) + x[0] if s <= 15623: d[s % 7] += 1 print d
Voilà, distribution uniforme et zéro appel rand5.
Besoin de semer au préalable.
la source
Je sais qu'il a été répondu, mais est-ce que cela semble fonctionner correctement, mais je ne peux pas vous dire si cela a un biais. Mes «tests» suggèrent que c'est, au moins, raisonnable.
Adam Rosenfield aurait peut-être la gentillesse de commenter?
Mon idée (naïve?) Est la suivante:
Accumulez les rand5 jusqu'à ce qu'il y ait suffisamment de bits aléatoires pour faire un rand7. Cela prend au plus 2 rand5. Pour obtenir le nombre rand7, j'utilise la valeur cumulée mod 7.
Pour éviter que l'accumulateur ne déborde, et comme l'accumulateur est le mod 7 alors je prends le mod 7 de l'accumulateur:
La fonction rand7 () suit:
(Je laisse la plage de rand5 être 0-4 et rand7 est également 0-6.)
Edit: Ajout de résultats pour 100 millions d'essais.
Fonctions «réelles» de rand mod 5 ou 7
rand5: avg = 1.999802 0: 20003944 1: 19999889 2: 20003690 3: 19996938 4: 19995539 rand7: avg = 3.000111 0: 14282851 1: 14282879 2: 14284554 3: 14288546 4: 14292388 5: 14288736 6: 14280046
Mon rand7
La moyenne semble correcte et les distributions de nombres semblent également correctes.
randt: avg = 3.000080 0: 14288793 1: 14280135 2: 14287848 3: 14285277 4: 14286341 5: 14278663 6: 14292943
la source
Il existe des algorithmes élégants cités ci-dessus, mais voici une façon de l'aborder, bien qu'il puisse s'agir d'un rond-point. Je suppose des valeurs générées à partir de 0.
R2 = générateur de nombres aléatoires donnant des valeurs inférieures à 2 (espace d'échantillonnage = {0, 1})
R8 = générateur de nombres aléatoires donnant des valeurs inférieures à 8 (espace d'échantillonnage = {0, 1, 2, 3, 4, 5, 6, 7 })
Afin de générer R8 à partir de R2, vous exécuterez R2 trois fois et utiliserez le résultat combiné des 3 exécutions comme un nombre binaire à 3 chiffres. Voici la plage de valeurs lorsque R2 est exécuté trois fois:
0 0 0 -> 0
.
.
1 1 1 -> 7
Maintenant, pour générer R7 à partir de R8, nous exécutons simplement R7 à nouveau s'il renvoie 7:
La solution du rond-point consiste à générer R2 à partir de R5 (tout comme nous avons généré R7 à partir de R8), puis R8 à partir de R2, puis R7 à partir de R8.
la source
Voici une solution qui s'intègre entièrement dans les entiers et se situe à environ 4% de l'optimal (c'est-à-dire utilise 1,26 nombres aléatoires dans {0..4} pour chacun dans {0..6}). Le code est en Scala, mais les mathématiques doivent être raisonnablement claires dans n'importe quelle langue: vous profitez du fait que 7 ^ 9 + 7 ^ 8 est très proche de 5 ^ 11. Donc, vous choisissez un nombre à 11 chiffres dans la base 5, puis vous l'interprétez comme un nombre à 9 chiffres dans la base 7 s'il est dans la plage (en donnant 9 nombres à la base 7), ou comme un nombre à 8 chiffres s'il est supérieur au nombre à 9 chiffres, etc. .:
Si vous collez un test dans l'interpréteur (REPL en fait), vous obtenez:
La distribution est agréable et plate (dans environ 10k de 1/7 de 10 ^ 8 dans chaque bac, comme prévu d'une distribution approximativement gaussienne).
la source
En utilisant un total mobile , vous pouvez à la fois
Ces deux problèmes sont un problème avec les
rand(5)+rand(5)...
solutions de type simpliste . Le code Python suivant montre comment l'implémenter (la plupart de cela prouve la distribution).Et cette sortie montre les résultats:
Un simpliste
rand(5)+rand(5)
, ignorant les cas où cela renvoie plus de 6 a une variation typique de 18%, 100 fois celle de la méthode indiquée ci-dessus:Et, sur les conseils de Nixuz, j'ai nettoyé le script pour que vous puissiez simplement extraire et utiliser les
rand7...
choses:la source
Cette réponse est plus une expérience pour obtenir le plus d'entropie possible à partir de la fonction Rand5. C'est donc quelque peu flou et presque certainement beaucoup plus lent que les autres implémentations.
En supposant la distribution uniforme de 0-4 et la distribution uniforme résultante de 0-6:
Le nombre de bits ajoutés au tampon par appel à Rand5 est actuellement de 4/5 * 2 donc 1,6. Si la valeur de probabilité 1/5 est incluse, elle augmente de 0,05, donc 1,65, mais voyez le commentaire dans le code où j'ai dû désactiver cela.
Bits consommés par appel à Rand7 = 3 + 1/8 * (3 + 1/8 * (3 + 1/8 * (...
C'est 3 + 3/8 + 3/64 + 3/512 ... donc environ 3,42
En extrayant des informations des sept, je récupère 1/8 * 1/7 bits par appel soit environ 0,018
Cela donne une consommation nette de 3,4 bits par appel, ce qui signifie que le rapport est de 2,125 appels vers Rand5 pour chaque Rand7. L'optimum devrait être de 2,1.
J'imagine que cette approche est beaucoup plus lente que la plupart des autres ici, à moins que le coût de l'appel à Rand5 ne soit extrêmement cher (par exemple, appeler une source externe d'entropie).
la source
en php
boucles pour produire un nombre aléatoire entre 16 et 127, divise par seize pour créer un flottant entre 1 et 7,9375, puis arrondit pour obtenir un entier entre 1 et 7. si je ne me trompe pas, il y a 16/112 chance d'obtenir l'un des 7 résultats.
la source
la source
7 = 111b
avecp(7) = 8 / 125
Je pense avoir quatre réponses, deux donnant des solutions exactes comme celle de @Adam Rosenfield mais sans le problème de boucle infinie, et deux autres avec une solution presque parfaite mais une mise en œuvre plus rapide que la première.
La meilleure solution exacte nécessite 7 appels à
rand5
, mais permet de continuer pour comprendre.Méthode 1 - exacte
La force de la réponse d'Adam est qu'elle donne une distribution uniforme parfaite, et il y a une très forte probabilité (21/25) que seulement deux appels à rand5 () soient nécessaires. Cependant, le pire des cas est la boucle infinie.
La première solution ci-dessous donne également une distribution uniforme parfaite, mais nécessite un total de 42 appels vers
rand5
. Pas de boucles infinies.Voici une implémentation R:
Pour les personnes qui ne connaissent pas R, voici une version simplifiée:
La distribution de
rand5
sera préservée. Si nous faisons le calcul, chacune des 7 itérations de la boucle a 5 ^ 6 combinaisons possibles, donc le nombre total de combinaisons possibles est(7 * 5^6) %% 7 = 0
. Ainsi, nous pouvons diviser les nombres aléatoires générés en groupes égaux de 7. Voir la méthode deux pour plus de discussion à ce sujet.Voici toutes les combinaisons possibles:
Je pense qu'il est simple de montrer que la méthode d'Adam fonctionnera beaucoup plus rapidement. La probabilité qu'il y ait 42 appels ou plus
rand5
dans la solution d'Adam est très faible ((4/25)^21 ~ 10^(-17)
).Méthode 2 - pas exacte
Maintenant, la deuxième méthode, qui est presque uniforme, mais nécessite 6 appels à
rand5
:Voici une version simplifiée:
Il s'agit essentiellement d'une itération de la méthode 1. Si nous générons toutes les combinaisons possibles, voici les comptes résultants:
Un numéro apparaîtra à nouveau dans les
5^6 = 15625
essais.Maintenant, dans la méthode 1, en ajoutant 1 à 6, nous déplaçons le nombre 2233 à chacun des points successifs. Ainsi, le nombre total de combinaisons correspondra. Cela fonctionne parce que 5 ^ 6 %% 7 = 1, puis nous faisons 7 variations appropriées, donc (7 * 5 ^ 6 %% 7 = 0).
Méthode 3 - exacte
Si l'argument des méthodes 1 et 2 est compris, la méthode 3 suit et ne nécessite que 7 appels à
rand5
. À ce stade, je pense que c'est le nombre minimum d'appels nécessaires pour une solution exacte.Voici une implémentation R:
Pour les personnes qui ne connaissent pas R, voici une version simplifiée:
La distribution de
rand5
sera préservée. Si nous faisons le calcul, chacune des 7 itérations de la boucle a 5 résultats possibles, donc le nombre total de combinaisons possibles est(7 * 5) %% 7 = 0
. Ainsi, nous pouvons diviser les nombres aléatoires générés en groupes égaux de 7. Voir méthode un et deux pour plus de discussion à ce sujet.Voici toutes les combinaisons possibles:
Je pense qu'il est simple de montrer que la méthode d'Adam fonctionnera toujours plus rapidement. La probabilité qu'il y ait 7 appels ou plus
rand5
dans la solution d'Adam est encore faible ((4/25)^3 ~ 0.004
).Méthode 4 - pas exacte
Il s'agit d'une variante mineure de la deuxième méthode. Il est presque uniforme, mais nécessite 7 appels à
rand5
, c'est un complément à la méthode 2:Voici une version simplifiée:
Si nous générons toutes les combinaisons possibles, voici les comptes résultants:
Deux numéros apparaîtront une fois de moins dans les
5^7 = 78125
essais. Pour la plupart des buts, je peux vivre avec ça.la source
i=7
n'a également aucun effet, car l'ajout7*rand5()
àr
ne modifie pas la valeur dur
mod 7.)La fonction dont vous avez besoin est rand1_7 () , j'ai écrit rand1_5 () pour que vous puissiez la tester et la tracer.
la source