Brownian motion–derived process: Rayleigh Distribution
Definición:
Sean \(\{B_1(t), t\geq 0 \}\) y \(\{B_2(t), t\geq 0 \}\) dos movimientos Brownianos estándar independientes. Y sea \[ \begin{eqnarray} R(t) = \sqrt{ B_1(t)^2 + B_2(t)^2}, \ \ \ t \geq 0 \end{eqnarray} \] donde \(R(t)\) representa la distancia al origen de un movimiento Browniano bidimensional. \(R(t)\) tiene distribución Rayleigh \[ \begin{eqnarray} f(x) & = &\frac{x}{t} e^{-\frac{x^2}{2t}}, \ \ \ x>0 \\ & = & 0, \ \ \ d.o.m. \end{eqnarray} \] con valor esperado
\[\mathbb{E}[X(t)] = \sqrt{\frac{\pi t}{2}}\]
y varianza
\[\mathbb{V}ar(X(t)) = \frac{4-\pi}{2} t\]
Movimiento Browniano con deriva y volatilidad en una dimensión
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()# Function para simular una Movimiento Aleatorio
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)
# Movimiento Browniano
def BrownianMotion(self):
# creamos arreglo
bm = np.zeros( (self.n_times, self.steps+1))
# inicializa simulación
for j in range(bm.shape[1]-1):
for i in range(bm.shape[0]):
bm[i][j+1] = bm[i][j] + np.sqrt(self.dt) * np.random.normal(size=1)
df = pd.DataFrame(bm)
return dfEn 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\) única trayectoria.
A continuación, se crea una instancia de la clase \(StochasticSimulation()\), pasando como argumentos el número de pasos y el número de trayectorias a simular. Esta clase se encarga de manejar los cálculos necesarios para generar procesos estocásticos.
Finalmente, se llama al método \(Brownian()\) com parámetros \(\mu\) y \(\sigma\) de la instancia, el cual genera la trayectoria simulada del movimiento Browniano. El resultado se almacena en df, un DataFrame que contiene los valores de la trayectoria a lo largo de los pasos de tiempo definidos.
Solución: Mil trayectorias del movimiento Browniano
# Parámetros
N = 10000 # número de pasos
M = 1000 # número de trayectorias simuladas
# Instanciamos las clases para dos procesos independientes
sim1 = StochasticSimulation(steps=N, n_times=M)
sim2 = StochasticSimulation(steps=N, n_times=M)
# generamos trajectorias de B1 y B2
B1 = sim1.BrownianMotion()
B2 = sim2.BrownianMotion()
# generamos trajectorias de Rt
Rt = np.sqrt(B1**2 + B2**2)
# Copia de Rt
df = Rt.copy()
# Colormap
cmap = cm.get_cmap("Greys", M*2)
# lista de t
t = sim1.t
std_teorica = np.sqrt(((4 -np.pi)/2)*t)
# Media teórica
mean_B = np.sqrt((t*np.pi)/2)
# 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"Simulación de R(t) [{M} trayectorias]")
plt.xlabel("Tiempo t")
plt.ylabel("R(t)")
plt.grid(True)
plt.legend()
plt.show()Distribución al tiempo \(t\)
Vamos a hacer cortes verticales a distintos tiempos \(t\) y hechar un vistazo a las distribuciones. Para cada \(t\) se puede verificar que tanto la media como la varianza se hacercan a los correspondientes valores teóricos definidos al inicio.
# histogramas para distintos valores de t
valores_t = [10, 100, 1000, 5000]
# Crear figura con subplots 4x4
fig, axes = plt.subplots(2, 2, figsize=(14, 10))
axes = axes.flatten()
for i, step in enumerate(valores_t):
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 al tiempo {step}")
axes[i].set_xlabel('Posición')
axes[i].set_ylabel('Frecuencia')
axes[i].legend([f'Media = {mean:.2f}, Varianza = {var:.2f}'])
# Eliminar los subplots vacíos
for j in range(len(valores_t), len(axes)):
fig.delaxes(axes[j])
plt.tight_layout()
plt.show()Ejemplo 1: para t=100
El valor teórico es:
\[\mathbb{E}[R(100)] = \sqrt{\frac{\pi t}{2}} = \sqrt{\frac{\pi *100}{2}} = 12.53\] \[\mathbb{V}ar(R(100)) = \sqrt{\frac{4- \pi}{2}}*t = \sqrt{\frac{4- \pi}{2}}*100 = 42.92\]
Simulación de un Movimiento Browniano 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 la figura y los ejes
fig, ax = plt.subplots(figsize=(14, 10))
ax.add_patch(circulo)
# Brownian Motion
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)) # Normaliza el valor de i
else:
continue
plt.title('Simulación de R(t): {} steps and {} paths'.format(N, M))
plt.xlabel('R(t)')
plt.ylabel('R(t)')
plt.gca().set_xticklabels([]) # quitar etiquetas eje X
plt.gca().set_yticklabels([]) # quitar etiquetas eje Y
plt.grid(True)
plt.show()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.