66. Funciones en simulaciones

66.1 Introducción

Una simulación es una representación aproximada de un proceso real o imaginario. En programación, las funciones permiten describir cómo cambia un sistema a medida que avanza el tiempo.

En lugar de calcular todo de una sola vez, una simulación actualiza un estado muchas veces. Cada actualización aplica reglas matemáticas simples: posición, velocidad, crecimiento, energía, temperatura, probabilidad o cualquier otra variable del modelo.

66.2 Qué es simular

Simular consiste en tomar un estado inicial, aplicar una regla de cambio y observar el resultado luego de varios pasos. La regla de cambio suele escribirse como una función.

function actualizar(valor) {
  return valor + 2;
}

let estado = 0;

for (let paso = 1; paso <= 5; paso++) {
  estado = actualizar(estado);
  console.log("Paso", paso, "estado:", estado);
}

La variable estado cambia en cada paso. La función actualizar define la dinámica de la simulación.

66.3 Estado de una simulación

El estado reúne los datos necesarios para continuar la simulación. Puede ser un número, un objeto, una lista o una estructura más compleja.

const estado = {
  tiempo: 0,
  posicion: 10,
  velocidad: 3
};

console.log("Tiempo:", estado.tiempo);
console.log("Posición:", estado.posicion);
console.log("Velocidad:", estado.velocidad);

Una buena simulación separa claramente qué datos representan el estado y qué funciones modifican esos datos.

66.4 Función de actualización

La función de actualización recibe el estado actual y devuelve un nuevo estado. Esta forma facilita probar, depurar y reutilizar la lógica.

function siguienteEstado(estado) {
  return {
    tiempo: estado.tiempo + 1,
    posicion: estado.posicion + estado.velocidad,
    velocidad: estado.velocidad
  };
}

let estado = { tiempo: 0, posicion: 5, velocidad: 2 };
estado = siguienteEstado(estado);

console.log(estado);

66.5 Simulación paso a paso

Una simulación discreta avanza en pasos. Cada paso representa un pequeño intervalo de tiempo o una iteración del algoritmo.

function avanzar(estado) {
  return {
    paso: estado.paso + 1,
    valor: estado.valor * 1.1
  };
}

let estado = { paso: 0, valor: 100 };

for (let i = 0; i < 6; i++) {
  estado = avanzar(estado);
  console.log("Paso:", estado.paso, "valor:", estado.valor.toFixed(2));
}

Este modelo podría representar una población, una inversión, una temperatura o cualquier variable que crece por porcentaje.

66.6 Delta de tiempo

En muchas simulaciones se usa dt, abreviatura de delta de tiempo. Indica cuánto tiempo avanza la simulación en cada actualización.

function mover(posicion, velocidad, dt) {
  return posicion + velocidad * dt;
}

let posicion = 0;
const velocidad = 4;
const dt = 0.5;

for (let i = 1; i <= 5; i++) {
  posicion = mover(posicion, velocidad, dt);
  console.log("Tiempo:", i * dt, "posición:", posicion);
}

Si dt es pequeño, la simulación suele ser más precisa, aunque requiere más cálculos.

66.7 Movimiento simple

Una de las simulaciones más básicas es el movimiento rectilíneo con velocidad constante.

La fórmula es:

posiciónNueva = posiciónActual + velocidad · dt

function actualizarMovimiento(estado, dt) {
  return {
    x: estado.x + estado.vx * dt,
    vx: estado.vx
  };
}

let particula = { x: 20, vx: 6 };

for (let paso = 1; paso <= 4; paso++) {
  particula = actualizarMovimiento(particula, 1);
  console.log("Paso", paso, "x =", particula.x);
}

66.8 Movimiento con aceleración

Cuando existe aceleración, primero cambia la velocidad y luego la posición. Es una aproximación muy usada en juegos, animaciones y simulaciones físicas simples.

