Una operación amortizada estima el costo promedio de una secuencia completa en lugar de enfocarse en un caso aislado. Esta visión evita sobreestimar procedimientos que rara vez son costosos y permite justificar el diseño de estructuras que reorganizan sus datos de forma esporádica.
Al entender el análisis amortizado sabrás cómo dimensionar buffers, decidir estrategias de redimensionamiento y explicar por qué una operación aparentemente lenta sigue siendo eficiente en el largo plazo.
Las siguientes subsecciones construyen la intuición necesaria para usar este concepto al evaluar contenedores dinámicos.
La complejidad amortizada distribuye el costo de una operación cara entre varias operaciones baratas subsecuentes, de modo que el promedio se mantenga bajo.
Las estructuras dinámicas incrementan su capacidad o reorganizan referencias de forma puntual. Ese evento costoso se financia con los ahorros de las inserciones previas.
El siguiente pseudocódigo ilustra una lista que duplica su capacidad cuando se llena:
class ArrayList:
def __init__(self):
self.capacidad = 2
self.datos = [None] * self.capacidad
self.longitud = 0
def append(self, valor):
if self.longitud == self.capacidad:
self._redimensionar()
self.datos[self.longitud] = valor
self.longitud += 1
def _redimensionar(self):
self.capacidad *= 2
nuevo = [None] * self.capacidad
for i, valor in enumerate(self.datos):
nuevo[i] = valor
self.datos = nuevo
Copiar n elementos durante la redimensionación cuesta O(n), pero ocurre infrecuentemente. Si ejecutas muchas operaciones append, el costo total se aproxima a 2n, resultando en un promedio O(1) por inserción.
Entender operaciones amortizadas evita elegir estructuras por percepciones erróneas y ayuda a negociar objetivos de rendimiento.
Cuando documentas tus estructuras, incluye el costo amortizado para que otros desarrolladores sepan cómo escalará el sistema bajo cargas normales.