Realocando un Arreglo
Supongamos que vamos leyendo números de un stream y almacenándolos en un arreglo. Como no sabemos cuántos números leeremos, no podemos alocar la memoria definitiva que el arreglo necesitará. Debemos, en cambio, ir realocando áreas de memoria cada vez mayores para el arreglo cada vez que éste se va llenando. Al realocar un arreglo que contiene elementos, debemos copiar sus elementos al área nueva de memoria. Consideremos como costo el número total de escrituras a memoria que se requiere a lo largo del proceso.
Si cada vez que se nos llena el arreglo de tamaño lo realocamos a tamaño , usaremos el mínimo posible de memoria, pero el costo total será . Lo que se usa es partir con un arreglo de tamaño pequeño (digamos de 1 elemento, para simplificar) y duplicar su tamaño cada vez que se llena. Esto garantiza que en total usamos a lo sumo el doble de la memoria necesaria. Lo que no está claro es cuánto es el costo de insertar un nuevo elemento en el arreglo si realocamos de esta manera.
En términos de peor caso, esta política parece tan mala como la cuadrática: como hay inserciones que nos requieren realocar el arreglo, el costo de peor caso de una inserción son escrituras, donde es el número de inserciones anteriores (o el tamaño del arreglo). Necesitamos un análisis amortizado para reflejar el hecho de que, con esta segunda política, estas inserciones tan costosas ocurren muy pocas veces e impactan poco en el costo total.
Análisis global
Podemos notar que los valores de en los que, al insertar un nuevo elemento, debemos expandir el arreglo (es decir, duplicarlo en tamaño) son las potencias de 2: 1, 2, 4, , . El peor caso es que , de modo que no tengamos operaciones baratas después de la última expansión. Además de las escrituras realizadas para insertar los elementos en el arreglo, hemos copiado elementos a lo largo de todas las expansiones. El costo total es entonces , y por ende el costo amortizado de una inserción es a lo más de escrituras.
Contabilidad de costos
En vez de cobrar el costo de expandir el arreglo a la operación de inserción, se lo cobraremos a los elementos que se copian, de modo que la operación misma pagará solamente 1 escritura. Cobrarle la operación a los elementos, sin embargo, también nos da un problema, porque a lo largo de todas las expansiones los primeros elementos insertados en el arreglo se copian más veces que los últimos insertados.
Haremos entonces lo siguiente: sólo les cobraremos a los elementos que se copian por primera vez. Cuando se copien elementos, entonces, sólo los de la segunda mitad pagarán la copia, pues estos elementos se están copiando por primera vez. Como alguien tiene que pagar la copia de los de la primera mitad, les cobraremos doble a los de la segunda mitad. Es decir, cada elemento "nuevo" paga por su copia y por la de un elemento "viejo". Luego de copiarse una primera vez, un elemento pasa a ser viejo y a residir en la primera mitad del nuevo arreglo, por lo que no paga nunca más. Esto facilita contabilizar los costos: cada uno de los elementos puede pagar hasta una vez, a costo 2. Además, cada una de las inserciones paga 1, por escribir el elemento en el arreglo. Sumando, tenemos un costo amortizado de a lo más escrituras por inserción.
--> imagen
Función potencial
Sea el tamaño actual del arreglo, que tiene escritos elementos, de modo que (excepto al comienzo, en que y supondremos que partimos con un arreglo de tamaño ). Definiremos la función potencial como . Al comienzo vale , pero luego de la primera inserción siempre vale . Partiremos la operación de inserción en dos sub-operaciones: la "inserción elemental" y la "expansión". La inserción elemental simplemente agrega el nuevo elemento al arreglo, y sólo puede invocarse cuando hay espacio. La expansión sólo duplica el arreglo y copia los elementos actuales, y se invoca cuando no hay espacio. Así, una inserción consiste de una inserción elemental, a veces precedida de una expansión.
La inserción elemental cuesta . Como incrementa , ocurre que . De modo que el costo amortizado de la inserción elemental es . Por otro lado, la expansión teniendo elementos en el arreglo cuesta . Se realiza cuando y hace que pase a ser . Por lo tanto, y , con lo cual . Sumando, tenemos que el costo amortizado de la expansión es . Por lo tanto, el costo amortizado de una inserción es a lo sumo .
📄️ Parametrizando la solución
Consideremos que, o bien para ahorrar espacio o bien para mejorar el tiempo,
📄️ Permitiendo contracciones
Supongamos ahora una situación más compleja en la que también se pueden