function actualizarConAceleracion(estado, dt) {
  const nuevaVelocidad = estado.velocidad + estado.aceleracion * dt;
  const nuevaPosicion = estado.posicion + nuevaVelocidad * dt;

  return {
    posicion: nuevaPosicion,
    velocidad: nuevaVelocidad,
    aceleracion: estado.aceleracion
  };
}

let objeto = { posicion: 0, velocidad: 0, aceleracion: 9.8 };

for (let i = 1; i <= 5; i++) {
  objeto = actualizarConAceleracion(objeto, 0.1);
  console.log(i, objeto.posicion.toFixed(3), objeto.velocidad.toFixed(3));
}

66.9 Integración aproximada

Las simulaciones por pasos usan aproximaciones numéricas. El método de Euler es una técnica sencilla: estima el estado siguiente a partir del estado actual y sus tasas de cambio.

function euler(y, derivada, dt) {
  return y + derivada(y) * dt;
}

function derivada(y) {
  return -0.5 * y;
}

let y = 10;

for (let paso = 1; paso <= 6; paso++) {
  y = euler(y, derivada, 0.2);
  console.log("Paso", paso, "y:", y.toFixed(4));
}

En este ejemplo, el valor disminuye de forma proporcional a su tamaño actual.

66.10 Límites y rebotes

Las simulaciones suelen tener restricciones. Por ejemplo, una pelota no debería atravesar una pared: al llegar al límite, puede invertir su velocidad.

function actualizarPelota(pelota, dt, limite) {
  let x = pelota.x + pelota.vx * dt;
  let vx = pelota.vx;

  if (x > limite) {
    x = limite;
    vx = -vx;
  }

  if (x < 0) {
    x = 0;
    vx = -vx;
  }

  return { x, vx };
}

let pelota = { x: 8, vx: 5 };

for (let paso = 1; paso <= 6; paso++) {
  pelota = actualizarPelota(pelota, 0.5, 10);
  console.log("Paso", paso, pelota);
}

66.11 Rozamiento y amortiguación

El rozamiento reduce la velocidad con el tiempo. En una simulación simple puede modelarse multiplicando la velocidad por un factor menor que 1.

function aplicarRozamiento(estado, factor) {
  return {
    posicion: estado.posicion + estado.velocidad,
    velocidad: estado.velocidad * factor
  };
}

let estado = { posicion: 0, velocidad: 12 };

for (let paso = 1; paso <= 8; paso++) {
  estado = aplicarRozamiento(estado, 0.8);
  console.log("Paso", paso, "pos:", estado.posicion.toFixed(2), "vel:", estado.velocidad.toFixed(2));
}

66.12 Oscilaciones en simulación

Las funciones trigonométricas permiten simular ciclos: olas, vibraciones, luces intermitentes, respiración de una interfaz o movimiento pendular aproximado.

function alturaOla(tiempo, amplitud, frecuencia) {
  return amplitud * Math.sin(2 * Math.PI * frecuencia * tiempo);
}

for (let i = 0; i <= 8; i++) {
  const tiempo = i * 0.25;
  const altura = alturaOla(tiempo, 3, 0.5);
  console.log("t =", tiempo.toFixed(2), "altura =", altura.toFixed(3));
}

66.13 Eventos y condiciones

Una simulación puede disparar eventos cuando una variable supera cierto umbral. Esto permite activar alarmas, cambios de estado o acciones del programa.

function actualizarTemperatura(temp) {
  return temp + 1.7;
}

let temperatura = 22;

for (let minuto = 1; minuto <= 10; minuto++) {
  temperatura = actualizarTemperatura(temperatura);

  if (temperatura >= 30) {
    console.log("Minuto", minuto, "activar ventilación:", temperatura.toFixed(1));
    break;
  }
}

66.14 Simulación de crecimiento

El crecimiento limitado aparece cuando una variable aumenta rápido al principio, pero se frena al acercarse a una capacidad máxima.

function crecimientoLogistico(poblacion, tasa, capacidad) {
  return poblacion + tasa * poblacion * (1 - poblacion / capacidad);
}

let poblacion = 50;

