Números Pseudoaleatorios · Tema 27

Pruebas de uniformidad

Chi-cuadrado y chequeos de distribución: partir [0,1) en k cajas, contar y decidir con un número.

01 · Punto de partida

Del “parece parejo” al “pasa χ²”

El Tema 26 te dejó la alarma (media, varianza). Ahora la primera prueba formal: si partís [0,1) en k intervalos iguales y contás cuántos valores caen en cada uno, un generador uniforme debe llenarlos parejo salvo ruido. Chi-cuadrado convierte “parejo salvo ruido” en una cuenta con veredicto: pasa / se rechaza al 5 %.

Es la prueba más usada del curso y la base de las de frecuencia y series (Temas 29–30): mismo mecanismo, distintas cajas.

  • ¿Cómo se calcula χ² desde conteos?
  • ¿Cuántos intervalos k convienen para N valores?
  • ¿Qué valor separa “ruido normal” de “sesgo”?
  • ¿Qué significa rechazar al 5 %?

02 · Definición

El mecanismo en tres pasos

◈

Partir

k cajas iguales en [0,1): [0,1/k), [1/k,2/k), …. Regla: E = N/k ≥ 5 en cada caja.

⇄

Contar

Observados O₁ … Oₖ frente a esperado E parejo. La distancia es χ², no la suma de desvíos.

▣

Decidir

Grados gl = k−1; al 5 %, k = 6 → 11,07 y k = 10 → 16,92. χ² mayor = rechazo.

Valores críticos al 5 % (rechazar si χ² los supera).
k cajasglCrítico 5 %
6511,07
8714,07
10916,92
201930,14

03 · Procedimiento correcto

k, N y nivel sin improvisar

El test solo vale si el diseño es previo: elegir k antes de mirar los datos, con N/k ≥ 5 (ideal ≥ 10), y fijar el nivel α = 5 % de antemano. Con N = 6000 y k = 6, E = 1000: el ruido típico por caja es √1000 ≈ 32, y χ² ≈ 5 en un sano.

Hipótesis H₀
“La secuencia es U(0,1)”. χ² grande la pone en duda; χ² chico no la “prueba”: solo no la rechaza con estos datos.
Nivel 5 %
Aun con generador perfecto, 1 de cada 20 tests rechaza por mala suerte. Un rechazo aislado se repite con otra semilla antes de condenar.
k extremo
k = 2 no ve nada; k = 1000 con N = 2000 deja E = 2 y el test se rompe. Zona sana: k entre 6 y 20 para N en miles.

Bien testeado

k previo, E ≥ 5

Un χ², un veredicto al 5 %, repetido con 2–3 semillas. El informe trae k, N, χ² y crítico.

Mal testeado

k a posteriori

Probar k = 5…50 y publicar el que “da mal” (o bien) es trampa estadística: el 5 % deja de valer.

04 · Ejemplos

Tres veredictos típicos

Partir en k→Contar O vs E→χ² vs crítico
  1. 1
    Dado sano, N = 6000, k = 6.

    χ² ≈ 3–9 < 11,07: pasa. Cada cara ≈ 1000 ± 32.

  2. 2
    % con resto, m = 100, k = 6.

    Cuatro caras con 17/100: χ² ≈ 25 > 11,07: rechazo sistemático, no mala suerte.

  3. 3
    Juguete m = 16, N = 1600, k = 10.

    Solo 16 valores discretos sobre 10 cajas: χ² gigante. El test detecta resolución, no solo sesgo.

05 · Implementación en Python

χ² a mano, sin librerías

Contamos en k cajas, calculamos χ² y comparamos con la tabla al 5 %. Puro estándar: corre en Pyodide sin instalar nada.

Python en tu navegador. Cambiá k a 20 o N a 600 y mirá cómo se mueve χ² en el sano.

import math
import random

CRIT5 = {5: 11.07, 7: 14.07, 9: 16.92, 19: 30.14}

def chi2_uniforme(us, k=10):
    n = len(us)
    e = n / k
    obs = [0] * k
    for u in us:
        j = min(k - 1, int(u * k))
        obs[j] += 1
    chi = sum((o - e) ** 2 / e for o in obs)
    return chi, obs


rng = random.Random(27)
us = [rng.random() for _ in range(6000)]
chi, obs = chi2_uniforme(us, k=10)
print("obs:", obs)
print("chi2:", round(chi, 2), "critico:", CRIT5[9], "pasa:", chi < CRIT5[9])

χ² de un dígito bajo 16,92: pasa. Las cajas rondan 600 ± 25.

El sesgado no pasa

import random

CRIT5 = {5: 11.07, 7: 14.07, 9: 16.92, 19: 30.14}

def chi2_conteos(obs):
    e = sum(obs) / len(obs)
    return sum((o - e) ** 2 / e for o in obs)


