Números Pseudoaleatorios · Tema 36

Reproducibilidad de experimentos

Semillas fijas y registro de versiones: que otro (o vos en seis meses) repita tu simulación bit a bit.

01 · Punto de partida

Sin semilla anotada no hay ciencia

Un resultado de simulación sin semilla registrada es una anécdota: nadie puede verificarlo, ni siquiera vos cuando el revisor pida “la figura 3 otra vez”. La reproducibilidad es el contrato que convierte corridas en evidencia: mismos datos, mismo código, misma semilla → mismos números.

Y no alcanza con la semilla: el generador, su versión y el orden de consumo también cuentan. Este tema fija el protocolo mínimo que usarás en el proyecto final (Tema 45).

  • ¿Qué hay que registrar además de la semilla?
  • ¿Por qué la misma semilla puede dar distinto en otra versión?
  • ¿Cómo congelar y retomar una secuencia a mitad de camino?
  • ¿Qué nunca debe llevar semilla fija?

02 · Definición

El cuarteto de la repetición

◈

Semilla

El número que fija la secuencia: seed(2026). Una por experimento, distinta por réplica (Temas 37–38).

⇄

Generador + versión

MT, PCG, NumPy 1.26…: el mismo seed en otro generador da otra secuencia. Registrar qué y cuál versión.

▣

Orden de consumo

El n-ésimo valor depende de cuántos se pidieron antes: cambiar el orden de los sorteos cambia todo lo posterior.

Qué registrar en cada corrida.
CampoEjemploSi falta…
Semilla2026Imposible repetir
GeneradorMT / PCG64Otra secuencia con mismo seed
VersionesPython 3.12, NumPy 1.26Cambios silenciosos de algoritmo
Orden / códigocommit + script de corridasMismo seed, distinto consumo

03 · Protocolo mínimo

Del cuaderno al config

El patrón profesional: un diccionario EXPERIMENTO (o YAML/JSON) con semilla, generador, versiones y parámetros, guardado junto a los resultados. Para pausar y retomar: getstate/setstate serializado a disco.

Una semilla madre
Sembrar una vez al inicio (SeedSequence(2026) en NumPy) y derivar las de cada réplica por índice: trazable y sin colisiones (Temas 37–38).
Estado congelable
random.getstate() captura las 624 palabras de MT: guardarlo permite retomar bit a bit tras un corte de luz.
Sorteso con premio: afuera
Lo reproducible es verificable… y predecible: ningún sorteo con incentivo lleva semilla publicada (Tema 18). Ciencia sí, secreto no.

Bien documentado

Config + seed + versión

Cualquiera repite tus figuras con un comando. El bug se reproduce, se arregla y se verifica.

Mal documentado

“seed = hora, creo”

Números huérfanos: ni el autor los repite. La revisión los descarta y el paper tambalea.

04 · Ejemplos

Tres contratos cumplidos

Config→Sembrar→Guardar todo
  1. 1
    Paper con figura.

    {semilla: 2026, gen: PCG64, numpy: 1.26, n: 5000} junto al PNG: el revisor repite la figura.

  2. 2
    Bug en la réplica 137.

    Con semilla por índice se re-corre solo la 137 y el bug aparece: depuración quirúrgica.

  3. 3
    Clase con 30 alumnos.

    Misma semilla para todos: el docente sabe qué números debe ver cada uno y detecta copias y errores.

05 · Implementación en Python

Config, estado y repetición

Corrida empaquetada: diccionario de experimento, secuencia repetible y congelamiento del estado a mitad de camino.

Python en tu navegador. Cambiá la semilla y el hash cambia; restaurala y vuelve.

import random
import sys

EXP = {"semilla": 2026, "gen": "MT", "python": sys.version.split()[0], "n": 5}
rng = random.Random(EXP["semilla"])
print(EXP)
print([round(rng.random(), 6) for _ in range(EXP["n"])])

Mismo dict, mismos 5 números, en cualquier máquina con este Python: contrato cumplido.

Congelar y retomar

import random

rng = random.Random(99)
print([round(rng.random(), 4) for _ in range(3)])
estado = rng.getstate()
a = [rng.random() for _ in range(3)]
rng.setstate(estado)
b = [rng.random() for _ in range(3)]
print(a == b)

06 · Exploración

Laboratorio: repetir o variar