for (let paso = 1; paso <= 10; paso++) {
  poblacion = crecimientoLogistico(poblacion, 0.35, 500);
  console.log("Paso", paso, "población:", poblacion.toFixed(2));
}

66.15 Aleatoriedad controlada

Muchas simulaciones incorporan variaciones aleatorias. Conviene mantener esa aleatoriedad dentro de una función clara para controlar mejor el comportamiento.

function variacionAleatoria(min, max) {
  return min + Math.random() * (max - min);
}

let energia = 100;

for (let paso = 1; paso <= 5; paso++) {
  energia += variacionAleatoria(-8, 4);
  console.log("Paso", paso, "energía:", energia.toFixed(2));
}

En simulaciones científicas o pruebas automatizadas puede ser necesario usar generadores pseudoaleatorios con semilla.

66.16 Pausar y reiniciar

Como el estado está separado, se puede pausar una simulación conservando sus valores o reiniciarla creando nuevamente el estado inicial.

function crearEstadoInicial() {
  return { tiempo: 0, valor: 1 };
}

function avanzar(estado) {
  return {
    tiempo: estado.tiempo + 1,
    valor: estado.valor + 3
  };
}

let estado = crearEstadoInicial();
estado = avanzar(estado);
estado = avanzar(estado);
console.log("Antes de reiniciar:", estado);

estado = crearEstadoInicial();
console.log("Después de reiniciar:", estado);

66.17 Precisión y estabilidad

Una simulación puede volverse inestable si el paso de tiempo es demasiado grande o si la regla de actualización amplifica demasiado los errores.

function decaer(valor, tasa, dt) {
  return valor - tasa * valor * dt;
}

function simular(dt) {
  let valor = 10;

  for (let i = 0; i < 5; i++) {
    valor = decaer(valor, 1.2, dt);
  }

  return valor;
}

console.log("dt pequeño:", simular(0.1).toFixed(4));
console.log("dt grande:", simular(1.0).toFixed(4));

Al aumentar dt, el resultado puede alejarse del comportamiento esperado.

66.18 Separar modelo y visualización

La simulación debería calcular el estado. La visualización debería mostrarlo. Separar estas responsabilidades evita mezclar matemática, lógica y presentación.

function actualizarModelo(estado) {
  return {
    x: estado.x + estado.vx,
    vx: estado.vx
  };
}

function textoParaMostrar(estado) {
  return `Objeto en x = ${estado.x}`;
}

let estado = { x: 4, vx: 2 };
estado = actualizarModelo(estado);

console.log(textoParaMostrar(estado));

66.19 Aplicaciones en programación

Las funciones en simulaciones aparecen en muchos contextos:

  • Juegos con movimiento, gravedad, colisiones y daño.
  • Animaciones de interfaces.
  • Modelos de crecimiento de usuarios, población o recursos.
  • Predicción aproximada de sistemas físicos.
  • Simuladores educativos y científicos.
  • Pruebas de algoritmos bajo muchas condiciones posibles.

66.20 Errores comunes

Al construir simulaciones conviene evitar estos problemas:

  • Usar un paso de tiempo demasiado grande.
  • Modificar el estado desde demasiadas partes del programa.
  • Mezclar la visualización con la regla matemática principal.
  • No controlar límites, umbrales o casos extremos.
  • Asumir que una aproximación simple siempre será precisa.

66.21 Qué recordar

En una simulación, una función suele responder esta pregunta: dado el estado actual, ¿cuál será el siguiente estado?

La estructura general es:

estadoSiguiente = actualizar(estadoActual, dt)

Esta idea se puede aplicar a fenómenos físicos, económicos, biológicos, gráficos o lógicos.

66.22 Conclusión

Las funciones son la base de muchas simulaciones porque permiten expresar reglas de cambio de forma clara y repetible. Al combinar estado, tiempo y funciones de actualización, un programa puede representar procesos dinámicos paso a paso.

Una simulación no necesita ser perfecta para ser útil. Lo importante es conocer qué simplificaciones se están haciendo, controlar los errores numéricos y mantener el código organizado.

Volver al índice