Consideraciones sobre los algoritmos de ordenación
Los algoritmos de ordenación son vitales en informática, no solo para poder presentar los datos ordenados sino para poder optimizar las búsquedas en ellos, como demuestra que el volumen 3 de la serie de 7 de "El arte de programar ordenadores" de Donald Knuth se dedique a "Ordenación y búsqueda". Se considera que el primer algoritmo de ordenación fue desarrollado en 1951 por Betty Holberton, una de las 6 programadoras del ENIAC.
Los primeros ordenadores disponían de muy pocos recursos, por lo que los algoritmos iterativos que no requieren recursos podían usarse, pero si los datos eran numerosos había que recurrir a memoria externa como las cintas magnéticas y en esos casos se usaban la clasificación por mezcla de datos que eran mas efectivas. Cuando la potencia de calculo, la cantidad de memoria y los recursos externos como los discos duros fueron habituales, se crearon los algoritmos recursivos. Aparte de estos hay un cuarto grupo que no son estrictamente de ordenación, mas bien hacen lo contrario y en lugar de ordenar desordenan, que se emplean para estudiar los otros algoritmos.
Para elegir un algoritmo de ordenación hay que evaluar los varios puntos del algoritmo, que son los que utilizan más recursos del ordenador:
- Comparaciones: Para poder ordenar hay que comparar valores, el tiempo empleado en una comparación es proporcional a tamaño del dato, comparar enteros es rápido, comparar cadenas es mas lento. Evidentemente contra menos comparaciones mas rápido, y siempre el que menos necesite será de los más rápidos.
- Intercambios: Para ordenar hay que cambiar el orden de los registros, este tiempo puede ser proporcional al tamaño del dato si se mueve físicamente, o ser lineal si se cambian punteros nada mas. Los algoritmos iterativos mueven muchos datos intercambiándolos en memoria, los recursivos mueven una cantidad pequeña de datos también intercambiándolos en memoria, mientras que los de mezcla los mueven menos pero lo hacen a otras zonas de memoria ocupando mucha mas.
- Memoria auxiliar: Los algoritmos iterativos necesitan muy poco memoria auxiliar para su proceso, los recursivos requieren una cantidad proporcional a los datos que van a manejar, mientras que los de mezcla requieren una cantidad de memoria igual o mayor a la que ocupan los datos.
- Ubicación: Otro aspecto importante es donde de encuentran los datos, si estos están en memoria o en una unidad externa como un fichero en disco, los tiempos de acceso son mucho mas elevados, por lo que minimizando la cantidad de accesos se pueden lograr aumentos importantes de velocidad.
Clasificación según su estructura y análisis general
Como ingeniero informático no puedo dejar de mencionar los ordenes de tiempo y ocupación de memoria de los algoritmos, pero no voy a entrar mucho más, el análisis de algoritmos es un tema muy bonito y complejo por lo que si alguno lo desea puedo discutirlo en otras entradas.- Algoritmos por intercambio: Se basan en ir intercambiando
los elementos de la lista unos con otros ubicándolos o al menos acercándolos
a su lugar correcto. Hay dos variantes generales:
- Algoritmos por intercambio iterativos Son los que menos recursos de espacio necesitan, normalmente solo espacio para guardar uno de los datos a clasificar temporalmente, a cambio requieren mas pasadas entre los datos para ordenarlos. Son mas lentos, pero muy sencillos de programar, por lo que para cantidades pequeñas de datos son los preferidos, el mas conocido es el de Burbuja, que son 4 líneas de programa, en general es también el más lento pero si en un PC moderno ordenar por burbuja 100 datos cuesta menos de un segundo, ¿para que emplear tiempo en hacer otros algoritmos mas complejos? En general tardan un tiempo proporcional al cuadrado del número de registros a ordenar por tanto Velocidad Θ(n2), y requieren solo de un solo dato en memoria por tanto Ocupación Θ(1), por lo que en caso de poca memoria son los mas recomendables, por ejemplo si queremos ordenar 1000 bytes en una máquina con 1024 posiciones de memoria, con esos 24 bytes podemos programar uno de estos algoritmos.
- Algoritmos por intercambio recursivos o por partición: Consiguen mucha mejor velocidad ya que ubican mas elementos en su lugar o muy cerca a la primera minimizando los intercambios. Van dividiendo la lista en sublistas a partir de un pivote, y las ordenan de forma que todos los elementos de la sublista anterior al pivote sean menores que él y los posteriores todos mayores, pasando luego a realizar el mismo proceso para cada una de las sublistas que se generan. Requieren mas memoria para la recursividad, pero reducen drásticamente el número de intercambios sobre los recursivos. El famoso QuickSort es el considerado mas rápido de los algoritmos de ordenación en casi todos los casos. Su tiempo en general es del orden de Velocidad Θ(n log n), igual que para los de mezcla, pero al ser recursivo Ocupación Θ(log n). Si dispones de memoria suficiente y de un lenguaje recursivo no dudes en usarlo, es sencillo de programar y el mas rápido casi siempre.
- Algoritmos por inserción: Variante de los de intercambio que buscan el lugar adecuado para un elemento y lo ubican en su lugar, desplazando el resto de elementos y minimizando así el número de intercambios. El mas conocido es el de Ordenación por Inserción. También los hay iterativos y recursivos, y su tiempo y ocupación en general es similar a los de intercambio, iterativos Velocidad Θ(n2) y recursivos Velocidad Θ(n log n), aunque al reducir el número de intercambios siempre son un poco mas rápidos. La ocupación en memoria es también similar, los iterativos Ocupación Θ(1) y los recursivos Ocupación Θ(log n).
- Algoritmos por selección: Estos algoritmos funcionan en dos pasos, primero utilizan estructuras auxiliares para clasificar los datos (usualmente creando un árbol), y luego lo recorren montando la lista ordenada. El mas usado es el de Ordenación por Montones (un montón es una forma de arbol binario que usa una lista) Al recorrer la lista una sola vez son rápidos y su velocidad es Velocidad Θ(n log n), pero requieren montar una estructura auxiliar por lo que necesitan tanta memoria como datos a ordena, y por tanto Ocupación Θ(n)
- Algoritmos de mezcla: Son los preferidos para ordenar cuando hay ubicaciones suficientes para ello, y siempre que se realiza en medios de almacenamiento auxiliares y no en memoria. Son mas rápidos pero requieren en general mucho espacio, habitualmente el mismo que ocupan los datos a ordenar si se realizan en memoria, pero se reduce mucho si es en disco. El mas popular entre estos es Merge Sort. Su tiempo varía mucho pero suelen estar en el orden de Velocidad Θ(n log n), y ocupan normalmente lo mismo que los datos a clasificar, por tanto Ocupación Θ(n) trabajando en memoria, pero pueden trabajar muy bien con memoria externa y en este caso Ocupación Θ(1). Apoyados en un medio de almacenamiento masivo son los mejores si estos ya están en memoria externa, así podemos ordenar en poco tiempo una gran cantidad de datos que no quepan en memoria.
- Algoritmos de distribución: Requieren un conocimiento previo de las claves a ordenar, ya que los van clasificando en grupos según un criterio, de forma similar a como se reparte el correo clasificándolo por códigos postales, luego por calles, por números de calle y finalmente por viviendas. El mas popular entre estos es el de Ordenación por Casilleros. Su tiempo es bueno ya que suelen ser Velocidad Θ(nk) , pero ocupan normalmente más que los datos a clasificar, por tanto Ocupación Θ(nk), con una k pequeña que puede llegar a ser 1 en algunos casos, pero con la limitación de ser muy dependientes del conocimiento de los datos a ordenar.
- Algoritmos concurrentes: En este grupo se incluyen algoritmos que se han diseñado específicamente para máquinas con varios procesadores (o varios núcleos) que trabajan a la vez de manera coordinada ordenando los datos. El mas usado es el Cube para ordenar los famosos "cubos" de análisis de datos, realmente arreglos multidimensionales.
- Algoritmos Híbridos: Es usual emplear varios algoritmos a la vez, por ejemplo Quicksort trabaja bien en listas grandes, pero mal cuando son ya pequeñas, por lo que se suele llamar a otro cuando las sublistas tienen ya pocos elementos. Este grupo de algoritmos hacen lo mismo, emplean dos métodos de ordenación complementarios para alcanzar el objetivo.
- Otros algoritmos: En este grupo se incluyen algoritmos especializados en un tipo de datos concreto, como grafos o qbits, o algoritmos muy imaginativos que son solo adecuados para el análisis de algoritmos ya que no son nada eficientes.
- Algoritmos de desordenación: Estos algoritmos son muy usados para producir resultados aleatorios en juegos. El mas antíguo es el Algoritmo del Sombrero. Estos algoritmos ordenan aleatoriamente los datos y repite el proceso hasta que por casualidad estén ordenados, por tanto no se puede usar para ordenar ya que su tiempo es del orden de los algoritmos intratables, con pocos datos puede ser del orden de Velocidad Θ(n!) aunque lo normal es que no acaben nunca. A cambio es muy usado para simular en un programa el barajar las cartas.
Algoritmos estables e inestables
Un registro puede contener claves primarias y secundarias de ordenación, por ejemplo cuando se ordena por Apellidos y Nombre una lista. Se considera un algoritmo estable cuando ordenan siempre los registros con claves primarias iguales en su orden por las secundarias, y se consideran inestables cuando no consiguen mantener el orden dentro de las secundarias. Esta inestabilidad se puede eliminar si unimos las claves primarias y secundarias en una clave única, al desaparecer las claves secundarias desaparece el problema.Orden y desorden
Un punto importante es cuan desordenados están los datos inicialmente, esto nos puede ayudar a elegir uno u otro algoritmo, hay alguno especializado en listas casi ordenadas. Una forma de añadir un elemento a una lista es ubicarlo al final y llamar a la ordenación para que lo ponga en su lugar, por eso se distinguen tres casos que son los que usaré en las pruebas:- Caso medio: Los datos están ordenados aleatoriamente, siendo este es el caso genérico que se usa para probar los algoritmos.
- Caso mejor: Los datos ya están ordenados, en este caso los últimos serán los primeros ya que la lenta Burbuja es el que mas rápido acaba normalmente. La comparación de este tiempo con el anterior nos proporciona una idea de la velocidad cuando los datos están casi ordenados.
- Caso peor: Los datos están en orden inverso, que es una buena prueba en la que los mejores algoritmos deben tardar mas o menos lo mismo que en el caso medio.
- Genero un arreglo con el tamaño que se indique, y lo relleno de números enteros aleatorios, que pueden estar repetidos. Será el que use luego con todos los algoritmos y así no hay diferencias de partida.
- Para cada algoritmo que deseo probar:
- Caso medio: Copio el arreglo sobre uno temporal, y llamo al algoritmo para ordenar el arreglo temporal de menor a mayor. Cuento la cantidad de comparaciones, la cantidad de intercambios y el tiempo en segundos que tarda en total.
- Caso mejor: Vuelvo a llamar al algoritmo con el arreglo temporal ya ordenado para que lo intente ordenar de menor a mayor, no debe mover ningún elemento del arreglo. Cuento la cantidad de comparaciones, la cantidad de intercambios y el tiempo en segundos que tarda en total
- Caso peor: Vuelvo a llamar al algoritmo con el arreglo temporal ya ordenado para que lo intente ordenar de mayor a menor, lo que debe cambiar todos los elementos del arreglo. Cuento la cantidad de comparaciones, la cantidad de intercambios, y el tiempo en segundos que tarda en total.
- Presento los resultados de todos los algoritmos.
Clasificación según su método de ordenación
Según el método que empleen para ordenar los datos hay una serie de familias de algoritmos. Los hay mas o menos rápidos, pero también se han desarrollado algoritmos impracticables, ya que el tiempo que tardan los hacen no abordables, y solo se han usado para el estudio de los algoritmos. No voy a desarrollar todos en superBASIC solo los mas habituales pues la lista es muy extensa, solo pongo aquí los algoritmos las más conocidos y ya son mas de 50:Por Intercambio |
Estos algoritmos son buenos trabajando sobre datos en memoria, trabajan
intercambiando elementos de la lista a ordenar para ir acercándolos
a su posición definitiva. Todos son variantes del denostado método de
la Burbuja, y en general Inestables.
|
---|---|
Por Inserción |
Se busca el lugar donde ubicar el elemento y se inserta en el, desplazando
el resto de elementos si es necesario.
|
Por Selección |
Estos algoritmos utilizan estructuras auxiliares en memoria para aumentar
la velocidad de proceso.
|
Por Mezcla |
Dividen los datos en grupos y van mezclándolos de forma ordenada. Su
época dorada vino con las cintas magnéticas, y hoy son buenos para ordenación
en disco.
|
Por Distribución |
Basados en la distribución de los elementos en varios casilleros, de
forma similar a como se reparte el correo agrupándolo por códigos postales.
Se diferencian principalmente en como se montan los casilleros.
|
Algoritmos Concurrentes |
Diseñados para el procesamiento en paralelo de los datos por varios
procesadores simultáneamente.
|
Algoritmos Híbridos |
Es habitual usar varios algoritmos diferentes para acelerar el proceso,
por ejemplo se suele usar mucho Quicksort que es bueno para listas grandes,
las va reduciendo de tamaño cada vez hasta que son pequeñas, momento
en que se usa otro algoritmo mas rápido con tamaños pequeños. Estos
algoritmos hacen algo similar
|
Otros Algoritmos |
|
Algoritmos desordenadores |
Estos algoritmos desordenan las listas aleatoriamente.
|
Este es un artículo memorable
ResponderEliminar- badaman