108 ANEXO A OPERACIONES Y ALGORITMOS DEL ÁRBOL R OPERACIONES BÚSQUEDA El algoritmo de búsqueda desciende en el árbol desde de la raíz en una forma similar al árbol B. Sin embargo, más de un subárbol bajo un nodo visitado puede necesitar ser buscado, ya que no es posible garantizar un buen desempeño del peor caso. Sin embargo, con datos semejantes el algoritmo de actualización puede mantener unas forma que permita al algoritmo de búsqueda eliminar regiones irrelevantes del espacio de indexación, y examinar solamente los datos cercanos al área de búsqueda. Se denota E.I el índice de entrada del rectángulo E y E.p el identificador de tupla o el apuntador al hijo. INSERCIÓN La inserción de registros de índices para nuevas tuplas de datos es similar a la inserción en un árbol B, en donde los nuevos registros de índice son adicionados en las hojas, los nodos que se llenan son divididos y la división se propaga hacia arriba en el árbol. 109 ALGORITMOS BÚSQUEDA Dado un árbol R donde el nodo raíz es T, encuentra todos los registros de índice donde los rectángulos se solapan con el rectángulo de búsqueda S. S1. Buscar subárboles Si T no es una hoja, chequea cada entrada E para determinar si E.I se solapa con S. Para todas las entradas que se solapan, invoca s BUSCAR sobre el subárbol donde el nodo raíz es apuntado por E.p. S2. Buscar en nodos hojas Si T es una hoja, chequea todas las entradas de E para determinar si E.I se solapa con S. Si es así, E es una registro cualificado. INSERCIÓN La inserción de registros de índices para nuevas tuplas de datos es similar a la inserción en un árbol B, en donde los nuevos registros de índice son adicionados en las hojas, los nodos que se llenan son divididos y la división se propaga hacia arriba en el árbol. Inserta una nueva entrada de índice E en el árbol R. I1. Encontrar la posición para un nuevo registro Invoca Escoger_Hoja para seleccionar un nodo hoja L en el cual se va a colocar a E. I2. Adicionar el registro en el nodo hoja 110 Si L tiene espacio para otra entrada, adicionar E. De lo contrario, invocar a Dividir_Nodo para obtener a L y a LL conteniendo a E y todas las entradas anteriores de L. I3. Propagar cambios hacia arriba Invocar Ajustar_Arbol sobre L, y por LL si ocurrió una división. I4. Aumentar altura en el árbol Si la propagación de la división de un nodo causa que la raíz se divida, crear un nuevo nodo raíz donde los hijos son los dos nodos resultantes. ESCOGER_HOJA Selecciona un nodo hoja en el cual se va a colocar una nueva entrada de índice E. CL1. Inicializar Fije a N a ser el nodo raíz. CL2. Chequear hoja Si N es una hoja, retorne N. CL3. Escoger subárbol Si N no es una hoja, fije a F a ser la entrada en N, en donde el rectángulo F.I necesite la mínima ampliación para incluir a E.I. Si existen varias entradas, escoger la entrada con área de rectángulo más pequeña. CL4. Descender hasta que una hoja sea alcanzada Fije a N a ser el nodo hijo apuntado por F.p repita desde CL2. 111 AJUSTAR_ ÁRBOL Asciende desde un nodo hoja L hasta la raíz, ajustando los rectángulos cubiertos y propagando la división de nodos cuando sea necesario. AT1. Inicializar Sea N=L. Si L ha sido dividido previamente, fije NN a ser el segundo nodo resultante. AT2. Chequear si terminó Si N es raíz, parar. AT3. Ajustar los rectángulos cubiertos por la entrada padre Fije a P a ser el nodo padre de N, y sea EN la entrada de N en P. Ajustar EN.I de tal forma que ajuste todas las entradas de los rectángulos cubiertas por N. AT4. Propagar división de nodos hacia arriba Si N tiene un padre NN resultante de una división anterior, crear una nueva entrada ENN con ENN.P apuntando a NN y ENN.I incluyendo todos los rectángulos en NN. Adicionar ENN a P si hay espacio. De lo contrario, invocar a DIVIDIR_NODO para producir a P y a PP conteniendo a ENN y todas las entradas anteriores de P. AT5. Moverse hacia el próximo nivel hacia arriba Fije a N=P y NN=PP si ha ocurrido una división. Repetir desde AT2. 112 DE BORRADO Remueva los registros de índice E de un árbol R. D1. Encontrar los nodos que contienen el registro. Invocar a Encontrar_Hoja para localizar el nodo hoja L que contiene a E. Parar si el registro no ha sido encontrado. D2. Borrar registro Remueva E desde L. D3. Propagar cambios invocar Condensar_Arbol, pasando L. D4. Reducir árbol Si el nodo raíz sólo tiene un hijo después de que el árbol ha sido ajustado, hacer que el hijo sea la nueva raíz. ENCONTRAR_HOJA Dado un árbol R donde el nodo raíz es T, encuentra el nodo hoja que contiene la entrada de índice E. FL1. Buscar subárboles Si T no es una hoja, chequear cada entrada F en T para determinar si F.I abarca a E.I. Para cada entrada invocar Encontrar_Hoja sobre el árbol donde la raíz está apuntada por F.P hasta que E sea encontrada o hasta que todas las entradas hayan sido chequeadas. 113 FL2. Buscar nodo hoja del registro Si T es una hoja, chequear cada entrada para ver si es E. Si encuentra a E retorna T. CONDENSAR_ÁRBOL Dado un nodo hoja L desde el cual una entrada ha sido borrada, eliminar el nodo si tiene también pocas entradas y relocalizar las entradas. Propagar la eliminación de nodos hacia arriba si es necesario. Ajustar todos los rectángulos abarcados en la trayectoria hasta la raíz. CT1. Inicializar Fijar N=L. Fije a Q, a ser el conjunto de nodos eliminados, a ser vacio. CT2. Encontrar la entrada del padre Si N es la raíz, ir a CT6. De lo contrario, fije a P a ser el padre de N, y fije a EN a ser la entrada de N en P. CT3. Eliminar nodos Si N tiene menos de m entradas, borrar EN de P y adicionar N a Q. CT4. Ajustar rectángulos abarcados Si N no ha sido eliminado, ajustar EN.I a contener todas las entradas en N lo más preciso posible. CT5. Moverse en el árbol hacia el nivel superior Fijar N=P y repetir desde CT2. CT6. Reinsertar las entradas huérfanas Reinsertar todas las entradas de los nodos del conjunto Q. 114 Las entradas eliminadas de los nodos hojas, son reinsertadas en las hojas del árbol como se describió en al algoritmo de Inserción, pero las entradas de los nodos del los niveles superiores deben ser colocadas arriba en el árbol, de tal manera que las hojas de ese subárbol dependiente estarán en el mismo nivel de las hojas del árbol principal. ACTUALIZACIÓN Si una tupla de datos es actualizada de tal forma que el rectángulo cubierto se modifique, el registro de índice debe ser borrado, actualizado y luego reinsertado, de modo que encuentre un lugar a la derecha en el árbol. DIVISIÓN DE NODOS En orden a adicionar una nueva entrada a un nodo completo que contiene M entradas, es necesario dividir el conjunto de M+1 entradas en dos nodos. La división deberá hacerse de tal forma de que la probabilidad, de que los dos nuevos nodos necesiten ser examinados en búsquedas futuras, sea mínima. Debido a que la decisión de visitar un nodo depende de si los rectángulos abarcados traslapan el área de búsqueda, el área total de los dos rectángulos abarcados después de la división, debe ser minimizada. La figura 17 ilustra éste punto. El área de los rectángulos abarcados en el caso de “mala división” es mucho más grande que la del caso de “buena división”. 115 El mismo criterio debe ser utilizado en e procedimiento de Escoger_Hoja para decidir en donde se inserta una nueva entrada de índice: en cada nivel del árbol, el subárbol escogido deberá ser aquel en donde el rectángulo abarcado tenga la menor amplitud. A continuación se mostrarán los algoritmos para particionar un conjunto de M+1 entradas en dos grupos. ALGORITMO EXHAUSTIVO La forma más directa de encontrar el área mínima del nodo a dividirse, es generando todos los posibles grupos y escogiendo el mejor. Sin embargo, el número de posibilidades es aproximadamente 2M-1 y un valor razonable de M es 50. Esto es debido a que un rectángulo en dos dimensiones puede ser representado por cuatro números de cuatro bytes cada uno. Y si el apuntador también toma cuatro bytes, cada entrada requiere de 20 bytes. Una página de 1024 bytes deberá contener alrededor de 50 entradas. Por lo tanto, el número posible de divisiones será muy grande. ALGORITMO DE COSTO CUADRÁTICO Este algoritmo pretende encontrar un área pequeña para la división, pero no garantiza encontrar el área más pequeña. El costo es cuadrático en M y lineal en el número de dimensiones. El algoritmo escoge dos de las M+1 entradas a ser los primeros elementos de los dos nuevos grupos, escogiendo el par que gaste la mayor área si ambos fueran colocados en el mismo grupo, esto es, el área de un rectángulo que abarque ambas entradas, menos el área de cada una de las entradas que son mayores. El resto de entradas se asignan luego, de a una, a los dos grupos. En cada paso el área de expansión requerida por adicionar dichas 116 entradas a los grupos, es calculada, y la entrada asignada es la que muestre la mayor diferencia entre los dos grupos. ALGORITMO DIVISIÓN_CUADRÁTICA Divide un conjunto de M+1 entradas de índices en dos grupos QS1. Escoger la primera entrada para cada grupo Invoca el algoritmo Escoger_Origen para seleccionar dos entradas que irán a ser el primer elemento de cada uno de los grupos. QS2. Distribuir entradas Repetir Invocar Distribuir_Entradas Hasta que todas las entradas son distribuidas o hasta que uno de los grupos tenga M-m+1 entradas. QS3. Si sobran entradas, asignarlas al otro grupo ALGORITMO Escoger_Origen Selecciona dos entradas que irán a ser las primeras en cada grupo. PS1. Calcular ineficiencia de las entradas agrupadas Por cada par de entradas E1 y E2 forme un rectángulo J incluyendo a E1.I y a E2.I. Calcular d = área(J) - área(E1.I) - área(E2.I) 117 PS2. Escoger el par que ocupe más espacio Escoger el par con mayor d. ALGORITMO Distribuir_Entradas DE1. Invocar Escoger_Próximo para escoger la próxima entrada a ser asignada. DE2. Seleccionar entrada a asignar Adicionar al grupo donde el rectángulo que lo abarque tenga la más pequeña amplitud. Si existe empate, éste se resuelve adicionando la entrada al grupo con área más pequeña, luego al que tenga menos entradas, luego a cualquier otro. ALGORITMO Escoger_Próximo Seleccionar una de las entradas restantes para clasificarla en un grupo. PN1. Determinar el costo de colocar cada entrada en cada grupo Para cada entrada E que aún no está en el grupo, calcular d1 = el incremento del área requerida en el rectángulo cubierto por el grupo 1 al incluir E.I. Calcular d2 = en forma similar para el grupo 2. PN2. Escoger la entrada con mayor preferencia. Escoger la entrada que tenga la mayor diferencia entre d1 y d2. 118 ALGORITMO DE COSTO LINEAL Este algoritmo es lineal en M y en el número de dimensiones. La División Lineal es idéntica a la División Cuadrática, pero utiliza una versión diferente en el procedimiento de Escoger_Origen. El algoritmo de Escoger_Próximo escoge cualquiera de las entradas. ALGORITMO Escoger_Origen_Lineal Selecciona dos entradas a ser los primeros elementos en cada uno de los dos grupos. LPS1. Encontrar los extremos de los rectángulos en todas las dimensiones. Para cada una de las dimensiones hallar la entrada donde el rectángulo tenga la parte inferior más alta, y la parte superior más baja. Registrar la separación. LPS2. Ajustar la forma del rectángulo almacenado Normalizar las separaciones dividiendo éstas por el ancho del conjunto de entradas, en la correspondiente dimensión. LPS3. Seleccionar el par con máximo extremo Escoger la pareja con la mayor separación normalizada en cualquier dimensión.