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:
rand5 <- function() sample(1:5,1)
rand7 <- function() (sum(sapply(0:6, function(i) i + rand5() + rand5()*2 + rand5()*3 + rand5()*4 + rand5()*5 + rand5()*6)) %% 7) + 1
Pour les personnes qui ne connaissent pas R, voici une version simplifiée:
rand7 = function(){
r = 0
for(i in 0:6){
r = r + i + rand5() + rand5()*2 + rand5()*3 + rand5()*4 + rand5()*5 + rand5()*6
}
return r %% 7 + 1
}
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:
table(apply(expand.grid(c(outer(1:5,0:6,"+")),(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
15625 15625 15625 15625 15625 15625 15625
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
:
rand7 <- function() (sum(sapply(1:6,function(i) i*rand5())) %% 7) + 1
Voici une version simplifiée:
rand7 = function(){
r = 0
for(i in 1:6){
r = r + i*rand5()
}
return r %% 7 + 1
}
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:
table(apply(expand.grid(1:5,(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
2233 2232 2232 2232 2232 2232 2232
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:
rand5 <- function() sample(1:5,1)
rand7 <- function() (sum(sapply(1:7, function(i) i * rand5())) %% 7) + 1
Pour les personnes qui ne connaissent pas R, voici une version simplifiée:
rand7 = function(){
r = 0
for(i in 1:7){
r = r + i * rand5()
}
return r %% 7 + 1
}
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:
table(apply(expand.grid(0:6,(1:5)),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
5 5 5 5 5 5 5
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:
rand7 <- function() (rand5() + sum(sapply(1:6,function(i) i*rand5())) %% 7) + 1
Voici une version simplifiée:
rand7 = function(){
r = 0
for(i in 1:6){
r = r + i*rand5()
}
return (r+rand5()) %% 7 + 1
}
Si nous générons toutes les combinaisons possibles, voici les comptes résultants:
table(apply(expand.grid(1:5,(1:5)*2,(1:5)*3,(1:5)*4,(1:5)*5,(1:5)*6,1:5),1,sum) %% 7 + 1)
1 2 3 4 5 6 7
11160 11161 11161 11161 11161 11161 11160
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.