Dos corridas con la semilla que elijas, dibujadas superpuestas: con la misma semilla coinciden punto por punto (distancia 0); con distinta, divergen. El panel trae hash de cada corrida y veredicto.

EXPERIMENTO 36

Misma semilla, misma curva

seed igual → distancia 0

Los resultados numéricos aparecen debajo.
Hash corrida A—
Hash corrida B—
Distancia máx—
Veredicto—

A = B = 2026: curvas idénticas, distancia 0.

Dos curvas superpuestas que se ven como una sola = reproducibilidad perfecta. Curvas distintas = semillas distintas.

Preguntas para explorar

  1. Con A = B: ¿la distancia es exactamente 0? ¿El hash coincide?
  2. Cambiá B en 1 (2027): ¿cuánto se parecen las curvas? ¿Qué dice de la “cercanía” de semillas?
  3. Si cambiás N a 240 con A = B: ¿siguen idénticas? ¿Por qué el orden de consumo importa?
Ver respuestas sugeridas
  1. Sí, 0 bit a bit: determinismo total. El hash (suma ponderada) coincide.
  2. Nada: 2026 y 2027 dan historias independientes desde el primer valor. Cercanía numérica ≠ cercanía de secuencia.
  3. Sí: la repetición vale para todo N. Pero si entre corridas pedís un valor extra, todo lo posterior diverge: el orden manda.

07 · Comprensión

Confusiones frecuentes

«Fijé semilla, soy reproducible»

Falta generador + versión + orden: la misma semilla en NumPy 1 y 2 puede dar distinto. El cuarteto completo o nada.

«Semillas cercanas dan secuencias parecidas»

No: 2026 y 2027 divergen desde el valor 1 (difusión del Tema 16). La cercanía numérica no existe en el espacio de secuencias.

«Guardo los resultados, la semilla no importa»

Sin semilla no hay re-corridas ante un bug, ni réplicas extra comparables, ni defensa ante revisores. Guardar todo cuesta un JSON.

«secrets con semilla fija es reproducible y seguro»

secrets no acepta semilla a propósito (Tema 18): fijarla (si pudieras) lo volvería predecible. Reproducible o secreto, no ambos.

08 · Práctica guiada

Ejercicios con Python

Ejercicio 1: contrato mínimo

Corré dos veces el mismo experimento con semilla 2026 y verificá igualdad bit a bit.

import random

def corrida(semilla=2026, n=5):
    rng = random.Random(semilla)
    return [rng.random() for _ in range(n)]


print(corrida() == corrida())
Ver solución razonada

True: determinismo puro. Sin el seed daría False casi siempre (entropía distinta por corrida).

Ejercicio 2: el orden manda

Mostrá que pedir un valor extra en el medio cambia todo lo posterior aunque la semilla sea la misma.

import random

rng1 = random.Random(5)
a = [rng1.random() for _ in range(3)]
rng2 = random.Random(5)
rng2.random()
b = [rng2.random() for _ in range(3)]
print([round(v, 4) for v in a])
print([round(v, 4) for v in b])
print(a == b)
Ver solución

False: b es a corrida en uno. El orden de consumo es parte del contrato: un sorteo extra “inocente” invalida la réplica.

Ejercicio 3: manifiesto del experimento

Armá el JSON del manifiesto con semilla, generador, versiones y hash de resultados.

Ver una posible respuesta
import hashlib
import json
import random
import sys

manifiesto = {"semilla": 2026, "gen": "MT", "python": sys.version.split()[0]}
rng = random.Random(manifiesto["semilla"])
res = [rng.random() for _ in range(100)]
manifiesto["sha"] = hashlib.sha256(json.dumps(res).encode()).hexdigest()[:16]
print(json.dumps(manifiesto))

El sha certifica los 100 valores: cualquiera con el manifiesto verifica la corrida sin reenviar datos.

09 · Síntesis

Ideas para recordar

  • Contrato: semilla + generador + versión + orden de consumo.
  • Una semilla madre + derivadas por índice; reloj nunca.
  • getstate/setstate para congelar y retomar.
  • Semillas cercanas ≠ secuencias parecidas.
  • Sorteo con premio: sin semilla publicada (Tema 18).

En el próximo tema repartiremos azar sin choques: múltiples secuencias independientes por componente.