Simple Random Walk
Definición
Sea \((\Omega, \mathcal{F}, \mathbb{P})\) un espacio de probabilidad. Consideremos una sucesión de variables aleatorias independientes e idénticamente distribuidas \(\{X_n: n \ge 1\}\), \(\quad X_n \in \{-1, +1\}\), tales que \(\mathbb{P}(X_n = 1) = p, \quad \mathbb{P}(X_n = -1) = q = 1 - p,\) para algún \(p \in [0,1]\). La \(\textbf{caminata aleatoria simple}\) se define como el proceso estocástico \(\{S_n : n \ge 0\}\) dado por \[ S_0 = 0, \qquad S_n = \sum_{k=1}^{n} X_k, \quad n \ge 1. \]
En cada paso, el caminante se mueve una unidad a la derecha con probabilidad \(p\) y una unidad a la izquierda con probabilidad \(q = 1 - p\).
\(\textbf{Caso simétrico:}\) Si \(p = q = \tfrac{1}{2}\), la caminata se llama caminata aleatoria simple simétrica, y cumple $ [S_n] = 0, (S_n) = n. $
\(\textbf{Distribución:}\) Para los enteros \(k\) tales que \(n + k\) es par, \[ \mathbb{P}(S_n = k) = \binom{n}{\frac{n+k}{2}} p^{\frac{n+k}{2}} q^{\frac{n-k}{2}}. \]
\(\textbf{Propiedades:}\)
- \(\{S_n\}\) es una cadena de Markov con espacio de estados \(\mathbb{Z}\).
- En el caso simétrico, \(\{S_n\}\) es una martingala respecto a la filtración natural \(\mathcal{F}_n = \sigma(X_1, \dots, X_n)\).
Valor esperado
\[\begin{eqnarray} E(S_n) & = & E\left(\sum_{i=1}^n X_i \right)\\ & = & \sum_{i=1}^n E\left( X_i \right)\\ & = & n E\left( X_i \right)\\ & = & n\left( (1)(p) + (-1)(1-p) \right)\\ & = & n(2p-1) \end{eqnarray}\]
Varianza \[\begin{eqnarray} Var(S_n) & = & Var\left(\sum_{i=1}^n X_i \right)\\ & = & \sum_{i=1}^n Var\left( X_i \right)\\ & = & n Var\left( X_i \right)\\ & = & n \left[E[X_i^2] - \left(X_i \right)^2 \right]\\ & = & n \left[\left((1)^2(p) + (-1)^2(1-p) \right) - \left(2p-1\right)^2 \right]\\ & = & n \left[4p(1-p)\right]\\ & = & 4np(1-p) \end{eqnarray}\]
Simulación
Este código define una clase \(StochasticSimulation()\) para simular procesos estocásticos en tiempo discreto. El método init inicializa el número de pasos, repeticiones (\(n_times\)), el tiempo total \(T\) y genera un vector de tiempos \(t\) con incremento \(dt\). El método \(SimpleRandomWalk()\) simula caminatas aleatorias simples: crea un arreglo de ceros y, paso a paso, suma \(+1\) o \(-1\) según la probabilidad \(p\). Se realizan \(n_times\) simulaciones en paralelo, almacenando cada trayectoria en filas del arreglo. Finalmente, convierte el resultado en un DataFrame de pandas.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.cm as cm
import matplotlib.patches as patches
sns.set_theme()
class StochasticSimulation:
def __init__(self, T=None, steps=100, n_times=1):
self.steps = steps
self.n_times = n_times
# Si T no se proporciona, usar T = steps
if T is None:
self.T = steps
else:
self.T = T
self.dt = self.T/ steps
self.t = np.linspace(0, self.T, steps+1)
# Caminata Aletaoria Simple
def SimpleRandomWalk(self, p):
# crea arreglo vacio
rw = np.zeros( (self.n_times, self.steps+1))
# genera arreglo
for j in range(rw.shape[1]-1):
for i in range(rw.shape[0]):
rw[i][j+1] = rw[i][j] + np.random.choice([-1,1], p=[1-p, p])#sample([-1,1], 1)
df = pd.DataFrame(rw)
return df
En este ejemplo, primero se definen los parámetros de la simulación: se tomarán \(𝑁=10,000\) pasos, lo que permitirá generar una trayectoria muy detallada del proceso, y se simulará \(𝑀=1\), una única trayectoria.
A continuación, se crea una instancia de la clase \(StochasticSimulation()\), pasando como argumentos el número de pasos \(N\)y el número de trayectorias \(M\) a simular. Esta clase se encarga de manejar los cálculos necesarios para generar procesos estocásticos.
Finalmente, se llama al método \(SimpleRandomWalk()\) de la instancia, recibe como argumento \(p\) probabilidad de ir hacia la derecha, y nos genera la trayectoria simulada de una caminata aleatoria simple. El resultado se almacena en df, un DataFrame que contiene los valores de la trayectoria a lo largo de los pasos de tiempo definidos.
Simulación de una Caminata Aleatoria Simple en una dimensión
Caminata Aleatoria Simple \(p=0.5\)
# Parámetros
N = 10000 # número de pasos
M = 1 # número de trayectorias simuladas
p=0.5 # probabilidad
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.SimpleRandomWalk(p)
# Colormap
cmap = cm.get_cmap("Greys", M)
# lista de t
t = sim.t
# desviación estándar teórica
std_teorica = np.sqrt(4*t*p*(1-p)) # 4np(1-p)
# Media teórica
mean_B = t*(2*p-1)
# Intervalos de confianza 95%
conf_upper = mean_B + 1.96 * std_teorica
conf_lower = mean_B - 1.96 * std_teorica
# Gráfico
plt.figure(figsize=(14,6))
for i in range(M):
plt.plot(t, df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t, conf_upper, 'blue', linewidth=0.3)
plt.plot(t, conf_lower, 'blue', linewidth=0.3)
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Caminata Aleatoria Simple [{M} trayectoria] con p={p}")
plt.xlabel("Tiempo t")
plt.ylabel("S(t)")
plt.grid(True)
plt.legend()
plt.show()
Caminata Aleatoria Simple con \(p=0.47\)
# Parámetros
N = 10000 # número de pasos
M = 1 # número de trayectorias simuladas
p=0.47 # probabilidad
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.SimpleRandomWalk(p)
# Colormap
cmap = cm.get_cmap("Greys", M)
# lista de t
t = sim.t
# desviación estándar teórica
std_teorica = np.sqrt(4*t*p*(1-p)) # 4np(1-p)
# Media teórica
mean_B = t*(2*p-1)
# Intervalos de confianza 95%
conf_upper = mean_B + 1.96 * std_teorica
conf_lower = mean_B - 1.96 * std_teorica
# Gráfico
plt.figure(figsize=(14,6))
for i in range(M):
plt.plot(t, df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t, conf_upper, 'blue', linewidth=0.3)
plt.plot(t, conf_lower, 'blue', linewidth=0.3)
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Caminata Aleatoria Simple [{M} trayectoria] con p={p}")
plt.xlabel("Tiempo t")
plt.ylabel("S(t)")
plt.grid(True)
plt.legend()
plt.show()
Caminata Aleatoria Simple con \(p=0.53\)
# Parámetros
N = 10000 # número de pasos
M = 1 # número de trayectorias simuladas
p=0.53 # probabilidad
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.SimpleRandomWalk(p)
# Colormap
cmap = cm.get_cmap("Greys", M)
# lista de t
t = sim.t
# desviación estándar teórica
std_teorica = np.sqrt(4*t*p*(1-p)) # 4np(1-p)
# Media teórica
mean_B = t*(2*p-1)
# Intervalos de confianza 95%
conf_upper = mean_B + 1.96 * std_teorica
conf_lower = mean_B - 1.96 * std_teorica
# Gráfico
plt.figure(figsize=(14,6))
for i in range(M):
plt.plot(t, df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t, conf_upper, 'blue', linewidth=0.3)
plt.plot(t, conf_lower, 'blue', linewidth=0.3)
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Caminata Aleatoria Simple [{M} trayectoria] con p={p}")
plt.xlabel("Tiempo t")
plt.ylabel("S(t)")
plt.grid(True)
plt.legend()
plt.show()
Caminata Aleatoria con múltiples trajectorias
Vamos ahora a simular \(N=10000\) pasos por caminata, y \(M=1000\) trayectorias con \(p = 0.5\) como probabilidad de moverse a la derecha.
# Parámetros
N = 10000 # número de pasos
M = 100 # número de trayectorias simuladas
p=0.50 # probabilidad
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.SimpleRandomWalk(p)
# Colormap
cmap = cm.get_cmap("Greys", M*2)
# lista de t
t = sim.t
std_teorica = np.sqrt(t)
# Media teórica
mean_B = [0]*len(t)
# Intervalos de confianza 95% usando desviación estándar teórica
conf_upper = mean_B + 1.96 * std_teorica
conf_lower = mean_B - 1.96 * std_teorica
# Grafica
plt.figure(figsize=(14,6))
for i in range(M):
plt.plot(t, df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t, conf_upper, 'blue', linewidth=0.3)
plt.plot(t, conf_lower, 'blue', linewidth=0.3)
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Caminata Aleatoria Simple [{M} trayectoria] con p={p}")
plt.xlabel("Tiempo t")
plt.ylabel("S(t)")
plt.grid(True)
plt.legend()
plt.show()
Cuando graficamos muchas trayectorias podemos hechar un vistazo a las estadística, de la variable a leatoria al tiempo \(t\). Veamos la distribución en diferentes posiciones, es decir, \(t=10,100,1000, 10000\) de la simulación anterior, \(N=10000\) pasos, \(M=100\) trajectorias, y con \(p=0.5\)
# Tiempos a graficar
steps_to_plot = [10, 100, 1000, 10000]
# Crear figura con 2x2 subplots
fig, axes = plt.subplots(2, 2, figsize=(10, 6))
axes = axes.flatten() # para indexar fácilmente
for i, step in enumerate(steps_to_plot):
data = df[step]
mean = data.mean()
var = data.var()
sns.histplot(data, bins=30, kde=True, ax=axes[i])
axes[i].set_title(f'Distribución en paso {step}')
axes[i].set_xlabel('Posición')
axes[i].set_ylabel('Frecuencia')
axes[i].legend([f'Media = {mean:.2f}, Varianza = {var:.2f}'])
plt.tight_layout()
plt.show()
# Parámetros
N = 10000 # número de pasos
M = 1000 # número de trayectorias simuladas
p=0.50 # probabilidad
sim = StochasticSimulation(steps=N, n_times=M)
df = sim.SimpleRandomWalk(p)
# Colormap
cmap = cm.get_cmap("Greys", M*2)
# lista de t
t = sim.t
std_teorica = np.sqrt(t)
# Media teórica
mean_B = [0]*len(t)
# Intervalos de confianza 95% usando desviación estándar teórica
conf_upper = mean_B + 1.96 * std_teorica
conf_lower = mean_B - 1.96 * std_teorica
# Grafica
plt.figure(figsize=(14,6))
for i in range(M):
plt.plot(t, df.iloc[i,:].to_list(), color=cmap(i), linewidth=0.5)
plt.plot(t, conf_upper, 'blue', linewidth=0.3)
plt.plot(t, conf_lower, 'blue', linewidth=0.3)
plt.fill_between(t, conf_lower, conf_upper, color='lightblue', alpha=0.2, label='Intervalo de confianza 95% (teórico)')
plt.title(f"Caminata Aleatoria Simple [{M} trayectoria] con p={p}")
plt.xlabel("Tiempo t")
plt.ylabel("S(t)")
plt.grid(True)
plt.legend()
plt.show()
# Tiempos a graficar
steps_to_plot = [10, 100, 1000, 10000]
# Crear figura con 2x2 subplots
fig, axes = plt.subplots(2, 2, figsize=(10, 6))
axes = axes.flatten() # para indexar fácilmente
for i, step in enumerate(steps_to_plot):
data = df[step]
mean = data.mean()
var = data.var()
sns.histplot(data, bins=30, kde=True, ax=axes[i])
axes[i].set_title(f'Distribución en paso {step}')
axes[i].set_xlabel('Posición')
axes[i].set_ylabel('Frecuencia')
axes[i].legend([f'Media = {mean:.2f}, Varianza = {var:.2f}'])
plt.tight_layout()
plt.show()
Veamos una comparación entre el valor teórico y el simulado, para 10,000 pasos y \(p=0.5\): \(E[S_n]=n(2p-1)=0\) y el valor simulado -2.04, mientras que \(Var(S_n)= 4np(1−p)= 1000\) y el valor simulado 9,840. Entre más trajectorias se generen el valor simulado se acercara más al valor teórico.
Simulación de una Caminata Aleatoria Simple en dos dimensiones
Finalmente, tomamos los datos de la simulación anterior y gráficamos pares de caminatas aleatorias para ver su evolución en dos dimensiones.
# Colormap (gradiente de gris)
cmap = cm.get_cmap("Greys", M*2)
centro = (0, 0)
circulo = patches.Circle(centro, 400, edgecolor='grey', facecolor='black', alpha=0.8, lw=1, ls='--')
# Crear gráfico
fig, ax = plt.subplots(figsize=(14, 10))
ax.add_patch(circulo)
# Gnera trajectorias
for i in range(df.shape[0] - 1):
if i < 800:
ax.plot(df.iloc[i][:], df.iloc[i + 1][:], linewidth=0.2, color=cmap(i))
else:
continue
plt.title('Simulación de una caminata aleatoria simple: {} steps and {} paths'.format(N, M))
plt.xlabel('S(n)')
plt.ylabel('S(n)')
plt.xlim(-400, 400)
## (-400.0, 400.0)
## (-400.0, 400.0)
Referencias
- Ubbo F Wiersema (2008). Brownian Motion Calculus. John Wiley & Sons Ltd.
- Sheldon M. Ross(1995). Stochastic Processes. Wiley Series in Probability and Statistics: Probability and Statistics. John Wiley & Sons.
- LefebvreMario(2007). Applied Stochastics Processes. Springer, New York.