Análisis de algoritmos Complejidad amortizada Dra. Elisa Schaeffer elisa.schaeffer@gmail.com PISIS / FIME / UANL Complejidad amortizada– p. 1 Complejidad amortizada La idea en el análisis de complejidad amortizada es definir una sucesión de n operaciones y estimar una cota superior para su tiempo de ejecución. Esta cota superior está dividido por el número de operaciones n para obtener la complejidad amortizada (inglés: amortized complexity) de una operación. No es necesario que las operaciones de la sucesión sean iguales ni del mismo tipo. Complejidad amortizada– p. 2 Motivación La motivación de complejidad amortizada es poder “pronósticar” con mayor exactitud el tiempo de ejecución del uso del algoritmo en el “mundo real”. Si uno típicamente quiere realizar ciertos tipos de cosas, ¿cuánto tiempo toma? Complejidad amortizada– p. 3 Formalmente Sea D0 el estado inicial de una estructura de datos. En cada momento, se aplica una operación O. Después de i operaciones: Di . Sucesión de operaciones como O1 , . . . , On . El “costo” computacional de operación Oi puede ser muy distinto del costo de otra operación Oj . Complejidad amortizada– p. 4 Costo promedio La idea es sumar los costos y pensar en términos de “costo promedio”. Para que tenga sentido ese tipo de análisis, la sucesión de operaciones estudiada tiene que ser la peor posible. Si no lo es, no hay guarantía ninguna que se pueda generalizar el análisis para sucesiones más largas. Complejidad amortizada– p. 5 Arreglos dinámicos Como un ejemplo, evaluamos la complejidad de operar un arreglo dinámico. Se duplica el tamaño siempre cuando hace falta añadir un elemento. Se corta el tamaño a mitad cuando se ha liberado tres cuartas partes del espacio. Complejidad amortizada– p. 6 Una sucesión de inserciones Evaluamos la sucesión de 2k inserciones en tal arreglo. Al realizar la primera inserción, se crea un arreglo de un elemento. Con la segunda, se duplica el tamaño para lograr a guardar el segundo elemento. En práctica, esto significa que se reserva un arreglo nuevo del tamaño doble en otra parte y mueve cada clave que ya estaba guadrara en el arreglo al espacio nuevo. Con la tercera inserción, se crea espacio para dos elementos más, etcétera. Complejidad amortizada– p. 7 Baratas versus caras Entonces, con la sucesión de operaciones definida, vamos a tener que multiplicar el tamaño del arreglo k veces y las otras 2k − k inserciones van a ser “baratas”. La complejidad de una inserción barata es O (1). Cuando el tamaño del arreglo es n, el costo de duplicar su tamaño es O (n). Entonces, la complejidad de una inserción “cara” es O (1) + O (n) ∈ O (n). Para la inserción difícil número i, el tamaño va a ser 2i . Complejidad amortizada– p. 8 Suma total La suma de la complejidad de las k inserciones difíciles es ! k X i k+1 . 2 =O 2 O i=1 k La complejidad total de los 2 − k operaciones fáciles es O 2k − k ∈ O 2k porque cada una es de tiempo constante. k =⇒ La complejidad de la sucesión completa de 2 k+1 k k inserciones es O 2 +2 ∈O 3·2 . Complejidad amortizada– p. 9 Complejidad amortizada Dividimos la complejidad total por el número de operaciones 2k para obtener la complejidad amortizada por operación: k 3·2 O = O (3) ∈ O (1) . k 2 Si hacemos n operaciones y cada uno tiene complejidad amortizada constante, cada sucesión de puras inserciones tiene complejidad amortizada lineal, O (n). Complejidad amortizada– p. 10 Análisis con eliminaciones Para poder incluir las eliminaciones en el análisis, hay que cambiar la técnica de análisis. Utilizamos el método de potenciales, también conocido como el método de cuentas bancarias, donde se divide el costo de operaciones caras entre operaciones más baratas. Complejidad amortizada– p. 11 Método de potenciales Supone que al comienzo nos han asignado una cierta cantidad M de pesos. Cada operación básica de tiempo constante O (1) cuesta un peso. Vamos a realizar n operaciones. Complejidad amortizada– p. 12 Costo planeado El costo planeado de una operación Oi es pi = M n pesos. Si una operación no necesita todos sus M pesos n asignados, los ahorramos en una cuenta bancaria común. Complejidad amortizada– p. 13 Costo planeado El costo planeado de una operación Oi es pi = M n pesos. Si una operación no necesita todos sus M pesos n asignados, los ahorramos en una cuenta bancaria común. Si una operación necesita más que M pesos, la n operación puede retirar fondos adicionales de la cuenta bancaria. Complejidad amortizada– p. 13 Costo planeado El costo planeado de una operación Oi es pi = M n pesos. Si una operación no necesita todos sus M pesos n asignados, los ahorramos en una cuenta bancaria común. Si una operación necesita más que M pesos, la n operación puede retirar fondos adicionales de la cuenta bancaria. Si en la cuenta no hay balance, la operación los toma prestados del banco. Complejidad amortizada– p. 13 Costo planeado El costo planeado de una operación Oi es pi = M n pesos. Si una operación no necesita todos sus M pesos n asignados, los ahorramos en una cuenta bancaria común. Si una operación necesita más que M pesos, la n operación puede retirar fondos adicionales de la cuenta bancaria. Si en la cuenta no hay balance, la operación los toma prestados del banco. Todo el préstamo habrá que pagar con lo que ahorran operaciones que siguen. Complejidad amortizada– p. 13 Balance = potencial Denotamos el balance de la cuenta después de la operación número i, o sea, en el estado Di , con Φi . El balance también se llama la potencial de la estructura. La meta: poder mostrar que la estructura siempre contiene “la potencial suficiente” para poder pagar la operaciones que vienen. Complejidad amortizada– p. 14 Costo amortizado Sea ri el costo El costo real de la operación Oi . amortizado de la operación Oi es ai = ti + Φi − Φi−1 | {z } pesos ahorrados Complejidad amortizada– p. 15 Costo amortizado total El costo amortizado total de las n operaciones es A= n X i=1 ai n X (ri + Φi − Φi−1 ) = = = i=1 n X i=1 n X n X (Φi − Φi−1 ) ri + i=1 ri + Φn − Φ0 , i=1 =⇒ Costo real total: R = n X i=1 r1 = Φ0 − Φn + n X ai . i=1 Complejidad amortizada– p. 16 Función de balance Para poder realizar el análisis, primero hay que asignar los costos planeados pi . Después de que se intenta definir una función de balance Φi : N → R tal que el costo amortizado de cada operación es menor o igual al costo planeado: ai = ri + Φi − Φi−1 ≤ pi . Complejidad amortizada– p. 17 Consecuencias Con tales costos amortizados, aplica n X ri ≤ Φ0 − Φn + n X pi . i=1 i=1 La transacción con el banco (ahorro o préstamo) de la operación Oi es de pi − ri pesos. Típicamente queremos que Φi ≥ 0 para cada estado Di . Si esto aplica, tenemos n X i=1 ri ≤ Φ0 + n X pi . i=1 Complejidad amortizada– p. 18 Exactitud Comúnmente además tenemos que Φ0 = 0, en cual caso la suma de los costos planeados es una cota superior a la suma de los costos reales. Si uno elige costos planeados demasiado grandes, la cota será cruda y floja y la estimación de la complejidad no es exacta. Haber elegido un valor demasiado pequeño de los costos planeados, resulta imposible encontrar una función de balance apropiada. Complejidad amortizada– p. 19 Precio de una inserción Hay que asegurar que el precio de una inserción cara se pueda pagar con lo que han ahorrado las inserciones baratas anteriores. Suponemos que el tiempo de una inserción fácil sea t1 y el tiempo de mover una clave al espacio nuevo (de tamaño doble) sea t2 . Entonces, si antes de la inserción cara, la estructura contiene n claves, el precio real total de la inserción cara es t1 + nt2 . Complejidad amortizada– p. 20 Función de balance Vamos a definir una función de balance tal que su valor inicial es cero y también su valor después de hacer duplicado el tamaño es cero. En cualquier otro momento, será Φi > 0. La meta es llegar al mismo resultado del método anterior n X ri = O (n) i=1 para tener complejidad amortizada O (1) por operación. Complejidad amortizada– p. 21 Intento: pi = t1 + 2t2 Habrá que poder pagar el costo de añadir la clave nueva (t1 ) y preparar para mover la clave misma después a un arreglo nuevo de tamaño doble (t2 ) además de preparar mover todas las claves anteriores (uno por elemento, el otro t2 ). Complejidad amortizada– p. 22 Requisito ∀i : ai = ri + Φi − Φi−1 ≤ pi = t1 + 2t2 . Denotamos por ci el número de claves guardados en el arreglo en el estado Di . Claramente ci = ci−1 + 1. Complejidad amortizada– p. 23 Intento: Φi = 2t2 ci − t2 ei ei = el espacio del arreglo después de Oi inserciones fáciles: ei = ei−1 inserciones difíciles: ei = 2ei−1 = 2ci−1 Complejidad amortizada– p. 24 Inserción fácil ai = t1 + (2t2 (ci−1 + 1) − t2 ei−1 ) − (2t2 ci−1 − t2 ei−1 ) = t1 + 2t2 = pi . El costo amortizado es igual al costo planeado. Complejidad amortizada– p. 25 Inserción difícil ai = (t1 + t2 ci−1 ) + (2t2 (ci−1 + 1) − t2 2ci−1 ) − (2t2 ci−1 − t2 ci−1 ) = t1 + 2t2 = pi , Cumple: ai = t1 + 2t2 para todo i. Complejidad amortizada– p. 26 Eliminaciones Cada inserción tiene costo amortizado O (1) y el costo real de una serie de n operaciones es O (n). Eliminaciones: si un arreglo de tamaño ei contiene solamente ci ≤ 0,25ei claves, su tamaño será cortado a la mitad: ei+1 = 0,5ei . El costo de la reducción del tamaño es t2 ci , porque habrá que mover cada clave. Complejidad amortizada– p. 27 Costo de eliminación Marcamos el precio de una eliminación simple (que no causa un ajuste de tamaño) con t3 . Vamos a cubrir el gasto del reducción por cobrar por eliminaciones que ocurren cuando el arreglo cuenta con menos que 0,5ei claves guardadas en el estado Di . Complejidad amortizada– p. 28 El ahorro Entonces, por cada eliminación de ese tipo, ahorramos t2 pesos. Al de reducir el tamaño, hemos ahorrado emomento i − 1 pesos (nota que ei solamente cambia cuando 4 duplicamos o reducimos, no en operaciones baratas). Ese ahorro paga la eliminación cara. Complejidad amortizada– p. 29 Función de balance Elegimos ahora una función de balance definido por partes: ei 2t2 ci − t2 ei , si ci ≥ 2 , Φi = e1 t2 − t2 ci , si ci < ei . 2 2 Complejidad amortizada– p. 30 El caso de inserciones ei Para el caso ci > , no cambia nada a la situación anterior 2 ni para inserciones baratas ni para las caras: ai = t1 + 2t2 . ei Para el otro caso ci < , tenemos 2 ai = ri + Φ i − Φi−1 t2 e i t2 ei−1 = t1 + − t 2 ci − − t2 ci−1 2 2 = t1 − t2 , porque para estas inserciones, siempre tenemos ei = ei−1 y son siempre baratas (nunca causan que se duplique el espacio). Complejidad amortizada– p. 31 Inserciones cuando “cambia” Φ ei El caso donde ci = : 2 ai = ri + Φi − Φi−1 t2 ei−1 = t1 + (2t2 ci − t2 ei ) − − t2 ci−1 2 = t1 + 2t2 ci − 2t2 ci − t2 ci + t2 (ci − 1) = t1 − t2 . Complejidad amortizada– p. 32 ei Eliminaciones baratas: ci ≥ 2 ai = = = = ri + Φi − Φi−1 t3 + (2t2 ci − t2 ei ) − (2t2 ci−1 − t2 ei−1 ) t3 + 2t2 (ci−1 − 1) − 2t2 ci−1 t3 − 2t2 . Complejidad amortizada– p. 33 Eliminaciones al “cambio” de Φ ei Con ci = − 1: 2 ai = ri + Φ i − Φi−1 t2 e i = t3 + − t2 ei − (2t2 ei−1 − t2 ei ) 2 3t2 ei = t3 + − t2 (ei−1 − 1) − 2t2 ci−1 2 = t3 + 3t2 ci−1 − t2 ei−1 + t2 − 2t2 ci−1 = t3 + t2 . Complejidad amortizada– p. 34 Caso de ahorro ei e1 − 1 mientras ci−1 ≥ + 1: ci < 2 4 ai = ri + Φ i − Φi−1 t2 e i 1 t2 ei−1 = t3 + − t 2 ci − − t2 ci−1 2 2 = t3 − t2 (ci−1 − 1) + t2 ci−1 = t3 + t2 . Complejidad amortizada– p. 35 Eliminaciones con ci−1 ei−1 = 4 ai = ri + Φ i − Φi−1 t2 ei−1 t2 e i − − t2 ci−1 = t 3 + t 2 ci − 2 2 t2 ei−1 t2 ei−1 = t3 + − + t2 ci−1 4 2 t2 ei−1 t2 ei−1 + = t3 − 4 4 = t3 . Complejidad amortizada– p. 36 Resumen En todos los casos llegamos a tener una complejidad amortizada constante O (1) por operación. =⇒ La complejidad de cualquier combinación de n inserciones y/o eliminaciones, la complejidad amortizada total es O (n). Complejidad amortizada– p. 37 Invariante de costo La regla general de poder lograr la meta de “siempre poder pagar las operaciones que quedan de hacer” es asegurar que se mantenga un invariante de costo (inglés: credit invariant) durante toda la sucesión. Complejidad amortizada– p. 38 Invariante del ejemplo ei 1. Si ci = , el balance Φi es cero. 2 e ei i 2. Si ≤ ci < , el balance Φi es igual a la cantidad 4 2 de celdas libres en las posiciones de la segunda cuarta parte del arreglo. ei 3. Si ci > , el balance Φi es igual a dos veces la 2 cantidad de claves guardados en las posiciones de la segunda mitad del arreglo. Complejidad amortizada– p. 39 Árboles biselados n m = la cantidad de claves guardadas en el árbol = el número de operaciones realizados al árbol Queremos mostrar que la complejidad amortizada de cualquier sucesión de m operaciones es O (m log n). Complejidad amortizada– p. 40 Costo planeado Marcamos con R(v) el ramo la raíz de cual es el vértice v y con |R(v)| el número de vértices en el ramo (incluyendo a v). Sea r la raíz del árbol completo. Definimos µ(v) = ⌊log |R(v)|⌋. El costo planeado de las operaciones será pi = O (log n) = O (log |R(r)|) = O (µ(r)) . Complejidad amortizada– p. 41 splay Cada operación constituye de un número constante de operaciones splay y un número constante de operaciones simples. =⇒ basta con establecer que la complejidad amortizada de una operación splay es O (µ(r)). Complejidad amortizada– p. 42 Cuentas bancarias Pensamos en este caso que cada vértice del árbol tiene una cuenta propia, pero el balance está en uso de todos. Mantenemos el siguiente invariante de costo: cada vértice v siempre tiene por lo menos µ(v) pesos en su propia cuenta. Complejidad amortizada– p. 43 Teorema Cada operación splay(v, A) requiere al máximo (3 (µ(A) − µ(v)) + 1) unidades de costo para su aplicación y la actualización del invariante de costo. Complejidad amortizada– p. 44 Demostración splay consiste de una sucesión de rotaciones. En el caso que el vértice u que contiene la llave de interés tenga un padre t, pero no un abuelo, hace falta una rotación simple derecha. Complejidad amortizada– p. 45 Caso 1: sin abuelo Aplica µdespués(u) = µantes (t) µdespués(t) ≤ µdespués(u) Mantener el invariante cuesta: µdespués (u) + µdespués(t) − (µantes (u) + µantes t)) = µdespués(t) − µantes (u)) ≤ µdespués(u) − µantes (u). El peso que sobra se gasta en las operaciones de tiempo constante y cantidad constante por operación splay (comparaciones, actualizaciones de punteros, etcétera). Complejidad amortizada– p. 46 Caso 2: con abuelo En el segundo caso, u tiene el padre t y un abuelo t′ . Hay que hacer dos rotaciones derechas — primero para mover t al lugar del abuelo, empujando el abuelo abajo a la derecha y después para mover u mismo al lugar nuevo del padre. El costo total de mantener el invariante es T = µdespués(u) + µdespués(t) + µdespués(t′ ) −µantes (u) − µantes (t) − µantes (t′ ). Complejidad amortizada– p. 47 Caso 2 cont. Por la estructura de la rotación aplica que µdespués(u) = µantes (t′ ), por lo cual T = = ≤ = µdespués(t) + µdespués(t′ ) − µantes (u) − µantes (t) (µdespués(t) − µantes (u)) + (µdespués(t′ ) − µantes (t)) (µdespués(u) − µantes (u)) + (µdespués(u) − µantes (u)) 2 (µdespués(u) − µantes (u)) . Complejidad amortizada– p. 48 Detalles Si logramos tener µdespués(u) > µantes (u), nos queda por lo menos un peso para ejecutar la operación. Hay que analizar el caso que µdespués(x) = µantes (x). Necesitamos asegurarnos que T ≤ 0, o sea, no nos cuesta nada mantener el invariante válido. (La derivación será un ejercicio.) Llegamos a µdespués(u) = µantes (u), por lo cual T < 0. El tercer caso de splay es una rotación doble izquierda-derecha. Omitimos sus detalles. Complejidad amortizada– p. 49 Resumen al insertar, asignar O (log n) pesos al elemento al unir, se asigna a la raíz nueva O (log n) pesos cada operación puede gastar O (log n) pesos en ejecutar las operaciones splay el mantenimiento del invariante de costo y las operaciones adicionales que se logra en tiempo O (1) =⇒ cada operación tiene costo amortizado O (log n) =⇒ la complejidad amortizada de una sucesión de m operaciones es O (m log n) Complejidad amortizada– p. 50 Montículos de Fibonacci Invariante de costo: cada raíz tiene un peso y cada vértice marcado tiene dos pesos. Los costos planeados de inserción, decrementación del valor de clase y unir dos montículos son un peso por operación. La eliminación del mínimo tiene costo planeado de O (log n) pesos. Complejidad amortizada– p. 51 Insertar una clave Lo único que se hace es crear una raíz nueva con la clave nueva y un peso. Esto toma tiempo constante O (1). Complejidad amortizada– p. 52 Disminuir una clave Movemos el vértice con la clave a la lista de raíces en tiempo O (1) junto con un peso. Si el padre está marcado, su movimiento a la lista de raízes se paga por el peso extra que tiene el vértice marcado — también se le quita la marca por la operación, por lo cual no sufre el invariante. El otro peso el vértice padre lleva consigo. Si hay que marcar el abuelo, hay que añadirle dos pesos. El tiempo total es constante, O (1). Complejidad amortizada– p. 53 Unir dos montículos Simplemente unimos las listas de raíces. Por la contracción de las listas que se realiza al eliminar el mínimo, el costo de esta operación es O (1). Esta operación no causa ningún cambio en el invariante. Hay que tomar en cuenta que estamos pensando que las listas de raíces están implementadas como listas enlazadas, por lo cual no hay que copiar nada, solamente actualizar unos punteros. Complejidad amortizada– p. 54 Eliminar el mínimo La operación en sí toma tiempo O (1). Además depositamos un peso en cada hijo directo del vértice eliminado. Contracción de la lista de raíces: Gastamos los pesos de las raíces mismas. Después depositamos de nuevo un peso en cada raíz. Son O (log n) raíces. =⇒ Complejidad amortizada O (log n). Complejidad amortizada– p. 55 Eliminar elemento cualquiera Primero reducir su valor a −∞. Después quitando el mínimo. Esto toma tiempo O (1) + O (log n) ∈ O (log n). Complejidad amortizada– p. 56 Complejidad de estructuras Oper. L A M B F Inser. Θ (n) O (log n) O (log n) O (log n) O (1) Ubic. mín. O (1) O (1) O (1) O (log n) O (1) Elim. mín. O (1) O (log n) O (log n) O (log n) O (log n) ∗ Elim. O (1) O (log n) O (log n) O (log n) O (log n) ∗ Dism. Θ (n) O (log n) O (log n) O (log n) O (1) ∗ Unir Θ (n) Θ (n) Ω (n) O (log n) O (1) Listas (L), árboles balanceados (A), montículos (M), montículos binomiales (B) y montículos de Fibonacci (F). Las complejidades amortizadas llevan un asterisco (∗ ). Complejidad amortizada– p. 57 Tarea para entregar el martes Demuestra que en el análisis de árboles biselados, de los suposiciones µdespués(u) = µantes (u) y µdespués(u) + µdespués(t) + µdespués(t′ ) ≥ µantes (u) + µantes (t) + µantes (t′ ) t siendo el padre de u y t′ el abuelo de u y la operación siendo rotación doble derecha, se llega a una contradicción. Complejidad amortizada– p. 58