01 · Punto de partida
Adiós multiplicación, hola bits
Los LCG viven de a·x + c y un mod caro. En 2003 Marsaglia mostró otra vía: solo XOR y desplazamientos (<<, >>), las operaciones más baratas de la CPU. Tres líneas, sin multiplicar ni dividir, período 2³²−1 y velocidad endemoniada.
La idea es vieja (registros LFSR de hardware) pero el envase es nuevo: el xorshift32. Entenderlo te abre la puerta a todos los modernos (xoshiro, PCG usa parte de esto).
- ¿Qué hacen
^,<<y>>a los bits? - ¿Por qué tres xorshifts encadenados mezclan y uno solo no?
- ¿Por qué la semilla 0 es letal aquí también?
- ¿Por qué en Python hay que enmascarar con
0xFFFFFFFF?
02 · Definición
XOR, shifts y el trío mágico
XOR ^
Mezcla reversible bit a bit: 1^1=0, 1^0=1. Es lo que difunde un cambio de un bit a muchos.
Shifts
x<<k mueve bits a la izquierda (entra 0); x>>k a la derecha. Solos pierden info; con XOR la propagan.
Trío (13,17,5)
Izquierda-derecha-izquierda con esos corrimientos da período 2³²−1. No cualquier trío sirve: solo una lista validada (Marsaglia).
| Aspecto | LCG | Xorshift32 |
|---|---|---|
| Operaciones | mult + suma + mod | xor + shifts |
| Período | ≤ m | 2³²−1 (trío válido) |
| Semilla 0 | Mata al multiplicativo; mixto sobrevive | Mata a todos: 0→0→0 |
| Estado | Un entero | Un entero de 32 bits |
03 · Trampa de Python
Enmascarar o morir (en la portabilidad)
En C, uint32_t trunca solo a 32 bits. En Python los enteros son infinitos: x << 13 crece sin cortar y tu secuencia deja de ser xorshift32. La solución es enmascarar tras cada paso:
- Máscara
x &= 0xFFFFFFFFtras cada XOR-shift: simula el desborde de 32 bits.- Cero
- Si
x == 0, el siguiente es 0: validá semilla ≠ 0 al sembrar. - Salida
u = x / 2**32en [0,1). Nuncax % mcon otro m sin pensar (Tema 22).
Bien portado
32 bits siempre
Máscara en cada paso, período y secuencia idénticos a C. Reproducible entre lenguajes.
Sin máscara
Entero infinito
Funciona en Python pero es otro generador: no coincide con C ni tiene el período prometido.
04 · Ejemplos
Un bit cambia todo
- 1Semilla 12345.
Cascada de bits que en 3 pasos ya es irreconocible: difusión total.
- 2Semilla 12346 (un bit más).
Historia totalmente distinta desde el primer valor: sensibilidad extrema.
- 3Semilla 0.
0^0 = 0tres veces: secuencia muerta. Validar ≠ 0 es obligatorio.
05 · Implementación en Python
Xorshift32 fiel a C
Python en tu navegador. Quitá una máscara y observá cómo diverge de la referencia.
MASK = 0xFFFFFFFF
def xorshift32(semilla, n=6):
assert semilla != 0, "semilla 0 prohibida"
x = semilla & MASK
sal = []
for _ in range(n):
x ^= (x << 13) & MASK
x &= MASK
x ^= x >> 17
x ^= (x << 5) & MASK
x &= MASK
sal.append(x / 2**32)
return sal
print([round(v, 6) for v in xorshift32(12345)])
Seis uniformes irreconocibles desde 12345. Con 12346 sale otra historia desde el valor 1.
El cero mata
def paso(x):
x ^= (x << 13) & 0xFFFFFFFF
x &= 0xFFFFFFFF
x ^= x >> 17
x ^= (x << 5) & 0xFFFFFFFF
return x & 0xFFFFFFFF
print(paso(0))
06 · Exploración
Laboratorio: bits que mezclan
Elegí variante y semilla (incluido 0 para ver la muerte). La curva es u(n); el panel muestra los primeros valores, distintos y alerta de cero. Compará xorshift con el LCG didáctico.
Mezcla en 3 pasos
13 · 17 · 5 vs. LCG
Con semilla 12345 el xorshift cubre sin orden visible.
Línea plana en cero = semilla prohibida. Todo lo demás debe cubrir sin ciclos visibles en esta ventana.
Preguntas para explorar
- Poné semilla 0 en xorshift: ¿qué sale? ¿Y en LCG mixto con semilla 0?
- Cambiá de 12345 a 12346 en xorshift: ¿se parecen las historias? ¿Qué dice de la difusión?
- Si olvidaras la máscara en Python, ¿seguiría dando en [0,1)? ¿Sería el mismo generador?
Ver respuestas sugeridas
- Xorshift: todo cero (muerto). LCG mixto: sobrevive por el +3. Distinta familia, distinta trampa.
- No se parecen en nada desde el valor 1: un bit cambia la cascada entera. Esa es la difusión buscada.
- Podría salirse de rango o divergir de C: sería otro algoritmo con otro período. La máscara es especificación.
07 · Comprensión
Confusiones frecuentes
«Cualquier trío de shifts sirve»
Solo los validados dan período máximo y buenas propiedades. Un trío improvisado puede ciclar corto o mezclar pobre.
«En Python no necesito máscara»
La necesitas para ser xorshift32 de verdad y coincidir con C. Sin ella es otro bicho con enteros infinitos.
«Xorshift es criptográfico por ser de bits»
No: es lineal y predecible con pocas observaciones. Para seguridad se exige otro diseño (Tema 18).
«Shifts a la derecha con signo dan igual»
En lenguajes con signo, >> puede replicar el bit de signo. Se opera en sin signo de 32 bits.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: un paso a mano
Partiendo de x = 1, aplicá los 3 xorshifts con máscara y verificá que da 270369.
x = 1
x ^= (x << 13) & 0xFFFFFFFF
x &= 0xFFFFFFFF
x ^= x >> 17
x ^= (x << 5) & 0xFFFFFFFF
x &= 0xFFFFFFFF
print(x)
Ver solución razonada
Debe dar 270369 (valor clásico de referencia). Si te da otro, revisá máscaras intermedias, no solo la final.
Ejercicio 2: el cero
Demostrá que el 0 es punto fijo absorbente para cualquier trío.
def paso(x, a=13, b=17, c=5):
x ^= (x << a) & 0xFFFFFFFF
x &= 0xFFFFFFFF
x ^= x >> b
x ^= (x << c) & 0xFFFFFFFF
return x & 0xFFFFFFFF
print(paso(0))
Ver solución
Da 0 siempre: 0^0=0 y shifts de 0 son 0. Por eso el estado nunca debe ser todo ceros (en versiones mult palabra, ninguna palabra toda cero según diseño).
Ejercicio 3: sensibilidad
Compará las primeras 5 salidas desde 12345 y 12346. ¿Cuántas coinciden?
Ver una posible respuesta
def xs(s, n=5):
x = s
sal = []
for _ in range(n):
x ^= (x << 13) & 0xFFFFFFFF
x &= 0xFFFFFFFF
x ^= x >> 17
x ^= (x << 5) & 0xFFFFFFFF
x &= 0xFFFFFFFF
sal.append(round(x / 2**32, 6))
return sal
print(xs(12345))
print(xs(12346))
Ninguna (o casi): la difusión es total desde el primer paso. Es lo que lo hace útil y a la vez impredecible a ojo.
09 · Síntesis
Ideas para recordar
- Xorshift32: 3 XOR-shifts (13,17,5) en 32 bits, u = x/2³².
- Rápido y período 2³²−1 con trío válido; cero prohibido.
- En Python, máscara en cada paso para ser fiel a C.
- Crudo es bueno pero lineal: los modernos agregan scrambling.
- No es criptográfico aunque sea «de bits».
En el próximo tema veremos el estado del arte: generadores modernos (Mersenne Twister, PCG, xoshiro, Philox).