01 · Punto de partida
Del umbral a la ruleta
En el Tema 1 generamos Bernoulli(p) con un umbral: 1 si U < p. Era una discreta con dos valores. Pero una demanda diaria puede valer {0,1,2,3,4}, un dado cargado {1,…,6} con probabilidades desiguales, una llegada Poisson {0,1,2,…} sin tope.
Todas se generan con la misma idea generalizada: partir el intervalo [0,1) en subintervalos de largo pk y devolver el valor cuyo intervalo atrapó a U. Es la inversión discreta, la ruleta del casino llevada a código.
- ¿Cómo se traduce una tabla de probabilidades en intervalos?
- ¿Qué fórmula única sirve para finitas e infinitas numerables?
- ¿Búsqueda lineal o binaria sobre acumuladas?
- ¿Cuándo conviene preprocesar con alias (Tema 13)?
02 · Definición
Masa, acumulada e inversa generalizada
Una discreta se define por su masa pk ≥ 0 con suma 1 y su acumulada escalonada F(x) = Σxk ≤ x pk. La inversa no existe como función lisa, pero sí como cuantil:
Intervalos
U cae en [Fk-1, Fk) con probabilidad Fk − Fk-1 = pk. El largo del intervalo es la probabilidad.
Exactitud
P(devolver xk) = P(Fk-1 ≤ U < Fk) = pk. No es aproximado: es exacto salvo error de punto flotante.
Costo
Buscar k cuesta O(n) lineal u O(log n) binaria. Con n grande y millones de muestras, ese costo manda (Temas 13–14 y 39).
Ejemplo concreto. Dado cargado con masa [0,10 · 0,20 · 0,30 · 0,25 · 0,10 · 0,05] para {1,…,6}. Acumuladas: 0,10 · 0,30 · 0,60 · 0,85 · 0,95 · 1,00. Si sale U = 0,73, recorremos: 0,73 > 0,60 pero ≤ 0,85 → devolvemos 4. Si U = 0,05 → 1.
| k | Valor xk | pk | Fk | Intervalo de U |
|---|---|---|---|---|
| 1 | 1 | 0,10 | 0,10 | [0,00 · 0,10) |
| 2 | 2 | 0,20 | 0,30 | [0,10 · 0,30) |
| 3 | 3 | 0,30 | 0,60 | [0,30 · 0,60) |
| 4 | 4 | 0,25 | 0,85 | [0,60 · 0,85) |
| 5 | 5 | 0,10 | 0,95 | [0,85 · 0,95) |
| 6 | 6 | 0,05 | 1,00 | [0,95 · 1,00) |
03 · Anatomía
Algoritmo general y sus variantes
- Preproceso (una vez)
- Validar que pk ≥ 0 y suman 1 (con tolerancia), ordenar valores si conviene y construir acumuladas Fk. Costo O(n).
- Muestreo (cada X)
- Generar U, buscar el primer k con Fk ≥ U, devolver xk. Lineal O(n), binaria O(log n) con
bisect. - Soporte infinito
- Geométrica, Poisson, binomial grande: no hay tabla finita. Se acumula sobre la marcha hasta superar U, o se usa fórmula cerrada / rechazo (Temas 18–20).
- Aceleración
- Si n es grande y N es enorme: alias O(1) tras O(n) de tablas Prob/Alias (Temas 13–14). Si p cambia en cada llamada, el preproceso no se amortiza.
Búsqueda lineal
Simple y rápida en n chico
Recorre F hasta superar U. Ideal si n ≤ 10 o si los valores más probables están primero (ponelos al inicio).
Búsqueda binaria
Escala a n grande
Usa bisect_left(F, U) sobre acumuladas ordenadas. Imprescindible con cientos de categorías y millones de réplicas.
04 · Casos típicos
Finitas, numerables y qué viene después
- 1Finitas directas.
Bernoulli, uniforme discreta, dado cargado, demanda empírica chica. Tabla completa + bisect. (Temas 16–17, 34–35)
- 2Numerables con fórmula.
Geométrica(p): k = 1 + piso(ln(1−U)/ln(1−p)). Evita acumular infinito con una inversión cerrada. (Tema 18)
- 3Numerables por acumulación.
Binomial(n,p) chico y Poisson(λ) chico: sumar pk hasta superar U. Simple pero O(media). (Temas 19–20)
- 4Masivas.
Miles de categorías o N millonario: alias O(1). El preproceso se paga una vez. (Temas 13–14)
05 · Representación en Python
Un generador discreto genérico
La misma función sirve para cualquier finita: recibe valores y probabilidades, valida, precalcula F y muestrea con bisect.
Python en tu navegador. Editá las probabilidades y reejecutá: la proporción debe seguir a la tabla cuando N es grande.
import random
import bisect
random.seed(5)
valores = [1, 2, 3, 4, 5, 6]
probs = [0.10, 0.20, 0.30, 0.25, 0.10, 0.05]
# Preproceso: acumuladas (forzar último = 1.0).
acum = []
s = 0.0
for p in probs:
s += p
acum.append(s)
acum[-1] = 1.0
def discreta():
u = random.random()
k = bisect.bisect_left(acum, u)
return valores[k]
muestra = [discreta() for _ in range(6000)]
for v in valores:
print(v, round(muestra.count(v) / len(muestra), 3))
Probá cambiar probs a seis veces 1/6: todas las frecuencias deben rondar 0,167. Volvé a la cargada y verificá que el 3 domine (~0,30).
Versión lineal didáctica (equivale, más lenta)
Útil para entender el recorrido intervalo por intervalo antes de usar bisect:
import random
random.seed(5)
valores = [1, 2, 3, 4, 5, 6]
probs = [0.10, 0.20, 0.30, 0.25, 0.10, 0.05]
def discreta_lineal():
u = random.random()
acc = 0.0
for v, p in zip(valores, probs):
acc += p
if u < acc:
return v
return valores[-1] # resguardo ante redondeo
print([discreta_lineal() for _ in range(10)])
06 · Exploración
Laboratorio: dado justo frente a dado cargado
Elegí la distribución y el N. Las barras muestran la frecuencia observada; los rombos amarillos, la probabilidad teórica. Con N grande las barras se pegan a los rombos; con N chico, el ruido domina.
Inversión discreta
X = mín{x : F(x) ≥ U}
Con N 2000, el histograma sigue a la tabla teórica.
Seis barras para {1,…,6}. El rombo marca la probabilidad teórica de cada cara. Probá N = 100 y luego N = 6000 para ver la convergencia.
Preguntas para explorar
- Con distribución cargada y N = 2000, ¿qué cara domina y a qué probabilidad teórica corresponde?
- Cambiá a justa con N = 100. ¿Alguna cara se desvía más de 0,05? ¿Es sesgo del método o ruido?
- Si U = 0,73 con la tabla cargada, ¿qué valor devuelve el algoritmo? Recorre las acumuladas a mano.
Ver respuestas sugeridas
- El 3, con p = 0,30 (acumulada 0,60). La barra del 3 debe ser la más alta, cerca de 0,30.
- Es ruido muestral: con N = 100 el error típico es del orden 1/√N ≈ 0,10. Al subir a 6000 se achica.
- 4, porque 0,60 < 0,73 ≤ 0,85 (intervalo del 4).
07 · Comprensión
Confusiones frecuentes
«Uso round() para llevar el uniforme al entero»
round(k·U) da n+1 valores con los extremos a mitad de probabilidad. Para {a,…,b} usá a + int((b−a+1)·U) con piso, no redondeo.
«Normalizo a ojo, total suma casi 1»
“Casi 1” deja un resto sin asignar o con doble asignación en el borde. Normalizá dividiendo por la suma y forzá la última acumulada a 1,0.
«Lineal siempre alcanza»
Con 6 caras sí; con 10 000 categorías y 10 millones de muestras, O(n) es prohibitivo. Ahí van binaria o alias (Temas 13–14, 39).
«Si el histograma se parece, ya está validado»
El ojo engaña con N chico. Complementá con error máximo y chi-cuadrado (Tema 38): el laboratorio ya los calcula.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: demanda con tabla dada
Demanda en {0,1,2,3} con probs [0,2 · 0,5 · 0,2 · 0,1]. Generá 5000 valores con semilla 9 y estimá P(demanda ≥ 2).
import random
import bisect
random.seed(9)
valores = [0, 1, 2, 3]
probs = [0.2, 0.5, 0.2, 0.1]
acum = []
s = 0.0
for p in probs:
s += p
acum.append(s)
acum[-1] = 1.0
muestra = [valores[bisect.bisect_left(acum, random.random())] for _ in range(5000)]
print(sum(1 for x in muestra if x >= 2) / len(muestra))
Ver solución razonada
La teórica es 0,2 + 0,1 = 0,30. La muestral debe caer en 0,28–0,32. Si da 0,5, probablemente contaste ≥ 1 en vez de ≥ 2.
Ejercicio 2: predecir antes de ejecutar
Con la tabla del dado cargado del laboratorio: si U = 0,05 · 0,30 · 0,60 · 0,97, ¿qué valores salen? Después verificá con código.
import bisect
acum = [0.10, 0.30, 0.60, 0.85, 0.95, 1.0]
valores = [1, 2, 3, 4, 5, 6]
for u in [0.05, 0.30, 0.60, 0.97]:
print(u, valores[bisect.bisect_left(acum, u)])
Ver solución
1, 2, 3 y 6. Notá los bordes: con bisect_left, U = 0,30 cae en el 2 (intervalo [0,10·0,30)) y U = 0,60 en el 3. La convención de borde debe ser consistente.
Ejercicio 3: detectar el sesgo del redondeo
Compará int(6U)+1 (correcto) contra round(5U)+1 o int(round(6U)) ingenuo con 20000 réplicas. ¿Qué caras se deforman?
Ver una posible respuesta
import random
random.seed(4)
N = 20000
bien = [1 + int(6 * random.random()) for _ in range(N)]
random.seed(4)
mal = [1 + round(5 * random.random()) for _ in range(N)]
def frec(m):
return [round(m.count(v) / len(m), 3) for v in range(1, 7)]
print("bien:", frec(bien))
print("mal: ", frec(mal))
El método bien da seis ≈ 0,167. El ingenuo con round(5U) concentra masa en el centro y afina los extremos (o los deforma según la variante): el histograma lo delata y el chi-cuadrado se dispara.
09 · Síntesis
Ideas para recordar
- Toda discreta se invierte con X = mín{x : F(x) ≥ U} sobre intervalos de largo pk.
- El preproceso construye Fk una vez; el muestreo busca k con lineal o binaria.
- Los bordes y la normalización exacta (Fn = 1) evitan valores perdidos.
- Soportes infinitos se acumulan sobre la marcha o usan fórmula cerrada / rechazo.
- Con n grande o N enorme, alias O(1) amortiza el preproceso.
En el próximo tema pasaremos al continuo: densidades, acumuladas lisas y la transformada inversa continua.