import numpy as np
import matplotlib.pyplot as plt
from dataclasses import dataclass
from typing import Optional@dataclass
class BitTonic:
"""Robot ubriaco che esplora spazi ad N dimensioni."""
dim: int = 2
steps: int = 10_000
bias: float = 0.5 def walk(self) -> np.ndarray:
"""Genera una traiettoria stocastica."""
pos = np.zeros((self.steps, self.dim))
d = np.random.choice(
[-1, 1],
size=(self.steps - 1, self.dim),
p=[1 - self.bias, self.bias]
)
pos[1:] = np.cumsum(d, axis=0)
return posdef msd(self, path: np.ndarray) -> float:
"""Mean Squared Displacement: teoricamente sqrt(N*d)."""
return float(np.sqrt(np.mean(path**2)))def is_recurrent(self) -> bool:
"""Teorema di Polya (1921): ricorrente sse dim <= 2."""
return self.dim <= 2def plot(self, path: np.ndarray,
save: Optional[str] = None):
fig, ax = plt.subplots(figsize=(10, 10),
facecolor='#020408')
ax.set_facecolor('#020408')
ax.plot(path[:, 0], path[:, 1],
color='#00e5ff', alpha=0.5, lw=0.4)
ax.scatter(*path[0], s=70, color='#7fff00',
zorder=5, label='Start')
ax.scatter(*path[-1], s=70, color='#ff6b35',
zorder=5, label='End')
for spine in ax.spines.values():
spine.set_edgecolor('#1a2530')
ax.tick_params(colors='#4a5568')
ax.set_title(
f'BitTonic · {self.steps:,} passi · dim={self.dim} · MSD={self.msd(path):.1f}',
color='#00e5ff', fontsize=11, pad=12)
plt.legend(facecolor='#0d1520', labelcolor='#c8cdd8',
edgecolor='#00e5ff')
plt.tight_layout()
if save: plt.savefig(save, dpi=200)
plt.show()
if __name__ == "__main__":
robot = BitTonic(dim=2, steps=5000, bias=0.52)
path = robot.walk()print(f"Spostamento medio : {robot.msd(path):.2f}")
print(f"Teoricamente : {(robot.steps * robot.dim)**0.5:.2f}")
print(f"Ricorrente? : {robot.is_recurrent()}")robot.plot(path, save='bittonic.png')
Tutti i notebook sono eseguibili su Google Colab senza alcuna installazione locale.
Richiedono NumPy, Matplotlib, opzionalmente NetworkX per i grafi e SciPy per l'analisi spettrale.