01 · Punto de partida
El LCG ya no alcanza
Con LCG y xorshift aprendiste lo esencial, pero la práctica moderna los jubiló para uso general: períodos de 2³¹ o 2³² se agotan en segundos, las ternas caen en planos (Marsaglia) y los tests duros como BigCrush los rompen. Una simulación seria consume miles de millones de valores: necesita período gigante, calidad probada y flujos paralelos.
La respuesta vino en tres oleadas: Mersenne Twister (1997), el caballo de batalla de Python; PCG (2014), pequeño, rápido y con streams; y Philox / counter-based (2011), pensado para GPU y paralelismo masivo.
- ¿Por qué un período de 2³² es hoy un juguete?
- ¿Qué ganás con un estado de 624 palabras frente a un solo entero?
- ¿Qué es el scrambling de salida y por qué lo usan PCG y Twister?
- ¿Cómo generás 8 flujos independientes sin 8 generadores distintos?
02 · Definición
Tres diseños, tres filosofías
MT19937
624 uint32 de estado, período 2¹⁹⁹³⁷−1 (primo de Mersenne). Recurre con twist y templa la salida. El random de Python.
PCG32 / PCG64
LCG de 64/128 bits + permutación XSH-RR. Período 2⁶⁴, estado mínimo, 2⁶³ streams vía incremento impar. Default de NumPy.
Philox
Counter-based: salida = F(contador, llave) con rondas tipo cifrado. Salto O(1), ideal GPU/paralelo. Período 2¹²⁸.
| Aspecto | LCG / Xorshift32 | MT19937 | PCG / Philox |
|---|---|---|---|
| Período | ≤ 2³² | 2¹⁹⁹³⁷−1 | 2⁶⁴ a 2¹²⁸ |
| Estado | 1 entero | 624 enteros | 1–4 enteros + llave |
| Streams paralelos | No (partir a mano) | Difícil (jump costoso) | Sí (nativo) |
| Python | Didáctico | random.Random | numpy default_rng / Philox |
03 · Cómo funcionan
Estado, tempering y contadores
Los tres rompen la linealidad visible con una permutación final. Sin ella verías la estructura del estado a simple vista (planos del LCG, linealidad del xorshift crudo del Tema 16).
- Twist + tempering (MT)
- Cada 624 pasos mezcla todo el vector (twist); cada salida pasa por 4 XOR-shifts con máscaras que la “peinan”. Equidistribución en 623 dimensiones.
- Permutación XSH-RR (PCG)
- Avanza un LCG de 64 bits y emite solo 32 bits rotados por los bits altos:
xorshifted = ((s >> 18) ^ s) >> 27; rot = s >> 59. Barato y pasa BigCrush. - Rondas de cifrado (Philox)
- No hay estado que avance: la salida n es una función invertible del contador n y la llave. Pedir
n + 10⁹cuesta lo mismo que pedirn + 1.
Estado que camina
MT y PCG
Secuencial: el valor n+1 depende del estado n. Rápidos en CPU, pero saltar N pasos exige avanzar o usar jump-ahead especial.
Contador que cifra
Philox
Aleatorio-accsible: cada índice es independiente. 8 hilos piden F(0..7, key) sin hablarse. El precio es más cómputo por valor.
04 · Ejemplos
Quién usa qué
- 1Python
random= MT.random.seed(42)inicia 624 palabras. Ideal para docencia y reproducibilidad clásica; estado pesado de guardar. - 2NumPy
default_rng= PCG64.Estado chico, streams con
SeedSequence.spawn. Lo que deberías usar hoy en simulaciones nuevas. - 3JAX / GPU = Philox / ThreeFry.
Cada réplica pide su bloque del contador con su llave: paralelismo sin solapes ni comunicación.
05 · Implementación en Python
Los tres en tu navegador
Python trae MT de fábrica. PCG y Philox los ilustramos en puro Python (fieles al espíritu, sin NumPy) para que el bloque corra en Pyodide sin instalar nada.
Python en tu navegador. Cambiá la semilla y compará: MT repite exacto, PCG cambia de stream con otro incremento, el contador salta gratis.
import random
random.seed(12345)
print([round(random.random(), 6) for _ in range(5)])
print("estado MT: %d enteros" % len(random.getstate()[1]))
print("periodo: 2**19937 - 1")
MT con semilla 12345: cinco uniformes estables y un estado de 624 enteros. Guardar ese estado pesa; PCG guarda 2.
PCG32 mínimo y contador tipo Philox
MASK64 = (1 << 64) - 1
MULT = 6364136223846793005
def pcg32(semilla, seq=1, n=5):
inc = ((seq << 1) | 1) & MASK64
state = 0
def paso():
nonlocal state
old = state
state = (old * MULT + inc) & MASK64
xs = ((old >> 18) ^ old) >> 27
rot = old >> 59
return ((xs >> rot) | (xs << ((-rot) & 31))) & 0xFFFFFFFF
paso()
state = (state + semilla) & MASK64
paso()
return [paso() / 2**32 for _ in range(n)]
def philox_like(contador, llave):
z = (contador + llave + 0x9E3779B97F4A7C15) & MASK64
z = ((z ^ (z >> 30)) * 0xBF58476D1CE4E5B9) & MASK64
z = ((z ^ (z >> 27)) * 0x94D049BB133111EB) & MASK64
return ((z ^ (z >> 31)) >> 32) / 2**32
print([round(v, 6) for v in pcg32(12345)])
print([round(philox_like(i, 999), 6) for i in range(5)])
print(round(philox_like(10**9, 999), 6), "<- salto a mil millones sin iterar")
06 · Exploración
Laboratorio: cuatro generadores cara a cara
Elegí generador y semilla. La curva es u(n); el panel muestra el primer valor, cuántos distintos hay en la ventana y el período teórico. Probá semilla 0 y saltos: el LCG testigo cicla visible, los modernos no.
Período y paralelismo
LCG · MT · PCG · Philox
MT con semilla 12345: cobertura sin orden visible.
En esta ventana corta todos cubren; la diferencia real es el período, el tamaño de estado y el salto paralelo. Philox puede pedir el índice mil millones sin iterar.
Preguntas para explorar
- Cambiá de MT a LCG con la misma semilla: ¿se parecen los primeros valores? ¿Qué dice de la portabilidad entre generadores?
- Poné N = 240 en PCG y en LCG: ¿ves ciclos en alguno? ¿Por qué la ventana es demasiado corta para MT/PCG/Philox?
- Si necesitaras 8 hilos paralelos, ¿por qué Philox/PCG con streams es más seguro que partir un LCG a mano?
Ver respuestas sugeridas
- No se parecen en nada: cada familia es otro algoritmo. La reproducibilidad exige fijar generador + semilla + versión (Tema 36).
- No ves ciclos: 240 « 2⁶⁴ « 2¹⁹⁹³⁷. El LCG chico didáctico sí ciclaría con módulo pequeño; el testigo de 2³¹ tampoco se agota aquí.
- Partir a mano arriesga solapes; PCG da streams por incremento y Philox por llave/contador con garantía matemática de no solape.
07 · Comprensión
Confusiones frecuentes
«MT es lo mejor porque su período es el más largo»
Período largo no implica mejor calidad ni velocidad: MT tiene estado enorme, siembra lenta y falla algunos tests lineales. PCG con 2⁶⁴ sobra para casi todo y es más liviano.
«PCG es solo un LCG, entonces es malo»
El motor es un LCG grande, pero la permutación de salida rompe la estructura visible. Pasa BigCrushCrush con nota alta; el LCG crudo no.
«Philox desperdicia cómputo por valor»
Hace más cuentas por número, pero gana salto O(1) y paralelismo sin comunicación: en GPU y simulaciones masivas termina más rápido en tiempo mural.
«Si es moderno sirve para criptografía»
No: MT, PCG y Philox son predecibles con observación suficiente. Seguridad exige CSPRNG del sistema operativo (Tema 18).
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: el peso del estado MT
Sembrá random con 42, pedí un valor, guardá el estado con getstate y restaurá con setstate: el siguiente valor debe repetirse exacto.
import random
random.seed(42)
print(round(random.random(), 6))
est = random.getstate()
a = random.random()
random.setstate(est)
b = random.random()
print(a == b, round(a, 6))
Ver solución razonada
Debe dar True: el estado captura las 624 palabras + índice. Por eso reproducir exige guardar más que la semilla si partiste la secuencia.
Ejercicio 2: streams PCG sin solape
Generá 5 valores con pcg32(7, seq=1) y con seq=2 (código de la sección 05). ¿Comparten valores?
# reutiliza pcg32() de la seccion 05
print([round(v, 6) for v in pcg32(7, seq=1)])
print([round(v, 6) for v in pcg32(7, seq=2)])
Ver solución
No comparten: distinto incremento impar implica distinto stream de período 2⁶⁴. Así se asigna un stream por componente (Tema 37).
Ejercicio 3: salto gratis del contador
Pedí philox_like(0, 999), philox_like(1, 999) y philox_like(10**12, 999). ¿Cuánto costaría llegar al índice 10¹² con un LCG?
Ver una posible respuesta
# reutiliza philox_like() de la seccion 05
print(round(philox_like(0, 999), 6))
print(round(philox_like(1, 999), 6))
print(round(philox_like(10**12, 999), 6))
El contador lo da en O(1); el LCG exigiría 10¹² iteraciones o un salto algebraico especial. Esa es la ventaja para paralelo (Tema 38).
09 · Síntesis
Ideas para recordar
- MT19937: estado 624, período 2¹⁹⁹³⁷−1, el
randomde Python; pesado pero ubicuo. - PCG: LCG grande + XSH-RR, período 2⁶⁴, streams por incremento; default moderno (NumPy).
- Philox: contador + llave con rondas, salto O(1) y paralelismo nativo.
- Reproducibilidad = generador + semilla + versión; el período solo no hace calidad.
- Ninguno es criptográfico: seguridad y velocidad son objetivos distintos.
En el próximo tema separaremos aguas: generadores criptográficos frente a generadores para simulación, velocidad contra imprevisibilidad.