01 · Punto de partida
Del unitario al mundo real
El Tema 20 te dejó un u impecable en [0,1). Pero nadie simula “unidades abstractas”: simulás temperaturas en [−10,40), esperas en [0,30) o factores en [0.8,1.2). El paso es un cambio de escala lineal, y aunque parece trivial, concentra los bugs más comunes del curso: olvidar restar a, incluir el b o truncar con int.
La buena noticia: una sola fórmula bien aplicada hereda gratis la uniformidad, los bordes y la resolución del [0,1).
- ¿Por qué
a + u·(b−a)y noa + u·b? - ¿El intervalo resultado incluye a
b? - ¿Qué media y qué
P(x < t)debe dar un [a,b) parejo? - ¿Cuándo conviene
uniform(a,b)y cuándo hacerlo a mano?
02 · Definición
Estirar y desplazar
Longitud
L = b−a es el estirón: multiplica la densidad 1 → 1/L y el paso de resolución 1/m → L/m.
Desplazamiento
Sumar a mueve la grilla sin deformarla: u=0 → a, el 1 inalcanzable → b inalcanzable.
Bordes [a,b)
Heredados: a sale con prob. 1/m, b nunca. Así x < b siempre y x >= a siempre.
| Receta | Rango real | Veredicto |
|---|---|---|
a + u*(b−a) | [a,b) exacto | Canónica: usar siempre |
a + u*b | [a, a+b) | Olvidó restar a: desplaza mal si a≠0 |
u*(b−a) sin +a | [0, b−a) | Solo vale si a=0 |
03 · Casos y límites
Negativos, grandes y flotante
La linealidad no distingue signos ni magnitudes, pero el flotante sí: con b−a enorme la resolución L/m puede superar tu tolerancia, y con a grande y L chico la suma pierde colas (cancelación del Tema 42).
- a > b
- Intervalo invertido: validar
a < bconassertantes de generar. Sin chequeo, la “uniforme” sale decreciente y los tests de rango fallan. - a == b
- Longitud cero: todo valor es
a. No es azar: devolver constante documentada o lanzar error según tu API. - Resolución L/m
- Con m = 2³² y L = 50 hay 4e9 puntos separados 1,2e−8: continuo a ojo. Con LCG m = 16 y L = 50 el paso es 3,125: dentado real.
Bien escalado
u canónico × L + a
Media ≈ (a+b)/2, mínimo ≥ a, máximo < b. P(x < t) lineal: la firma de la uniformidad continua (Tema 27).
Mal escalado
Truncar o redondear
int(a+u·L) o round() convierten el continuo en discreto con masas desparejas: eso pertenece al Tema 22, no aquí.
04 · Ejemplos
El mismo u, tres mundos
- 1Espera [0,30) min.
x = 0 + u·30. Media 15;P(x < 5) = 1/6. El 30 nunca sale: la espera “exacta” no existe. - 2Temperatura [−10,40).
x = −10 + u·50. El signo no cambia nada: estirón 50 y desplazamiento −10. - 3Factor [0.8,1.2).
x = 0.8 + u·0.4. L chico: la resolución del generador sobra y el flotante es exacto.
05 · Implementación en Python
Escalar y auditar
Una función de tres líneas más un auditor de contrato: rango, media y probabilidad en un umbral. Si el auditor pasa, la escala es sana.
Python en tu navegador. Cambiá a, b a negativos o invertidos y mirá qué denuncia el auditor.
import random
def uniforme_ab(a, b, n=5000, semilla=7):
assert a < b, "se exige a < b"
rng = random.Random(semilla)
return [a + rng.random() * (b - a) for _ in range(n)]
xs = uniforme_ab(-10, 40, n=5000)
print("min:", round(min(xs), 4), "max:", round(max(xs), 4))
print("media:", round(sum(xs) / len(xs), 4), "esperada:", 15.0)
print("P(x < 0):", round(sum(1 for x in xs if x < 0) / len(xs), 4), "esperada:", 0.2)
Mínimo ≥ −10, máximo < 40, media ≈ 15 y P(x<0) ≈ 0,2: la firma lineal de [−10,40).
El bug de olvidar la resta
import random
rng = random.Random(7)
mal = [-10 + rng.random() * 40 for _ in range(3)]
rng = random.Random(7)
bien = [-10 + rng.random() * 50 for _ in range(3)]
print("mal :", [round(v, 4) for v in mal])
print("bien:", [round(v, 4) for v in bien])
06 · Exploración
Laboratorio: estirar el [0,1)
Mové los extremos a y b y la cantidad N. La banda verde es tu [a,b); la línea naranja es el b inalcanzable. El panel compara media observada con (a+b)/2 y cuenta fugas.
x = a + u·(b−a)
media → (a+b)/2
Con [−10,40) la media tiende a 15.
Si a ≥ b el laboratorio lo marca como intervalo inválido: la fórmula exige a < b.
Preguntas para explorar
- Con N = 20 y [−10,40): ¿la media observada está cerca de 15? ¿Y con N = 400?
- Poné a = 30, b = 40: ¿cambia la forma o solo la posición? ¿Qué pasa con a = 40, b = −10?
- ¿Algún valor toca o supera a b? ¿Por qué eso confirma el [a,b)?
Ver respuestas sugeridas
- Con N chico la media oscila (±4 es normal); con N grande se pega a 15. Igual que la moneda del Tema 1: dilución.
- Solo la posición y la longitud: la uniformidad es invariante por escala. Con a ≥ b el intervalo es inválido y el lab lo denuncia.
- Nunca:
u < 1implicax < bsiempre. Un solox ≥ bdelataría un u fugado del Tema 20.
07 · Comprensión
Confusiones frecuentes
«Para [−10,40) hago −10 + u·40»
Da [−10,30): olvidaste que L = 40−(−10) = 50. El error se esconde si a = 0 y explota con negativos.
«El máximo puede ser b, lo incluyo»
No: b es inalcanzable por herencia del 1. Forzarlo con min(x, b) crea un átomo en b detectable en el Tema 27.
«Uso int() para quedarme en el rango»
int() trunca hacia cero y rompe negativos y bordes: para discretos usá la receta del Tema 22, no truncamiento.
«Escalar cambia la calidad del generador»
Una transformación lineal no crea ni destruye uniformidad: si el u era bueno, el x es bueno; si el u estaba sesgado, la escala lo hereda.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: espera de colectivo
Simulá 3000 esperas en [0,30) y estimá P(espera < 5). Compará con el valor teórico 1/6.
import random
rng = random.Random(11)
xs = [0 + rng.random() * 30 for _ in range(3000)]
print(round(sum(1 for x in xs if x < 5) / len(xs), 4))
Ver solución razonada
≈ 0,1667 ± 0,015. Si te da ≈ 0,13 o 0,20 repetidamente, revisá la fórmula: probablemente usaste mal L.
Ejercicio 2: termómetro con negativos
Generá 5 temperaturas en [−10,40) con semilla fija y verificá bordes: todas ≥ −10 y < 40.
import random
rng = random.Random(3)
ts = [-10 + rng.random() * 50 for _ in range(5)]
print([round(t, 4) for t in ts])
print(all(-10 <= t < 40 for t in ts))
Ver solución
Cinco valores entre −10 y 40 con True. Con la fórmula rota −10 + u·40 el máximo posible sería 30: la mitad del rango perdida.
Ejercicio 3: auditor reutilizable
Escribí auditar(xs, a, b) que devuelva min, max, media y fugas, y probalo con [0.8,1.2).
Ver una posible respuesta
import random
def auditar(xs, a, b):
fugas = sum(1 for x in xs if not (a <= x < b))
return min(xs), max(xs), sum(xs) / len(xs), fugas
rng = random.Random(9)
xs = [0.8 + rng.random() * 0.4 for _ in range(2000)]
print(auditar(xs, 0.8, 1.2))
Fugas 0, media ≈ 1,0 y max < 1,2. Reutilizá este auditor en los Temas 22–25 antes de culpar al generador.
09 · Síntesis
Ideas para recordar
- Continuo en [a,b):
x = a + u·(b−a), media (a+b)/2. - L = b−a manda: densidad 1/L y resolución L/m.
- Bordes heredados: a sí sale, b nunca; exigir
a < b. - No truncar ni redondear continuos: los discretos van por el Tema 22.
- La escala no mejora ni empeora al generador: hereda su calidad.
En el próximo tema cruzamos a lo discreto: generación de enteros aleatorios en rangos sin trampas de módulo.