Ordenamiento
Extenderemos el algoritmo de MergeSort a memoria secundaria. MergeSort comienza dividiendo el arreglo en dos y se invoca recursivamente, hasta que los subarreglos que tiene que ordenar son de tamaño 1. Entonces vuelve de la recursión, uniendo los subarreglos ordenados derecho e izquierdo en forma ordenada.
En un entorno de memoria secundaria, tiene sentido detener esta recursión cuando los subarreglos a ordenar son de tamaño . En este momento se puede leer el bloque de disco, ordenarlo, y reescribirlo a costo en I/Os. A la vuelta de la recursión, la unión se hace leyendo secuencialmente los dos subarreglos, usando un buffer de tamaño en memoria para cada subarreglo y otro para el resultado del merging. En total, todas las uniones de un nivel del árbol requieren leer el arreglo completo y reescribirlo, a costo . Como la recursión se detiene en los subarreglos de tamaño , la cantidad de niveles en la recursión es . Es decir, esta variante de MergeSort requiere I/Os.
Podemos mejorar este costo deteniendo la recursión cuando el subarreglo a ordenar es de tamaño . En este punto, simplemente se lee el subarreglo a memoria, se ordena (gratis), y se reescribe ordenado. Con la reducción resultante de la cantidad de niveles, el costo de ordenar pasa a ser I/Os.
Se consigue una reducción adicional mediante aumentar la aridad del árbol de recursión, es decir, no particionando el subarreglo en dos sino en más. El único límite a la aridad del árbol de la recursión es que, al unir, se necesita tener un buffer de elementos en memoria por cada archivo que se une, por lo cual éstos no pueden exceder . Ahora la cantidad de niveles es , por lo que la cantidad de I/Os del algoritmo se reduce a (notar que las últimas dos expresiones difieren sólo en , que es un término de orden inferior).
Esta complejidad es bastante buena en la práctica. Considerando una memoria de GBs y un bloque de KBs, se pueden ordenar PBs (petabytes, ) con sólo dos pasadas de lectura y dos de escritura sobre los datos. En la práctica, sin embargo, cuando se usan discos magnéticos, puede ser mala idea llegar realmente a la aridad , pues esto aumenta la cantidad de seeks a posiciones aleatorias para leer bloques de muchos archivos distintos. Experimentalmente el óptimo suele ser unir de a unas decenas de archivos por vez. En este caso, ordenar unos PBs puede requerir unas 10 lecturas y escrituras del arreglo completo.
Note que, con la estructura adecuada para la unión, el costo de CPU de este algoritmo es . Cuando se hace la unión de archivos, debe usarse una cola de prioridad en memoria principal para extraer el mínimo entre los primeros elementos de cada uno de los buffers. Eso hace que el costo de unir todos los datos de un nivel sea . Multiplicado por los niveles, nos da . A esto debe agregarse el costo de CPU de ordenar los subarreglos de largo en memoria, en el último nivel de la recursión, , lo que en total nos da .
📄️ Cota Inferior
Demostraremos que este algoritmo de ordenamiento es óptimo si se procede por