01 · Punto de partida
El déjà vu que invalida todo
Imaginá una simulación de un millón de clientes con un generador de período 65536: a los 65536 el azar empieza de nuevo, idéntico. Tus “clientes” 1 y 65537 son clones, las colas se sincronizan y los promedios mienten con cara seria. Peor: con réplicas paralelas sembradas “al azar”, dos streams pueden solaparse sin que nadie lo note.
Este tema cuantifica el daño: regla de margen, solapamiento de streams y cómo detectar el ciclo antes de que te detecte a vos.
- ¿Qué pasa exactamente cuando N supera al período?
- ¿Cuánto margen pedir entre período y consumo?
- ¿Por qué dos semillas “distintas” pueden solaparse?
- ¿Re-sembrar a mitad de camino arregla algo?
02 · Definición
Período, consumo y solape
Período p
Valores hasta repetir el estado: m = 16 → p ≤ 16; MT → 2¹⁹⁹³⁷−1. El Tema 10 lo midió; acá se sufre.
Consumo N
Valores que pide tu simulación: réplicas × pasos × variables. Se estima antes de elegir generador.
Solape
Dos streams que comparten un tramo: réplicas “independientes” calcadas en parte. Nace de sembrar sin esquema (Temas 37–38).
| Tu N | p mínimo (×1000) | Generador apto |
|---|---|---|
| 10⁶ | 10⁹ | LCG 2³¹ justo; PCG/MT sobrados |
| 10⁹ | 10¹² | PCG64 / MT / Philox; LCG 2³¹ no |
| 10¹² | 10¹⁵ | MT / Philox / PCG128; PCG32 no |
03 · Daños concretos
Qué rompe la repetición
Pasado p, la secuencia es un calco: medias que no convergen, varianzas que colapsan y tests que “pasan” sobre datos clonados. En paralelo es peor: con p = 2³¹ y 1000 streams de 10⁶ sembrados al azar, el solape esperado ya es apreciable.
- Clones en serie
- Cliente i y cliente i+p idénticos: la cola simulada se sincroniza en fase y los tiempos de espera se subestiman sistemáticamente.
- Solape en paralelo
- Semillas al azar ≈ puntos al azar en el ciclo: dos streams de largo L solapan con prob. ≈ 2L/p por par. Con p chico, casi seguro.
- Re-sembrar no salva
- Re-sembrar con el reloj o con
rand()rompe la reproducibilidad y puede achicar el ciclo efectivo (caés en un punto ya visitado). La cura es generador con p grande + streams (Temas 37–38).
Bien dimensionado
p ≫ N + streams
Nunca tocás el ciclo; cada réplica tiene flujo propio garantizado. La simulación escala sin sorpresas.
Mal dimensionado
N ≈ p o semillas al voleo
Clones silenciosos: todo “cierra” pero estima otro experimento. El error no avisa con varianza alta: avisa con réplicas idénticas.
04 · Ejemplos
Tres mordidas del ciclo
- 1Juguete m = 16, N = 100.
Seis ciclos completos: la “muestra” es el mismo disco 6 veces. Media y χ² mienten con seguridad.
- 2LCG 2³¹, N = 5·10⁹.
Dos vueltas al ciclo: la segunda mitad calca la primera. Resultado “estable” y falso.
- 3100 réplicas con
srand(tiempo).Dos arranques en el mismo segundo comparten semilla y stream: réplicas gemelas que achican la varianza fingida.
05 · Implementación en Python
Detectar el ciclo y medir el margen
Detectamos el período del juguete con un diccionario estado→índice y auditamos margen p/N para consumos reales.
Python en tu navegador. Cambiá la semilla: el período del LCG de período máximo no cambia; el transitorio sí.
def periodo_lcg(semilla, a=5, c=3, m=16):
visto = {}
x = semilla % m
i = 0
while x not in visto:
visto[x] = i
x = (a * x + c) % m
i += 1
if i > m + 5:
break
return i - visto[x], visto[x]
p, trans = periodo_lcg(7)
print("periodo:", p, "transitorio:", trans)
Período 16, transitorio 0: ciclo puro desde el arranque (LCG de período máximo).
Margen para tu N
def apto(periodo, n, factor=1000):
return periodo >= factor * n
for nombre, p in [("juguete m=16", 16), ("LCG 2**31", 2**31 - 1),
("PCG64", 2**64), ("MT", 2**19937 - 1)]:
print(nombre, "N=1e9:", apto(p, 10**9))
06 · Exploración
Laboratorio: ver el ciclo morder
Tres LCG didácticos con la secuencia dibujada: la línea vertical naranja marca el fin del primer período. Pedí N mayor que p y mirá cómo el calco se superpone exacto.
N contra p
si N ≥ p, repite exacto
Con p = 16 y N = 80 hay 5 ciclos exactos.
Pasada la línea naranja todo es calco: mismos valores, mismo orden. Ningún test 1D lo nota si el ciclo es parejo.
Preguntas para explorar
- Con m = 16 y N = 16: ¿cuántos distintos? ¿Y con N = 17?
- Con m = 1024 y N = 600: ¿hay repetición? ¿Qué margen p/N queda?
- Si tu N real fuera 10⁹, ¿qué generador del laboratorio serviría? ¿Cuál de verdad?
Ver respuestas sugeridas
- 16 y 16: el 17º repite el 1º. El distinto nº 17 no existe: el ciclo muerde.
- No hay repetición, margen 1,7×: pobrísimo (se exige ≥ 100×). Para jugar vale; para publicar, no.
- Ninguno del laboratorio: haría falta PCG64/MT/Philox (Tema 17) con p ≥ 10¹².
07 · Comprensión
Confusiones frecuentes
«Período largo = bueno»
RANDU tenía 2²⁹ y era malo en 3D. El período es el tamaño del tanque, no la calidad del combustible.
«Re-sembro cada tanto y estiro el período»
No: re-sembrar reinicia en un punto quizá ya visitado y rompe reproducibilidad. El período del generador no crece por sembrar más.
«Tomar % k achica el período, lo sé y lo acepto»
Peor: puede colapsarlo (los bits bajos de muchos LCG ciclan corto). El mapeo no solo sesga (Tema 22): puede recortar el ciclo.
«Con N = p/2 estoy a salvo»
Margen 2× es temerario: streams, remuestreos y bootstrap multiplican el consumo real. Exigí 100–1000×.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: el calco exacto
Generá 40 valores del juguete m = 16 y mostrá que los primeros 16 igualan a los siguientes 16.
x = 7
seq = []
for _ in range(40):
x = (5 * x + 3) % 16
seq.append(x)
print(seq[:16] == seq[16:32])
Ver solución razonada
True: calco perfecto. Toda estadística sobre N = 40 es la del ciclo de 16 contada 2,5 veces.
Ejercicio 2: dimensionar de verdad
Tu simulación pide 200 réplicas × 50000 pasos × 3 variables. ¿Sirve un LCG 2³¹? ¿Y PCG64?
n = 200 * 50000 * 3
for nombre, p in [("LCG 2**31", 2**31 - 1), ("PCG64", 2**64)]:
print(nombre, "N:", n, "margen:", round(p / n, 1))
Ver solución
N = 3·10⁷. LCG: margen ≈ 71× (corto, < 100×). PCG64: margen ≈ 6·10¹¹ (sobrado). Decisión: PCG o superior.
Ejercicio 3: gemelas por reloj
Simulá dos réplicas sembradas con el mismo segundo y mostrá que salen idénticas.
Ver una posible respuesta
import random
def replica(semilla):
rng = random.Random(semilla)
return [rng.random() for _ in range(5)]
print(replica(1718440000) == replica(1718440000))
True: mismo segundo, mismo stream, varianza entre réplicas = 0. Por eso se siembra por índice (Temas 37–38).
09 · Síntesis
Ideas para recordar
- N ≥ p repite exacto; exigir p ≥ 100–1000×N estimado antes de simular.
- Solape en paralelo: semillas al azar ≈ puntos al azar en el ciclo.
- Re-sembrar no estira p y rompe reproducibilidad.
- Período largo ≠ calidad (RANDU); % puede colapsar el ciclo.
- Cura: generador grande + streams por índice (Temas 17, 37–38).
En el próximo tema, otro sesgo silencioso: sesgos en la generación y sus causas.