El generador de números aleatorios de Lehmer [ 1 ] (llamado así por DH Lehmer ), a veces también conocido como generador de números aleatorios de Park-Miller (por Stephen K. Park y Keith W. Miller ), es un tipo de generador congruencial lineal (LCG) que opera en el grupo multiplicativo de enteros módulo n . La fórmula general es
donde el módulo m es un número primo o una potencia de un número primo , el multiplicador a es un elemento de alto orden multiplicativo módulo m (por ejemplo, una raíz primitiva módulo n ), y la semilla X 0 es coprima con m .
Otros nombres son generador congruencial lineal multiplicativo (MLCG) [ 2 ] y generador congruencial multiplicativo (MCG) .
Parámetros de uso común
En 1988, Park y Miller [ 3 ] propusieron un generador de números aleatorios de Lehmer con parámetros particulares m = 2³¹ − 1 = 2.147.483.647 (un primo de Mersenne M³¹ ) y a = 7⁵ = 16.807 (una raíz primitiva módulo M³¹ ), ahora conocido como MINSTD . Aunque MINSTD fue criticado posteriormente por Marsaglia y Sullivan (1993), [ 4 ] [ 5 ] todavía se utiliza hoy en día (en particular, en CarbonLib y C++11 ) . Park, Miller y Stockmeyer respondieron a la crítica (1993), [ 6 ] diciendo:minstd_rand0
Dada la naturaleza dinámica del área, a quienes no son especialistas les resulta difícil decidir qué generador utilizar. «Necesito algo que pueda comprender, implementar y adaptar... no tiene por qué ser de última generación, solo que sea razonablemente bueno y eficiente». Nuestro artículo y el generador estándar mínimo asociado fueron un intento de responder a esta solicitud. Cinco años después, no vemos la necesidad de modificar nuestra respuesta, salvo sugerir el uso del multiplicador a = 48271 en lugar de 16807.
Esta constante revisada se utiliza en el generador de números aleatorios de C++11minstd_rand .
El Sinclair ZX81 y sus sucesores utilizan el generador de números aleatorios de Lehmer con parámetros m = 2 16 + 1 = 65 537 (un primo de Fermat F 4 ) y a = 75 (un módulo de raíz primitiva F 4 ). [ 7 ] [ 8 ] El generador de números aleatorios RANF de CRAY es un generador de números aleatorios de Lehmer con el módulo de potencia de dos m = 2 48 y a = 44 485 709 377 909. [ 9 ] La Biblioteca Científica GNU incluye varios generadores de números aleatorios de la forma Lehmer, incluidos MINSTD, RANF y el infame generador de números aleatorios RANDU de IBM . [ 9 ]
Elección del módulo
Lo más habitual es elegir el módulo como un número primo, lo que simplifica enormemente la elección de una semilla coprima (cualquier valor entre 0 < X y 0 < m es válido). Esto produce el resultado de mejor calidad, pero introduce cierta complejidad en la implementación, y es poco probable que el rango del resultado se ajuste a la aplicación deseada; para convertirlo al rango deseado se requiere una multiplicación adicional.
El uso de un módulo m que es una potencia de dos facilita una implementación informática particularmente conveniente, pero tiene un costo: el período es como máximo m /4, y los bits inferiores tienen períodos más cortos. Esto se debe a que los k bits más bajos forman por sí solos un generador de k bits con módulo 2 ; los bits de orden superior nunca afectan a los bits de orden inferior. [ 10 ] Los valores Xᵢ son siempre impares (el bit 0 nunca cambia), los bits 2 y 1 se alternan (los 3 bits inferiores se repiten con un período de 2), los 4 bits inferiores se repiten con un período de 4, y así sucesivamente. Por lo tanto, la aplicación que utiliza estos números aleatorios debe usar los bits más significativos; reducir el rango mediante una operación de módulo con un módulo par producirá resultados desastrosos. [ 11 ]
Para lograr este período, el multiplicador debe satisfacer a ≡ ±3 (mod 8), [ 12 ] y la semilla X 0 debe ser impar.
Es posible usar un módulo compuesto, pero el generador debe inicializarse con un valor coprimo a m , o el período se reducirá considerablemente. Por ejemplo, un módulo de F 5 = 2 32 + 1 podría parecer atractivo, ya que las salidas se pueden mapear fácilmente a una palabra de 32 bits 0 ≤ X i − 1 < 2 32 . Sin embargo, una semilla de X 0 = 6700417 (que divide a 2 32 + 1) o cualquier múltiplo daría como resultado una salida con un período de solo 640.
Otro generador con un módulo compuesto es el recomendado por Nakazawa y Nakazawa: [ 13 ]
- m =134 265 023 ×134 475 827 =18 055 400 005 099 021 ≈ 2 54
- a =7 759 097 958 782 935 (cualquiera de ± a ±1 (mod m ) también servirá)
Como ambos factores del módulo son menores que 2³² , es posible mantener el estado módulo cada uno de los factores y construir el valor de salida utilizando el teorema chino del resto , utilizando no más de 64 bits de aritmética intermedia. [ 13 ] : 70
Una implementación más popular para períodos grandes es un generador congruencial lineal combinado ; combinar (por ejemplo, sumando sus salidas) varios generadores es equivalente a la salida de un solo generador cuyo módulo es el producto de los módulos de los generadores componentes. [ 14 ] y cuyo período es el mínimo común múltiplo de los períodos componentes. Aunque los períodos compartirán un divisor común de 2, los módulos se pueden elegir de modo que sea el único divisor común y el período resultante sea ( m 1 − 1)( m 2 − 1)···( m k − 1)/2 k −1 . [ 2 ] : 744 Un ejemplo de esto es el generador de Wichmann-Hill .
Relación con LCG
Si bien el generador de números aleatorios de Lehmer (RNG de Lehmer ) puede considerarse un caso particular del generador congruencial lineal con c = 0 , se trata de un caso especial que implica ciertas restricciones y propiedades. En particular, para el RNG de Lehmer, la semilla inicial X₀ debe ser coprima con el módulo m , lo cual no se requiere para los generadores congruenciales lineales (LCG) en general. La elección del módulo m y del multiplicador a también es más restrictiva para el RNG de Lehmer. A diferencia de los LCG, el período máximo del RNG de Lehmer es igual a m − 1, y se cumple cuando m es primo y a es una raíz primitiva módulo m .
Por otro lado, los logaritmos discretos (en base a o cualquier raíz primitiva módulo m ) de X k enrepresentar una secuencia lineal congruencial módulo el totiente de Euler.
Implementación
Un módulo primo requiere el cálculo de un producto de doble ancho y un paso de reducción explícito. Si se utiliza un módulo ligeramente menor que una potencia de 2 (los primos de Mersenne 2 31 − 1 y 2 61 − 1 son populares, al igual que 2 32 − 5 y 2 64 − 59), la reducción módulo m = 2 e − d puede implementarse de forma más económica que una división general de doble ancho utilizando la identidad 2 e ≡ d (mod m ) .
El paso básico de reducción divide el producto en dos partes de e bits, multiplica la parte de mayor valor por d y las suma: ( ax mod 2e ) + d ⌊ ax /2 e ⌋ . A continuación, se resta m hasta que el resultado esté dentro del rango. El número de restas está limitado a ad / m , que se puede limitar fácilmente a una si d es pequeño y se elige a < m / d . (Esta condición también garantiza que d ⌊ ax /2 e ⌋ sea un producto de ancho simple; si no se cumple, se debe calcular un producto de ancho doble).
Cuando el módulo es un primo de Mersenne ( d = 1), el procedimiento es particularmente sencillo. No solo la multiplicación por d es trivial, sino que la resta condicional puede reemplazarse por un desplazamiento y una suma incondicionales. Para comprobarlo, observe que el algoritmo garantiza que x ≢ 0 (mod m ) , lo que significa que x = 0 y x = m son imposibles. Esto evita la necesidad de considerar representaciones equivalentes de e bits del estado; solo los valores cuyos bits superiores no son cero requieren reducción.
Los bits e bajos del producto ax no pueden representar un valor mayor que m , y los bits altos nunca tendrán un valor mayor que a − 1 ≤ m − 2. Por lo tanto, el primer paso de reducción produce un valor como máximo m + a − 1 ≤ 2 m − 2 = 2 e +1 − 4. Este es un número de ( e + 1) bits, que puede ser mayor que m (es decir, podría tener el bit e activado), pero la mitad alta es como máximo 1, y si lo es, los bits e bajos serán estrictamente menores que m . Por lo tanto, ya sea que el bit alto sea 1 o 0, un segundo paso de reducción (suma de las mitades) nunca desbordará los bits e , y la suma será el valor deseado.
Si d > 1, también se puede evitar la resta condicional, pero el procedimiento es más complejo. El desafío fundamental de un módulo como 2 32 − 5 radica en asegurar que produzcamos una única representación para valores como 1 ≡ 2 32 − 4. La solución consiste en sumar temporalmente d , de modo que el rango de valores posibles sea de d a 2 e − 1, y reducir los valores mayores que e bits de manera que nunca se generen representaciones menores que d . Finalmente, restando el desplazamiento temporal se obtiene el valor deseado.
Comencemos asumiendo que tenemos un valor parcialmente reducido y acotado de modo que 0 ≤ y < 2 m = 2 e +1 − 2 d . En este caso, un solo paso de resta de desplazamiento producirá 0 ≤ y ′ = (( y + d ) mod 2 e ) + d ⌊ ( y + d )/2 e ⌋ − d < m . Para ver esto, consideremos dos casos:
- 0 ≤ y < m = 2 e − d
- En este caso, y + d < 2 e y ′ = y < m , como se deseaba .
- m ≤ y < 2 m
- En este caso, 2 e ≤ y + d < 2 e +1 es un número de ( e + 1) bits, y ⌊ ( y + d )/2 e ⌋ = 1. Por lo tanto, y ′ = ( y + d ) − 2 e + d − d = y − 2 e + d = y − m < m , como se deseaba. Debido a que la parte superior multiplicada es d , la suma es al menos d , y restar el desplazamiento nunca causa subdesbordamiento.
(En el caso específico de un generador de Lehmer, un estado cero o su imagen y = m nunca ocurrirá, por lo que un desplazamiento de d − 1 funcionará igual de bien, si resulta más conveniente. Esto reduce el desplazamiento a 0 en el caso de los números primos de Mersenne, cuando d = 1).
Reducir un producto mayor ax a menos de 2 m = 2 e +1 − 2 d se puede hacer mediante uno o más pasos de reducción sin desplazamiento.
Si ad ≤ m , entonces un paso de reducción adicional es suficiente. Dado que x < m , ax < am ≤ ( a − 1)2 e , y un paso de reducción convierte esto en como máximo 2 e − 1 + ( a − 1) d = m + ad − 1. Esto está dentro del límite de 2 m si ad − 1 < m , que es la suposición inicial.
Si ad > m , entonces es posible que el primer paso de reducción produzca una suma mayor que 2 m = 2 e +1 − 2 d , que es demasiado grande para el paso de reducción final. (También requiere la multiplicación por d para producir un producto mayor que e bits, como se mencionó anteriormente). Sin embargo, siempre que d 2 < 2 e , La primera reducción producirá un valor dentro del rango requerido para que se aplique el caso anterior de dos pasos de reducción.
El método de Schrage
Si no se dispone de un producto de doble ancho, se puede utilizar el método de Schrage , [ 15 ] [ 16 ] también llamado método de factorización aproximada, [ 17 ] para calcular ax mod m , pero esto tiene un coste:
- El módulo debe poder representarse en un entero con signo ; las operaciones aritméticas deben permitir un rango de ± m .
- La elección del multiplicador a está restringida. Requerimos que m mod a ≤ ⌊ m / a ⌋ , lo cual se logra comúnmente eligiendo a ≤ √ m .
- Se requiere una división (con resto) por iteración.
Si bien esta técnica es popular para implementaciones portátiles en lenguajes de alto nivel que carecen de operaciones de doble ancho, [ 2 ] : 744 en computadoras modernas la división por una constante generalmente se implementa usando multiplicación de doble ancho, por lo que esta técnica debe evitarse si la eficiencia es una preocupación. Incluso en lenguajes de alto nivel, si el multiplicador a está limitado a √ m , entonces el producto de doble ancho ax puede calcularse usando dos multiplicaciones de ancho simple y reducirse usando las técnicas descritas anteriormente.
Para utilizar el método de Schrage, primero factorice m = qa + r , es decir, precalcule las constantes auxiliares r = m mod a y q = ⌊ m / a ⌋ = ( m − r )/ a . Luego, en cada iteración, calcule ax ≡ a ( x mod q ) − r ⌊ x / q ⌋ (mod m ) .
Esta igualdad se mantiene porque
Entonces, si factorizamos x = ( x mod q ) + q ⌊ x / q ⌋ , obtenemos:
La razón por la que no se desborda es que ambos términos son menores que m . Dado que x mod q < q ≤ m / a , el primer término es estrictamente menor que am / a = m y puede calcularse con un producto de ancho simple.
Si se elige a de modo que r ≤ q (y por lo tanto r / q ≤ 1), entonces el segundo término también es menor que m : r ⌊ x / q ⌋ ≤ rx / q = x ( r / q ) ≤ x (1) = x < m . Por lo tanto, la diferencia se encuentra en el rango [1− m , m −1] y puede reducirse a [0, m −1] con una sola suma condicional. [ 18 ]
Esta técnica puede extenderse para permitir un r negativo (− q ≤ r < 0), cambiando la reducción final a una resta condicional.
La técnica también puede extenderse para permitir valores de a mayores aplicándola recursivamente. [ 17 ] : 102 De los dos términos restados para producir el resultado final, solo el segundo ( r ⌊ x / q ⌋ ) corre el riesgo de desbordarse. Pero esto es en sí mismo una multiplicación modular por una constante de tiempo de compilación r , y puede implementarse con la misma técnica. Debido a que cada paso, en promedio, reduce a la mitad el tamaño del multiplicador (0 ≤ r < a , valor promedio ( a −1)/2), esto parecería requerir un paso por bit y ser espectacularmente ineficiente. Sin embargo, cada paso también divide x por un cociente cada vez mayor q = ⌊ m / a ⌋ , y rápidamente se llega a un punto donde el argumento es 0 y la recursión puede terminarse.
Código C99 de ejemplo
Utilizando código C , el generador de números aleatorios Park-Miller se puede escribir de la siguiente manera:
uint32_t lcg_parkmiller ( uint32_t * estado ) { return * estado = ( uint64_t ) * estado * 48271 % 0x7fffffff ; }Esta función puede invocarse repetidamente para generar números pseudoaleatorios, siempre que quien la invoque tenga cuidado de inicializar el estado con cualquier número mayor que cero y menor que el módulo. En esta implementación, se requiere aritmética de 64 bits; de lo contrario, el producto de dos enteros de 32 bits podría desbordarse.
Para evitar la división de 64 bits, realice la reducción manualmente:
uint32_t lcg_parkmiller ( uint32_t * estado ) { uint64_t producto = ( uint64_t ) * estado * 48271 ; uint32_t x = ( producto & 0x7fffffff ) + ( producto >> 31 );x = ( x & 0x7fffffff ) + ( x >> 31 ); return * estado = x ; }Para usar únicamente aritmética de 32 bits, utilice el método de Schrage:
uint32_t lcg_parkmiller ( uint32_t * state ) { // Parámetros precalculados para el método de Schrage const uint32_t M = 0x7fffffff ; const uint32_t A = 48271 ; const uint32_t Q = M / A ; // 44488 const uint32_t R = M % A ; // 3399uint32_t div = * estado / Q ; // máximo: M / Q = A = 48,271 uint32_t rem = * estado % Q ; // máximo: Q - 1 = 44,487int32_t s = rem * A ; // máx: 44,487 * 48,271 = 2,147,431,977 = 0x7fff3629 int32_t t = div * R ; // máx: 48,271 * 3,399 = 164,073,129 int32_t result = s - t ;si ( resultado < 0 ) resultado += M ;devolver * estado = resultado ; }o utilice dos multiplicaciones de 16×16 bits:
uint32_t lcg_parkmiller ( uint32_t * estado ) { const uint32_t A = 48271 ;uint32_t low = ( * state & 0x7fff ) * A ; // máx.: 32.767 * 48.271 = 1.581.695.857 = 0x5e46c371 uint32_t high = ( * state >> 15 ) * A ; // máx.: 65.535 * 48.271 = 3.163.439.985 = 0xbc8e4371 uint32_t x = low + (( high & 0xffff ) << 15 ) + ( high >> 16 ); // máx.: 0x5e46c371 + 0x7fff8000 + 0xbc8e = 0xde46ffffx = ( x & 0x7fffffff ) + ( x >> 31 ); return * estado = x ; }Otro generador de Lehmer popular utiliza el módulo primo 2 32 −5:
uint32_t lcg_rand ( uint32_t * estado ) { return * estado = ( uint64_t ) * estado * 279470273u % 0xfffffffb ; }Esto también se puede escribir sin una división de 64 bits:
uint32_t lcg_rand ( uint32_t * estado ) { uint64_t producto = ( uint64_t ) * estado * 279470273u ; uint32_t x ;// No es necesario porque 5 * 279470273 = 0x5349e3c5 cabe en 32 bits. // producto = (producto & 0xffffffff) + 5 * (producto >> 32); // Un multiplicador mayor que 0x33333333 = 858,993,459 lo necesitaría.// El resultado de la multiplicación cabe en 32 bits, pero la suma podría ser de 33 bits. producto = ( producto & 0xffffffff ) + 5 * ( uint32_t )( producto >> 32 );producto += 4 ; // Esta suma está garantizada para ser de 32 bits. x = ( uint32_t ) producto + 5 * ( uint32_t )( producto >> 32 ); return * estado = x - 4 ; }Muchos otros generadores de Lehmer tienen buenas propiedades. El siguiente generador de Lehmer módulo 2 128 requiere soporte de 128 bits del compilador y utiliza un multiplicador calculado por L'Ecuyer. [ 19 ] Tiene un período de 2 126 :
estado estático sin signo __int128 ;/* El estado debe inicializarse con un valor impar. */ void seed ( unsigned __int128 seed ) { state = seed << 1 | 1 ; }uint64_t next ( void ) { // GCC no puede escribir literales de 128 bits, así que usamos una expresión const unsigned __int128 mult = ( unsigned __int128 ) 0x12e15e35b500f16e << 64 | 0x2e714eb2b37916a5 ; state *= mult ; return state >> 64 ; }El generador calcula un valor impar de 128 bits y devuelve sus 64 bits superiores.
Este generador supera la prueba BigCrush de TestU01 , pero falla la prueba TMFn de PractRand . Dicha prueba se diseñó para detectar precisamente el defecto de este tipo de generador: dado que el módulo es una potencia de 2, el período del bit menos significativo en la salida es solo 2⁶² , en lugar de 2¹²⁶ . Los generadores congruenciales lineales con un módulo que es una potencia de 2 presentan un comportamiento similar.
La siguiente rutina principal mejora la velocidad del código anterior para cargas de trabajo con números enteros (si el compilador permite optimizar la declaración de la constante fuera de un bucle de cálculo):
uint64_t next ( void ) { uint64_t result = state >> 64 ; // GCC no puede escribir literales de 128 bits, así que usamos una expresión const unsigned __int128 mult = ( unsigned __int128 ) 0x12e15e35b500f16e << 64 | 0x2e714eb2b37916a5 ; state *= mult ; return result ; }Sin embargo, debido a que la multiplicación se difiere, no es adecuada para el hash, ya que la primera llamada simplemente devuelve los 64 bits superiores del estado de la semilla.
Referencias
- ↑ WH Payne; JR Rabung; TP Bogyo (1969). "Codificación del generador de números pseudoaleatorios de Lehmer" (PDF) . Communications of the ACM . 12 (2): 85– 86. doi : 10.1145/362848.362860 . S2CID 2749316 .
- 1 2 3 L'Ecuyer, Pierre (junio de 1988). "Generadores de números aleatorios combinados eficientes y portátiles" (PDF) . Communications of the ACM . 31 (6): 742– 774. doi : 10.1145/62959.62969 . S2CID 9593394 .
- ↑ Park, Stephen K.; Miller, Keith W. (1988). "Generadores de números aleatorios: los buenos son difíciles de encontrar" (PDF) . Communications of the ACM . 31 (10): 1192– 1201. doi : 10.1145/63039.63042 . S2CID 207575300 .
- ↑ Marsaglia, George (1993). "Correspondencia técnica: Observaciones sobre la elección e implementación de generadores de números aleatorios" (PDF) . Communications of the ACM . 36 (7): 105–108 . doi : 10.1145/159544.376068 . S2CID 26156905 .
- ↑ Sullivan, Stephen (1993). "Correspondencia técnica: otra prueba de aleatoriedad" (PDF) . Communications of the ACM . 36 (7): 108. doi : 10.1145/159544.376068 . S2CID 26156905 .
- ↑ Park, Stephen K.; Miller, Keith W.; Stockmeyer, Paul K. (1988). "Correspondencia técnica: Respuesta" (PDF) . Communications of the ACM . 36 (7): 108– 110. doi : 10.1145/159544.376068 . S2CID 26156905 .
- ↑ Vickers, Steve (1981). "Capítulo 5. Funciones" . Programación básica del ZX81 (2.ª ed.). Sinclair Research Ltd. Consultado el 21 de abril de 2024. El
ZX81 utiliza p=65537
y
a=75 [...]
(Tenga en cuenta que el manual del ZX81 indica erróneamente que 65537 es un número primo de Mersenne igual a 2¹⁶ − 1. El manual del ZX Spectrum corrigió ese error e indica correctamente que es un número primo de Fermat igual a 2¹⁶ + 1).
- ↑ Vickers, Steve (1983). "Capítulo 11. Números aleatorios" . Programación básica del Sinclair ZX Spectrum (2.ª ed.). Sinclair Research Ltd. págs. 73–75 . Recuperado el 26 de mayo de 2022.
El ZX Spectrum utiliza p=65537 y a=75, y almacena algunos bi-1 en la memoria.
- 1 2 Biblioteca Científica GNU: Otros generadores de números aleatorios .
- ↑ Knuth, Donald (1981). Algoritmos seminuméricos . El arte de la programación informática . Vol. 2 (2.ª ed.). Reading, MA: Addison-Wesley Professional. pp. 12–14 .
- ↑ Bique, Stephen; Rosenberg, Robert (mayo de 2009). Generación rápida de números pseudoaleatorios y permutaciones de alta calidad mediante MPI y OpenMP en el Cray XD1 . Grupo de usuarios de Cray 2009.
El dado se determina mediante aritmética modular, por ejemplo
,
... ¡La función RANF de CRAY solo lanza tres de los seis resultados posibles (qué tres caras dependen de la semilla)!
lrand48() % 6 + 1 - ↑ Greenberger, Martin (abril de 1961). "Notas sobre un nuevo generador de números pseudoaleatorios" . Journal of the ACM . 8 (2): 163– 167. doi : 10.1145/321062.321065 . S2CID 17196519 .
- 1 2 Nakazawa, Naoya; Nakazawa, Hiroshi (2025). "§7.1 El mejor generador MC actual n.° 001". Generador de números aleatorios en computadoras . págs. 67–71 . ISBN 978-1-003-41060-7. Nótese que la implementación de ejemplo no es óptima. En lugar de mantener variables de estado
mz1ymz2calcularmz1aymz2aen cada iteración, es más eficiente mantener estas últimas como variables de estado. Además, la reducción final módulo m (denominadaiden el libro) tiene un valor menor que 2 m , por lo que puede consistir en una única resta condicional. - ↑ L'Ecuyer, Pierre; Tezuka, Shu (octubre de 1991). "Propiedades estructurales para dos clases de generadores de números aleatorios combinados" (PDF) . Matemáticas de la computación . 57 (196): 735–746 . doi : 10.2307/2938714 . JSTOR 2938714 .
- ↑ Schrage, Linus (junio de 1979). "Un generador de números aleatorios Fortran más portátil" (PDF) . ACM Transactions on Mathematical Software . 5 (2): 132– 138. CiteSeerX 10.1.1.470.6958 . doi : 10.1145/355826.355828 . S2CID 14090729 .
- ↑ Jain, Raj (9 de julio de 2010). "Análisis del rendimiento de sistemas informáticos Capítulo 26: Generación de números aleatorios" (PDF) . págs. 19–22 . Recuperado el 31 de octubre de 2017 .
- 1 2 L'Ecuyer, Pierre; Côté, Serge (marzo de 1991). "Implementación de un paquete de números aleatorios con funciones de división" . ACM Transactions on Mathematical Software . 17 (1): 98–111 . doi : 10.1145/103147.103158 . S2CID 14385204 . Este trabajo explora varias implementaciones diferentes de la multiplicación modular por una constante.
- ^ Fenerty, Paul (11 de septiembre de 2006). "El método de Schrage" . Consultado el 31 de octubre de 2017 .
- ↑ L'Ecuyer, Pierre (enero de 1999). "Tablas de generadores congruenciales lineales de diferentes tamaños y buena estructura reticular" (PDF) . Matemáticas de la Computación . 68 (225): 249– 260. CiteSeerX 10.1.1.34.1024 . doi : 10.1090/s0025-5718-99-00996-5 .
- Lehmer, DH (1949). "Métodos matemáticos en unidades de computación a gran escala". Actas del Segundo Simposio sobre Maquinaria de Cálculo Digital a Gran Escala . págs. 141-146 . MR 0044899 . (Versión publicada en revista: Annals of the Computation Laboratory of Harvard University , vol. 26 (1951)).
- Steve Park, Generadores de números aleatorios
Enlaces externos
- Los números primos ligeramente menores que una potencia de dos pueden ser útiles para elegir módulos. Parte de Páginas de números primos .
- Generadores de números pseudoaleatorios
- aritmética modular