Método de Monte Carlo · Tema 38

Simulación de inventarios

Cuánto stockear si la demanda es azar: sobrante que cuesta y faltante que duele.

01 · Punto de partida

Comprar de más o vender de menos

El diariero compra Q a $2 y vende a $5 lo que la demanda D permita. Si sobra, pierde $2 por diario; si falta, pierde $3 de margen. El Q óptimo equilibra ambos con la distribución de D.

Monte Carlo prueba cada Q con miles de demandas y elige el de mayor media con riesgo acotado.

  • ¿Qué vale sobrar (costo) y faltar (margen)?
  • ¿Qué distribución tiene D?
  • ¿Qué Q maximiza la media?
  • ¿Qué nivel de servicio deja?

02 · Definición

Tres números del inventario

💰

Media

Ganancia esperada por Q: la que se maximiza.

📦

Servicio

P(D ≤ Q): fracción de demanda satisfecha. El cliente lo ve.

⚖️

Crítico

Fractil (p−c)/p: la teoría dice Q* ≈ ese cuantil de D.

Diariero p = 5, c = 2, D en {80,100,120}.
QMediaServicio
8024030%
10029070%
120270100%

03 · Método

Demanda→Probar Q→Elegir
  1. 1
    Modelar D.

    Empírica con pesos de ventas reales. Sin D no hay Q.

  2. 2
    Barrer Q.

    Grilla 60–140 con N = 10 000 y mismas semillas por Q.

  3. 3
    Elegir con regla.

    Mayor media con servicio ≥ 70% (o tu corte). Fractil 0,6 orienta.

Fractil crítico
(p−c)/p = 0,6 aquí: comprar hasta cubrir el 60% de la demanda. Regla teórica que MC verifica.
Sobrante/faltante
max(Q−D,0) y max(D−Q,0): el diagnóstico físico del Q.
Mismas semillas
Cada Q ve las mismas demandas: la diferencia es stock, no suerte.

04 · Ejemplos

  1. 1
    Diarios.

    Q* = 100: media 290, servicio 70%.

  2. 2
    Panadería.

    Perecedero con sobrante 0: Q* baja, servicio baja.

  3. 3
    Repuestos caros.

    Faltante carísimo: Q* alto aunque sobre seguido.

05 · Representación en Python

Barrer Q con mismas semillas

Python en tu navegador. La curva dice dónde está el pico.

import random

def evaluar(Q, demandas):
    return sum(5*min(Q, D)-2*Q for D in demandas)/len(demandas)

random.seed(7)
demandas = random.choices([80, 100, 120], weights=[30, 40, 30], k=10000)
for Q in [60, 80, 100, 120, 140]:
    print(Q, round(evaluar(Q, demandas), 1))

06 · Exploración

Laboratorio: la curva del Q

Curva exacta de media vs Q con el óptimo en amarillo. Tu Q en celeste: movelo y leé media, servicio y sobrante/faltante.

EXPERIMENTO 38

Buscar el pico

G(Q) = 5·mín(Q,D) − 2·Q

Los resultados numéricos aparecen debajo.
Media—
Servicio—
Sobrante/faltante—
Lectura—

Mové Q para recorrer la curva.

A la izquierda del pico falta (pierdo margen); a la derecha sobra (pago costo). El pico equilibra.

Preguntas para explorar

  1. ¿El pico está en 100? ¿Cuánto pierde 80 y 120?
  2. ¿Qué servicio da el óptimo? ¿Y Q = 120?
  3. Si el costo sube a 4, ¿el pico se mueve?
Ver respuestas sugeridas
  1. Sí: 290 vs 240/270. Comprar mal cuesta 20–50.
  2. 70% vs 100%: el óptimo no es servicio pleno.
  3. Sí, a la izquierda: sobrar duele más y el fractil (5−4)/5 = 0,2 pide Q chico.

07 · Comprensión

Confusiones frecuentes

« Stockeo la media de D»

La media es 100 aquí por simetría, pero en general el óptimo es el fractil, no la media.

«Servicio 100% es lo mejor»

Cuesta sobrante: Q = 120 da 100% pero 20 menos de media. Servicio se negocia.

«Cada Q con sus demandas»

Sin mismas demandas el ranking tiembla. Una lista para todos.

«Un periodo vale para todo»

Solo perecederos o única compra. Con reposición hay dinámica (fuera del curso).

08 · Práctica guiada

Ejercicios con Python

Ejercicio 1: curva completa

Barré Q 60–140 cada 10 con N = 10 000 común.

import random

random.seed(7)
Ds = random.choices([80, 100, 120], weights=[30, 40, 30], k=10000)
for Q in range(60, 141, 10):
    print(Q, round(sum(5*min(Q, D)-2*Q for D in Ds)/len(Ds), 1))
Ver solución razonada

Pico en 100 (~290). La curva sube por margen y baja por sobrante.

Ejercicio 2: servicio del óptimo

P(D ≤ 100) con la empírica.

print(0.3+0.4)
Ver solución

70%: el óptimo deja 30% de ventas perdidas. Negocio, no caridad.

Ejercicio 3: costo más alto

Repetí la curva con c = 4. ¿Dónde queda el pico?

Ver una posible respuesta
import random

random.seed(7)
Ds = random.choices([80, 100, 120], weights=[30, 40, 30], k=10000)
for Q in [60, 80, 100, 120]:
    print(Q, round(sum(5*min(Q, D)-4*Q for D in Ds)/len(Ds), 1))

Pico en 80: sobrar a $4 duele y el fractil 0,2 lo anticipa.

09 · Síntesis

Ideas para recordar

  • G = p·mín(Q,D) − c·Q; se maximiza su media.
  • Q* ≈ fractil (p−c)/p de D; MC lo verifica.
  • Servicio P(D ≤ Q) se reporta junto a la media.
  • Una lista de D para todos los Q: números comunes.
  • Sobrante vs faltante explica el pico.

En el próximo tema simularemos azar puro: los sistemas probabilísticos.