rng = random.Random(27)
N = 6000
xs = [rng.randrange(100) for _ in range(N)]
dados = [(x % 6) for x in xs]
obs = [dados.count(c) for c in range(6)]
print("obs:", obs)
chi = chi2_conteos(obs)
print("chi2:", round(chi, 2), "critico:", CRIT5[5], "pasa:", chi < CRIT5[5])

06 · Exploración

Laboratorio: barras contra la línea

Elegí fuente y k: las barras son los conteos O, la línea es E = N/k. El panel trae χ², crítico al 5 % y veredicto. El “sesgado” es el % con resto del Tema 22.

EXPERIMENTO 27

χ² en vivo

χ² = Σ (O−E)² / E

Los resultados numéricos aparecen debajo.
χ² obs.—
Crítico 5 %—
E = N/k—
Veredicto—

Sano con k = 10: χ² de un dígito bajo el crítico.

Barras naranjas sistemáticamente altas delatan sesgo; barras que oscilan parejo alrededor de la línea son ruido honesto.

Preguntas para explorar

  1. Con sano y N = 12000, k = 10: ¿χ² crece o se mantiene? ¿Pasa igual?
  2. Con sesgado y k = 6: ¿χ² supera 11,07 siempre? ¿Y con k = 10?
  3. Con sano, repetí cambiando k entre 6 y 20: ¿algún k “rechaza” por mala suerte?
Ver respuestas sugeridas
  1. Se mantiene en su escala (χ² no crece con N si es sano): sigue pasando. Más N afina, no condena.
  2. Sí en ambas: el sesgo 17/16 es estructural y χ² crece con N. A N = 12000 el rechazo es contundente.
  3. Puede pasar 1 de cada 20 por diseño del 5 %: por eso se repite con otra semilla antes de condenar.

07 · Comprensión

Confusiones frecuentes

«p = 0,04 significa 4 % de chance de que sea uniforme»

No: p es P(χ² tan grande | H₀), no P(H₀ | datos). Al 5 % se rechaza por regla, sin convertir p en probabilidad de inocencia.

«Con k = 100 el test es más fino»

Solo si N acompaña (E ≥ 5): con N = 6000 y k = 100, E = 60 vale; con N = 600, E = 6 apenas. k se elige por N, no por ambición.

«No rechazó, quedó probado»

No rechazar ≠ certificar: solo “sin evidencia contra”. La certificación es batería (Temas 28–32) + réplicas.

«χ² chiquito es mejor»

χ² ≈ 0 sistemático también es sospechoso (datos “demasiado parejos”, posible amaño). Lo sano oscila alrededor de gl, no en 0.

08 · Práctica guiada

Ejercicios con Python

Ejercicio 1: dado sano

Test χ² a 6000 tiros de dado con k = 6 al 5 % (crítico 11,07). ¿Pasa?

import random

rng = random.Random(3)
N = 6000
tiros = [rng.randint(1, 6) for _ in range(N)]
obs = [tiros.count(c) for c in range(1, 7)]
e = N / 6
chi = sum((o - e) ** 2 / e for o in obs)
print(obs, round(chi, 2), chi < 11.07)
Ver solución razonada

χ² ≈ 2–10 con True: pasa. Si te da 11–14 una vez, repetí con otra semilla: 1 rechazo aislado en 20 es la mala suerte del 5 %.

Ejercicio 2: cazar el % viciado

Repetí el test sobre randrange(100) % 6 con N = 12000. ¿Pasa?

import random

rng = random.Random(3)
N = 12000
xs = [rng.randrange(100) % 6 for _ in range(N)]
obs = [xs.count(c) for c in range(6)]
e = N / 6
chi = sum((o - e) ** 2 / e for o in obs)
print(obs, round(chi, 2), chi < 11.07)
Ver solución

χ² ≈ 40–60 con False: rechazo contundente y repetible. El sesgo 17/16 no es mala suerte: es estructural (Tema 22).

Ejercicio 3: k que rompe el test

Mostrá que k = 500 con N = 2000 invalida el test (E < 5) aunque el generador sea sano.

Ver una posible respuesta
import random

rng = random.Random(3)
us = [rng.random() for _ in range(2000)]
k = 500
print("E =", len(us) / k)

E = 4,0 < 5: el test no aplica (χ² se vuelve inestable). Bajá a k = 20 (E = 100) y el veredicto vuelve a valer.

09 · Síntesis

Ideas para recordar

  • χ² = Σ (O−E)²/E con E = N/k, gl = k−1; rechazar si supera el crítico.
  • Diseño previo: k con E ≥ 5, α = 5 %, informe con k/N/χ²/crítico.
  • Un rechazo aislado se repite; tres rechazos condenan.
  • χ² no ve orden: uniformidad ≠ independencia.
  • χ² ≈ gl es lo sano; ≈ 0 sistemático también sospecha.

En el próximo tema atacamos la segunda pata: pruebas de independencia, detectar dependencias ocultas.