Creo que tengo cuatro respuestas, dos que dan soluciones exactas como la de @Adam Rosenfield pero sin el problema del bucle infinito, y otras dos con una solución casi perfecta pero una implementación más rápida que la primera.
La mejor solución exacta requiere 7 llamadas a rand5
, pero procedamos para entender.
Método 1 - Exacto
La fortaleza de la respuesta de Adam es que proporciona una distribución uniforme perfecta, y hay una probabilidad muy alta (21/25) de que solo se necesiten dos llamadas a rand5 (). Sin embargo, el peor de los casos es el bucle infinito.
La primera solución a continuación también ofrece una distribución uniforme perfecta, pero requiere un total de 42 llamadas a rand5
. No hay bucles infinitos.
Aquí hay una implementación de 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
Para las personas que no están familiarizadas con R, aquí hay una versión simplificada:
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 distribución de rand5
será preservada. Si hacemos los cálculos, cada una de las 7 iteraciones del ciclo tiene 5 ^ 6 combinaciones posibles, por lo tanto, el número total de combinaciones posibles es (7 * 5^6) %% 7 = 0
. Por lo tanto, podemos dividir los números aleatorios generados en grupos iguales de 7. Vea el método dos para más discusión sobre esto.
Aquí están todas las combinaciones posibles:
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
Creo que es sencillo demostrar que el método de Adam se ejecutará mucho más rápido. La probabilidad de que haya 42 o más llamadas rand5
en la solución de Adam es muy pequeña ((4/25)^21 ~ 10^(-17)
).
Método 2: no exacto
Ahora el segundo método, que es casi uniforme, pero requiere 6 llamadas a rand5
:
rand7 <- function() (sum(sapply(1:6,function(i) i*rand5())) %% 7) + 1
Aquí hay una versión simplificada:
rand7 = function(){
r = 0
for(i in 1:6){
r = r + i*rand5()
}
return r %% 7 + 1
}
Esto es esencialmente una iteración del método 1. Si generamos todas las combinaciones posibles, aquí están los conteos resultantes:
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 número aparecerá una vez más en las 5^6 = 15625
pruebas.
Ahora, en el Método 1, al sumar 1 a 6, movemos el número 2233 a cada uno de los puntos sucesivos. Por lo tanto, el número total de combinaciones coincidirá. Esto funciona porque 5 ^ 6 %% 7 = 1, y luego hacemos 7 variaciones apropiadas, entonces (7 * 5 ^ 6 %% 7 = 0).
Método 3 - Exacto
Si se entiende el argumento de los métodos 1 y 2, sigue el método 3 y solo requiere 7 llamadas a rand5
. En este punto, siento que esta es la cantidad mínima de llamadas necesarias para una solución exacta.
Aquí hay una implementación de R:
rand5 <- function() sample(1:5,1)
rand7 <- function() (sum(sapply(1:7, function(i) i * rand5())) %% 7) + 1
Para las personas que no están familiarizadas con R, aquí hay una versión simplificada:
rand7 = function(){
r = 0
for(i in 1:7){
r = r + i * rand5()
}
return r %% 7 + 1
}
La distribución de rand5
será preservada. Si hacemos los cálculos, cada una de las 7 iteraciones del ciclo tiene 5 resultados posibles, por lo tanto, el número total de combinaciones posibles es(7 * 5) %% 7 = 0
. Por lo tanto, podemos dividir los números aleatorios generados en grupos iguales de 7. Vea el método uno y dos para más discusión sobre esto.
Aquí están todas las combinaciones posibles:
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
Creo que es sencillo demostrar que el método de Adam seguirá funcionando más rápido. La probabilidad de que haya 7 o más llamadas rand5
en la solución de Adam sigue siendo pequeña ( (4/25)^3 ~ 0.004
).
Método 4: no exacto
Esta es una variación menor del segundo método. Es casi uniforme, pero requiere 7 llamadas a rand5
, que es una adicional al método 2:
rand7 <- function() (rand5() + sum(sapply(1:6,function(i) i*rand5())) %% 7) + 1
Aquí hay una versión simplificada:
rand7 = function(){
r = 0
for(i in 1:6){
r = r + i*rand5()
}
return (r+rand5()) %% 7 + 1
}
Si generamos todas las combinaciones posibles, aquí están los conteos resultantes:
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
Dos números aparecerán una vez menos en las 5^7 = 78125
pruebas. Para la mayoría de los propósitos, puedo vivir con eso.