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.
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.
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.
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);
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.
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.
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);
}
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));
}
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.
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);
}
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));
}
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));
}
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;
}
}
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));
}
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.
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);
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.
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));
Las funciones en simulaciones aparecen en muchos contextos:
Al construir simulaciones conviene evitar estos problemas:
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.
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.