Saltar al contenido principal

Permitiendo contracciones

Supongamos ahora una situación más compleja en la que también se pueden eliminar elementos en el arreglo. Un elemento eliminado se reemplaza por el que está en último lugar, de modo que el arreglo se almacene en forma compacta. Queremos evitar el estar almacenando demasiada memoria para un arreglo que alguna vez fue grande pero ahora tiene pocos elementos. Para ello, podemos contraer el arreglo cuando queda muy vacío después de un borrado. Esta es la acción contraria a la expansión que se realiza durante la inserción, y supondremos que también cuesta nn escrituras.

El primer impulso puede ser que, una vez que el arreglo tenga tamaño s>αns > \alpha n, lo contraigamos a tamaño sα\frac{s}{\alpha}. Sin embargo, si volvemos a insertar inmediatamente, el arreglo se volverá a expandir. Por ello, el costo de esta alternativa puede ser muy alto: cada operación costará Θ(n)\Theta(n) escrituras si se elimina un elemento inmediatamente después de expandir, lo que provoca una contracción, luego se vuelve a insertar, lo que provoca otra expansión, luego se vuelve a eliminar, lo que provoca otra contracción, etc.

--> imagen

Para seguir teniendo costo amortizado constante, establecemos que el arreglo se contraerá solamente cuando sβns \ge \beta n, para cierto β>α\beta > \alpha, y entonces se contraerá a tamaño γn\gamma n, para un 1<γ<β1<\gamma<\beta. Esto garantiza que la memoria máxima a ser usada es βn\beta n. Nos preguntamos ahora cuál es el costo amortizado de una operación.

Esta vez no funciona una función de tipo ϕ=anbs\phi = an-bs. Debemos elegir un esquema algo más complejo: ϕ=anbs\phi = |an-bs|. Tenemos ahora las siguientes operaciones:

  • Inserción elemental, a costo ci=1c_i=1. El valor de Δϕi\Delta\phi_i es a lo sumo aa, por lo que una cota superior al costo amortizado es ci^1+a\hat{c_i} \le 1+a.
  • Borrado elemental (es decir, sin considerar la contracción), a costo ci=1c_i=1. El valor de Δϕi\Delta\phi_i es también a lo sumo aa, por lo que tenemos la misma cota superior ci^1+a\hat{c_i} \le 1+a.
  • Expansión, a costo ci=nc_i=n. Esta ocurre cuando n=sn=s, y pasaremos de ϕi1=anbs=anbn\phi_{i-1} = |an-bs|=|an-bn| a ϕi=anbsα=anbnα\phi_i = |an-bs\alpha|=|an-bn\alpha|. Para que nuestro esquema funcione, la expansión debe darse cuando anbnα0an-bn\alpha \ge 0, es decir, que debe valer abαa \ge b\alpha. En este caso, tenemos Δϕi=b(α1)n\Delta\phi_i = -b(\alpha-1)n, y tendremos ci^0\hat{c_i} \le 0 siempre que b1α1b \ge \frac{1}{\alpha-1}.
  • Contracción, a costo ci=nc_i=n. Esta ocurre cuando sβns \ge \beta n, y pasaremos de ϕi1=anbs\phi_{i-1} = |an-bs| a ϕi=anbγn\phi_i = |an-b\gamma n|. Para que nuestro esquema funcione, la contracción debe darse cuando anbγn0an-b\gamma n \le 0, es decir, que debe valer abγa \le b\gamma. En este caso, tenemos ϕi1=bsanbβnan\phi_{i-1}=bs-an \ge b\beta n-an y ϕi=bγnan\phi_i=b\gamma n-an, y entonces Δϕib(βγ)n\Delta\phi_i \le -b(\beta-\gamma)n. Por lo tanto, tendremos ci^0\hat{c_i} \le 0 siempre que b1βγb \ge \frac{1}{\beta-\gamma}.

Tenemos entonces las condiciones bαabγb\alpha \le a \le b\gamma, b1α1b \ge \frac{1}{\alpha-1} y b1βγb \ge \frac{1}{\beta-\gamma}. Dado un β\beta fijo (relacionado con la memoria máxima que permitimos desperdiciar), queremos minimizar aa (pues el costo amortizado es 1+a1+a). El menor aa posible es a=bαa = b \alpha. Usar el menor bb posible implica usar el menor γ\gamma posible, pues b1βγb \ge \frac{1}{\beta-\gamma}. Como nuestras desigualdades implican γα\gamma \ge \alpha, elegimos γ=α\gamma = \alpha. Las nuevas cotas inferiores son b1α1b \ge \frac{1}{\alpha-1} y b1βαb \ge \frac{1}{\beta-\alpha}, que decrecen y crecen, respectivamente, con α\alpha. Por lo tanto, el óptimo se da cuando 1α1=1βα\frac{1}{\alpha-1} = \frac{1}{\beta-\alpha}, es decir, α=β+12\alpha=\frac{\beta+1}{2}. El costo amortizado es entonces 1+a=1+bα=1+αα1=2α1α1=2ββ11+a= 1+b\alpha = 1+\frac{\alpha}{\alpha-1}=\frac{2\alpha-1}{\alpha-1} = \frac{2\beta}{\beta-1}.

Concluyendo, podemos garantizar un uso máximo de βn\beta n celdas para almacenar nn elementos, permitiendo inserciones y borrados, a un costo amortizado de 2ββ1\frac{2\beta}{\beta-1} por operación. Para lograrlo, cuando insertamos y s=ns=n, expandimos el arreglo a s=β+12ns=\frac{\beta+1}{2} n, y cuando borramos y sβns\ge\beta n, contraemos el arreglo a s=β+12ns=\frac{\beta+1}{2} n. Por ejemplo, podemos elegir β=3\beta=3 para obtener un costo amortizado de 3n3n.

--> imagen