Dada una función que produce un entero aleatorio en el rango de 1 a 5, escriba una función que produzca un entero aleatorio en el rango de 1 a 7.
- ¿Qué es una solución simple?
- ¿Cuál es una solución efectiva para reducir el uso de memoria o ejecutarse en una CPU más lenta?
7 * rand5() / 5
?Respuestas:
Esto es equivalente a la solución de Adam Rosenfield, pero puede ser un poco más claro para algunos lectores. Se supone que rand5 () es una función que devuelve un entero estadísticamente aleatorio en el rango de 1 a 5 inclusive.
¿Como funciona? Piénselo de esta manera: imagine imprimir esta matriz de doble dimensión en papel, pegarla en un tablero de dardos y arrojarle al azar dardos. Si alcanza un valor distinto de cero, es un valor estadísticamente aleatorio entre 1 y 7, ya que hay un número igual de valores distintos de cero para elegir. Si golpeas un cero, sigue tirando el dardo hasta que golpees un no cero. Eso es lo que está haciendo este código: los índices i y j seleccionan aleatoriamente una ubicación en el tablero de dardos, y si no obtenemos un buen resultado, seguimos lanzando dardos.
Como dijo Adam, esto puede durar para siempre en el peor de los casos, pero estadísticamente el peor de los casos nunca sucede. :)
fuente
rand5
es uniforme, cada celda de lavals
cuadrícula tiene la misma probabilidad de ser seleccionada. La cuadrícula contiene exactamente tres copias de cada número entero en el intervalo [1, 7], más cuatro ceros. Por lo tanto, la secuencia de resultados "en bruto" tiende a una mezcla uniforme de valores [1, 7], más algunos ceros que ocurren un poco más frecuentemente que cualquier valor individual permitido. Pero eso no importa porque los ceros se eliminan, dejando solo una mezcla uniforme de valores [1, 7].No existe una solución (exactamente correcta) que se ejecute en una cantidad de tiempo constante, ya que 1/7 es un decimal infinito en la base 5. Una solución simple sería utilizar un muestreo de rechazo, por ejemplo:
Esto tiene un tiempo de ejecución esperado de 25/21 = 1.19 iteraciones del bucle, pero hay una probabilidad infinitamente pequeña de bucle para siempre.
fuente
N
llamadasrand5()
en el peor de los casos. Entonces, hay 5 ^ N posibles resultados de la secuencia de llamadas arand5
, cada una de las cuales tiene una salida de 1-7. Entonces, si suma todas las secuencias posibles de llamadas cuya salida esk
para cada 1≤k≤7, entonces la probabilidad de que la salidak
sea m / 5 ^ N, donde m es el número de tales secuencias. Entonces, m / 5 ^ N = 1/7, pero no hay posibles soluciones enteras (N, m) a esta ==> contradicción.Me gustaría agregar otra respuesta, además de mi primera respuesta . Esta respuesta intenta minimizar el número de llamadas a
rand5()
por llamadarand7()
, para maximizar el uso de la aleatoriedad. Es decir, si considera que la aleatoriedad es un recurso valioso, queremos usar la mayor cantidad posible, sin tirar ningún fragmento aleatorio. Esta respuesta también tiene algunas similitudes con la lógica presentada en la respuesta de Ivan .La entropía de una variable aleatoria es una cantidad bien definida. Para una variable aleatoria que toma N estados con probabilidades iguales (una distribución uniforme), la entropía es log 2 N. Por lo tanto,
rand5()
tiene aproximadamente 2.32193 bits de entropía yrand7()
tiene aproximadamente 2.80735 bits de entropía. Si esperamos maximizar nuestro uso de aleatoriedad, necesitamos usar todos los 2.32193 bits de entropía de cada llamada arand5()
, y aplicarlos para generar 2.80735 bits de entropía necesarios para cada llamada arand7()
. El límite fundamental, entonces, es que no podemos hacer nada mejor que log (7) / log (5) = 1.20906 llamadas arand5()
por llamada arand7()
.Notas al margen: todos los logaritmos en esta respuesta serán de base 2 a menos que se especifique lo contrario.
rand5()
se supondrá que devuelve números en el rango [0, 4], yrand7()
se supondrá que devuelve números en el rango [0, 6]. Ajustar los rangos a [1, 5] y [1, 7] respectivamente es trivial.Entonces, ¿Cómo lo hacemos? Generamos un número real aleatorio infinitamente preciso entre 0 y 1 (simule por el momento que realmente podríamos calcular y almacenar un número tan infinitamente preciso; lo arreglaremos más adelante). Podemos generar dicho número generando sus dígitos en la base 5: elegimos el número aleatorio 0.
a
1a
2a
3 ..., donde cada dígito ai
es elegido por una llamada arand5()
. Por ejemplo, si nuestro RNG eligió ai
= 1 para todosi
, ignorando el hecho de que eso no es muy aleatorio, eso correspondería al número real 1/5 + 1/5 2 + 1/5 3 + ... = 1/4 (suma de una serie geométrica).Bien, entonces hemos elegido un número real aleatorio entre 0 y 1. Ahora afirmo que dicho número aleatorio está distribuido uniformemente. Intuitivamente, esto es fácil de entender, ya que cada dígito se seleccionó de manera uniforme y el número es infinitamente preciso. Sin embargo, una prueba formal de esto es algo más complicada, ya que ahora estamos tratando con una distribución continua en lugar de una distribución discreta, por lo que debemos demostrar que la probabilidad de que nuestro número se encuentre en un intervalo [
a
,b
] es igual a la longitud de ese intervalo,b - a
. La prueba se deja como un ejercicio para el lector =).Ahora que tenemos un número real aleatorio seleccionado uniformemente del rango [0, 1], necesitamos convertirlo a una serie de números aleatorios uniformes en el rango [0, 6] para generar la salida de
rand7()
. Cómo hacemos esto? Justo lo contrario de lo que acabamos de hacer: lo convertimos a un decimal infinitamente preciso en base 7, y luego cada dígito de base 7 corresponderá a una salida derand7()
.Tomando el ejemplo de antes, si nuestro
rand5()
produce un flujo infinito de 1, entonces nuestro número real aleatorio será 1/4. Con la conversión de 1/4 a base 7, obtenemos el decimal infinito 0.15151515 ..., por lo que produciremos como salida 1, 5, 1, 5, 1, 5, etc.Bien, tenemos la idea principal, pero nos quedan dos problemas: no podemos calcular o almacenar un número real infinitamente preciso, entonces, ¿cómo lidiamos con solo una porción finita? En segundo lugar, ¿cómo lo convertimos realmente a base 7?
Una forma de convertir un número entre 0 y 1 a base 7 es la siguiente:
Para lidiar con el problema de la precisión infinita, calculamos un resultado parcial y también almacenamos un límite superior sobre cuál podría ser el resultado. Es decir, supongamos que llamamos
rand5()
dos veces y devolvió 1 las dos veces. El número que hemos generado hasta ahora es 0.11 (base 5). Cualquiera que sea el resto de las infinitas series de llamadas que serand5()
producirán, el número real aleatorio que estamos generando nunca será mayor que 0.12: siempre es cierto que 0.11 ≤ 0.11xyz ... <0.12.Por lo tanto, al realizar un seguimiento del número actual hasta el momento y el valor máximo que podría tomar, convertimos ambos números a la base 7. Si están de acuerdo con los primeros
k
dígitos, entonces podemos generar con seguridad los siguientesk
dígitos, independientemente de lo que ¡flujo infinito de base 5 dígitos son, nunca afectarán los siguientesk
dígitos de la representación de base 7!Y ese es el algoritmo: para generar la siguiente salida de
rand7()
, generamos solo tantos dígitosrand5()
como sea necesario para garantizar que sepamos con certeza el valor del siguiente dígito en la conversión del número real aleatorio a la base 7. Aquí está Una implementación de Python, con un arnés de prueba:Tenga en cuenta que
rand7_gen()
devuelve un generador, ya que tiene un estado interno que implica la conversión del número a la base 7. El arnés de prueba llamanext(r7)
10000 veces para producir 10000 números aleatorios, y luego mide su distribución. Solo se usa matemática entera, por lo que los resultados son exactamente correctos.También tenga en cuenta que los números aquí se vuelven muy grandes, muy rápidos. Las potencias de 5 y 7 crecen rápidamente. Por lo tanto, el rendimiento comenzará a degradarse notablemente después de generar muchos números aleatorios, debido a la aritmética bignum. Pero recuerde aquí, mi objetivo era maximizar el uso de bits aleatorios, no maximizar el rendimiento (aunque ese es un objetivo secundario).
En una corrida de esto, hice 12091 llamadas a
rand5()
10000 llamadas arand7()
, logrando el mínimo de llamadas log (7) / log (5) en promedio a 4 cifras significativas, y el resultado resultante fue uniforme.Para portar este código a un idioma que no tenga enteros arbitrariamente grandes incorporados, tendrá que limitar los valores
pow5
ypow7
al valor máximo de su tipo integral nativo; si se vuelven demasiado grandes, reinicie todo y empezar de nuevo. Esto aumentará el número promedio de llamadasrand5()
por llamada arand7()
muy ligeramente, pero es de esperar que no aumente demasiado incluso para enteros de 32 o 64 bits.fuente
(He robado la respuesta de Adam Rosenfeld y la hice correr un 7% más rápido).
Suponga que rand5 () devuelve uno de {0,1,2,3,4} con igual distribución y el objetivo es devolver {0,1,2,3,4,5,6} con igual distribución.
Estamos realizando un seguimiento del valor más grande que el ciclo puede hacer en la variable
max
. Si el resultado hasta ahora es entre max% 7 y max-1, el resultado se distribuirá uniformemente en ese rango. Si no, usamos el resto, que es aleatorio entre 0 y max% 7-1, y otra llamada a rand () para hacer un nuevo número y un nuevo max. Entonces comenzamos de nuevo.Editar: esperar número de veces para llamar a rand5 () es x en esta ecuación:
fuente
5 * rand5() + rand5()
.Algoritmo:
7 se puede representar en una secuencia de 3 bits
Use rand (5) para llenar aleatoriamente cada bit con 0 o 1.
Por ejemplo: llame a rand (5) y
si el resultado es 1 o 2, llene el bit con 0
si el resultado es 4 o 5, llene el bit con 1
si el resultado es 3, luego ignórelo y vuelva a hacerlo (rechazo)
De esta manera podemos llenar 3 bits al azar con 0/1 y así obtener un número del 1 al 7.
EDITAR: Esta parece ser la respuesta más simple y eficiente, así que aquí hay un código para ello:
fuente
fuente
Editar: Eso no funciona del todo. Está apagado por aproximadamente 2 partes en 1000 (suponiendo un rand5 perfecto). Los cubos obtienen:
Al cambiar a una suma de
parece ganar un orden de magnitud por cada 2 agregados
Por cierto: la tabla de errores anterior no se generó a través del muestreo, sino por la siguiente relación de recurrencia:
fuente
fuente
ans += (r < 3) << i
Lo siguiente produce una distribución uniforme en {1, 2, 3, 4, 5, 6, 7} usando un generador de números aleatorios que produce una distribución uniforme en {1, 2, 3, 4, 5}. El código es desordenado, pero la lógica es clara.
fuente
A diferencia de la solución elegida, el algoritmo se ejecutará en tiempo constante. Sin embargo, realiza 2 llamadas más a rand5 que el tiempo de ejecución promedio de la solución elegida.
Tenga en cuenta que este generador no es perfecto (el número 0 tiene un 0.0064% más de posibilidades que cualquier otro número), pero para la mayoría de los propósitos prácticos, la garantía de tiempo constante probablemente supere esta inexactitud.
Explicación
Esta solución se deriva del hecho de que el número 15,624 es divisible por 7 y, por lo tanto, si podemos generar de manera aleatoria y uniforme números del 0 al 15,624 y luego tomar mod 7, podemos obtener un generador rand7 casi uniforme. Los números del 0 al 15,624 pueden generarse de manera uniforme haciendo rodar rand5 6 veces y usándolos para formar los dígitos de un número base 5 de la siguiente manera:
Sin embargo, las propiedades del mod 7 nos permiten simplificar un poco la ecuación:
Entonces
se convierte
Teoría
El número 15,624 no se eligió al azar, pero se puede descubrir utilizando el pequeño teorema de fermat, que establece que si p es un número primo, entonces
Entonces esto nos da,
(5 ^ 6) -1 es igual a
Este es un número en forma de base 5 y, por lo tanto, podemos ver que este método puede usarse para pasar de cualquier generador de números aleatorios a cualquier otro generador de números aleatorios. Aunque siempre se introduce un pequeño sesgo hacia 0 cuando se usa el exponente p-1.
Para generalizar este enfoque y ser más precisos, podemos tener una función como esta:
fuente
¿Se permiten problemas de tarea aquí?
Esta función realiza matemática cruda "base 5" para generar un número entre 0 y 6.
fuente
Si consideramos la restricción adicional de tratar de dar la respuesta más eficiente, es decir, una que da un flujo de entrada
I
, de enteros distribuidos uniformemente de longitudm
de 1 a 5 salidas, un flujoO
de enteros distribuidos uniformemente de 1 a 7 de la longitud relativa más larga am
, por ejemploL(m)
.La forma más sencilla de analizar esto es tratar los flujos I y
O
como números 5-ary y 7-ary respectivamente. Esto se logra mediante la idea de la respuesta principal de tomar la transmisióna1, a2, a3,... -> a1+5*a2+5^2*a3+..
y de manera similar para la transmisiónO
.Luego, si tomamos una sección de la secuencia de entrada de longitud
m choose n s.t. 5^m-7^n=c
dondec>0
y es lo más pequeña posible. Luego hay un mapa uniforme de la secuencia de entrada de longitud m a enteros de1
a5^m
y otra correspondencia uniforme de enteros de 17^n
a la secuencia de salida de longitud n donde es posible que tengamos que perder algunos casos de la secuencia de entrada cuando el entero asignado supera7^n
.Entonces esto da un valor
L(m)
de alrededor de lom (log5/log7)
cual es aproximadamente.82m
.La dificultad con el análisis anterior es la ecuación
5^m-7^n=c
que no es fácil de resolver con exactitud y el caso en que el valor uniforme desde1
que5^m
excede7^n
y perder eficiencia.La pregunta es qué tan cerca se puede alcanzar el mejor valor posible de m (log5 / log7). Por ejemplo, cuando este número se aproxima a un número entero, ¿podemos encontrar una manera de lograr este número entero exacto de valores de salida?
Si
5^m-7^n=c
a continuación de la corriente de entrada que generamos efectivamente un número aleatorio uniforme de0
a(5^m)-1
y no utilizamos cualquier valor más alto que7^n
. Sin embargo, estos valores pueden ser rescatados y utilizados nuevamente. Generan efectivamente una secuencia uniforme de números del 1 al5^m-7^n
. Entonces podemos intentar usarlos y convertirlos en números de 7 arios para poder crear más valores de salida.Si dejamos
T7(X)
que sea la longitud promedio de la secuencia de salida derandom(1-7)
enteros derivados de una entrada uniforme de tamañoX
, y suponiendo eso5^m=7^n0+7^n1+7^n2+...+7^nr+s, s<7
.Entonces,
T7(5^m)=n0x7^n0/5^m + ((5^m-7^n0)/5^m) T7(5^m-7^n0)
dado que tenemos una longitud sin secuencia con probabilidad 7 ^ n0 / 5 ^ m con un residual de longitud5^m-7^n0
con probabilidad(5^m-7^n0)/5^m)
.Si seguimos sustituyendo obtenemos:
Por lo tanto
Otra forma de decir esto es:
El mejor caso posible es mi original sobre dónde
5^m=7^n+s
, dóndes<7
.Entonces
T7(5^m) = nx(7^n)/(7^n+s) = n+o(1) = m (Log5/Log7)+o(1)
como antes.El peor de los casos es cuando solo podemos encontrar k y st 5 ^ m = kx7 + s.
Otros casos están en algún punto intermedio. Sería interesante ver qué tan bien podemos hacerlo para m muy grande, es decir, qué tan bueno podemos obtener el término de error:
Parece imposible de lograr
e(m) = o(1)
en general, pero esperamos poder demostrarloe(m)=o(m)
.Todo se basa en la distribución de los dígitos de 7 arios de
5^m
varios valores dem
.Estoy seguro de que hay mucha teoría por ahí que cubre esto, puedo echar un vistazo e informar en algún momento.
fuente
Aquí hay una implementación de Python en funcionamiento de la respuesta de Adam .
Me gusta lanzar algoritmos que estoy viendo en Python para poder jugar con ellos, pensé en publicarlo aquí con la esperanza de que sea útil para alguien allá afuera, no es que me tomó mucho tiempo armarlo.
fuente
rand5()
es un PRNG decente, entonces el ciclo no será infinito porque finalmente5*(rand5() - 1) + rand5()
será <= 21.)¿Por qué no hacerlo simple?
Las posibilidades de obtener 1 y 7 en esta solución son menores debido al módulo, sin embargo, si solo desea una solución rápida y legible, este es el camino a seguir.
fuente
Suponiendo que rand (n) aquí significa "entero aleatorio en una distribución uniforme de 0 a n-1 ", aquí hay una muestra de código usando randint de Python, que tiene ese efecto. Utiliza solo randint (5) y constantes para producir el efecto de randint (7) . Un poco tonto, en realidad
fuente
do ... while
. Podría haber sido1337
, o12345
, o cualquier número> 1.La premisa detrás de la respuesta correcta de Adam Rosenfield es:
Cuando n es igual a 2, tiene 4 posibilidades de descarte: y = {22, 23, 24, 25}. Si usa n es igual a 6, solo tiene 1 descarte: y = {15625}.
5 ^ 6 = 15625
7 * 2232 = 15624
Llamas a rand5 más veces. Sin embargo, tiene muchas menos posibilidades de obtener un valor de descarte (o un bucle infinito). Si hay una manera de no obtener un valor de descarte posible para y, todavía no lo he encontrado.
fuente
Aquí está mi respuesta:
Es un poco más complicado que otros, pero creo que minimiza las llamadas a rand5. Al igual que con otras soluciones, existe una pequeña probabilidad de que se pueda repetir durante mucho tiempo.
fuente
Simple y eficiente:
(Inspirado en ¿Cuál es tu dibujo animado favorito de "programador"? ).
fuente
Mientras no queden siete posibilidades para elegir, dibuje otro número aleatorio, que multiplique el número de posibilidades por cinco. En perl:
fuente
$possibilities
siempre tiene que crecer hasta 25 para salir del bucle y volver. Entonces, su primer resultado es[0-124] % 7
, que no se distribuye uniformemente porque125 % 7 != 0
(esto es 6, en realidad).No me gustan los rangos que comienzan desde 1, así que comenzaré desde 0 :-)
fuente
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
Ahí tienes, distribución uniforme y cero llamadas rand5.
Necesidad de establecer semillas de antemano.
fuente
Sé que ha sido respondido, pero parece que esto funciona bien, pero no puedo decirte si tiene un sesgo. Mi 'prueba' sugiere que es, al menos, razonable.
¿Quizás Adam Rosenfield sería tan amable de comentar?
Mi idea (ingenua) es esta:
Acumula rand5's hasta que haya suficientes bits aleatorios para hacer un rand7. Esto toma como máximo 2 rand5's. Para obtener el número rand7, uso el valor acumulado mod 7.
Para evitar que el acumulador se desborde, y dado que el acumulador es mod 7, entonces tomo el mod 7 del acumulador:
La función rand7 () sigue:
(Dejé que el rango de rand5 sea 0-4 y rand7 también sea 0-6).
Editar: Resultados agregados para 100 millones de ensayos.
Funciones rand 'reales' mod 5 o 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
Mi rand7
El promedio se ve bien y las distribuciones de números también se ven bien.
randt: promedio = 3.000080 0: 14288793 1: 14280135 2: 14287848 3: 14285277 4: 14286341 5: 14278663 6: 14292943
fuente
Hay algoritmos elegantes citados anteriormente, pero aquí hay una forma de abordarlo, aunque podría ser indirecto. Asumo valores generados a partir de 0.
R2 = generador de números aleatorios que proporciona valores inferiores a 2 (espacio muestral = {0, 1})
R8 = generador de números aleatorios que proporciona valores inferiores a 8 (espacio muestral = {0, 1, 2, 3, 4, 5, 6, 7 })
Para generar R8 a partir de R2, ejecutará R2 tres veces y utilizará el resultado combinado de las 3 ejecuciones como un número binario con 3 dígitos. Aquí está el rango de valores cuando R2 se ejecuta tres veces:
0 0 0 -> 0
.
.
1 1 1 -> 7
Ahora para generar R7 a partir de R8, simplemente ejecutamos R7 nuevamente si devuelve 7:
La solución indirecta es generar R2 a partir de R5 (al igual que generamos R7 a partir de R8), luego R8 a partir de R2 y luego R7 a partir de R8.
fuente
Aquí hay una solución que se ajusta completamente a enteros y está dentro de aproximadamente el 4% de lo óptimo (es decir, usa 1.26 números aleatorios en {0..4} para cada uno en {0..6}). El código está en Scala, pero las matemáticas deben ser razonablemente claras en cualquier idioma: aprovecha el hecho de que 7 ^ 9 + 7 ^ 8 está muy cerca de 5 ^ 11. Por lo tanto, elige un número de 11 dígitos en la base 5 y luego lo interpreta como un número de 9 dígitos en la base 7 si está dentro del rango (dando 9 números de base 7), o como un número de 8 dígitos si está por encima del número de 9 dígitos, etc. .:
Si pega una prueba en el intérprete (REPL en realidad), obtiene:
La distribución es agradable y plana (dentro de aproximadamente 10k de 1/7 de 10 ^ 8 en cada contenedor, como se esperaba de una distribución aproximadamente gaussiana).
fuente
Al usar un total rodante , ambos pueden
Ambos problemas son un problema con las
rand(5)+rand(5)...
soluciones de tipo simplista . El siguiente código de Python muestra cómo implementarlo (la mayor parte de esto es probar la distribución).Y esta salida muestra los resultados:
Una simplista
rand(5)+rand(5)
, ignorando aquellos casos en los que esto devuelve más de 6 tiene una variación típica del 18%, 100 veces la del método que se muestra arriba:Y, siguiendo los consejos de Nixuz, he limpiado el script para que pueda extraer y usar las
rand7...
cosas:fuente
Esta respuesta es más un experimento para obtener la mayor entropía posible de la función Rand5. Por lo tanto, no está claro y es casi seguro que es mucho más lento que otras implementaciones.
Suponiendo la distribución uniforme de 0-4 y la distribución uniforme resultante de 0-6:
El número de bits agregados al búfer por llamada a Rand5 es actualmente 4/5 * 2, entonces 1.6. Si se incluye el valor de probabilidad de 1/5 que aumenta en 0.05, entonces 1.65, pero vea el comentario en el código donde he tenido que desactivar esto.
Bits consumidos por llamada a Rand7 = 3 + 1/8 * (3 + 1/8 * (3 + 1/8 * (...
Esto es 3 + 3/8 + 3/64 + 3/512 ... entonces aproximadamente 3,42
Al extraer información de los sietes, reclamo 1/8 * 1/7 bits por llamada, por lo que aproximadamente 0.018
Esto proporciona un consumo neto de 3,4 bits por llamada, lo que significa que la proporción es de 2.125 llamadas a Rand5 por cada Rand7. El óptimo debe ser 2.1.
Me imagino que este enfoque es significativamente más lento que muchos de los otros aquí a menos que el costo de la llamada a Rand5 sea extremadamente costoso (por ejemplo, llamar a alguna fuente externa de entropía).
fuente
en php
realiza un bucle para producir un número aleatorio entre 16 y 127, se divide por dieciséis para crear un flotante entre 1 y 7.9375, luego se redondea hacia abajo para obtener un int entre 1 y 7. Si no me equivoco, hay una probabilidad de 16/112 de obtener cualquiera de los 7 resultados.
fuente
fuente
7 = 111b
conp(7) = 8 / 125
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:
Para las personas que no están familiarizadas con R, aquí hay una versión simplificada:
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:
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
:Aquí hay una versión simplificada:
Esto es esencialmente una iteración del método 1. Si generamos todas las combinaciones posibles, aquí están los conteos resultantes:
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:
Para las personas que no están familiarizadas con R, aquí hay una versión simplificada:
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:
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:Aquí hay una versión simplificada:
Si generamos todas las combinaciones posibles, aquí están los conteos resultantes:
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.fuente
i=7
tampoco tiene efecto, ya que agregar7*rand5()
ar
no cambia el valor delr
mod 7.)La función que necesita es rand1_7 () , escribí rand1_5 () para que pueda probarla y trazarla.
fuente