Programación de Sistemas Algoritmos de Ordenación Índice Por qué es importante la ordenación? Un par de ejemplos InsertionSort QuickSort Para cada uno veremos: En qué consisten, Casos extremos Eficiencia Ejemplos de implementación Algoritmos de ordenación 2
Por qué es importante? Facilita la búsqueda La información devuelta por un cómputo suele hacerse de forma ordenada para facilitar su manejo. Ej: palabras de un diccionario, ficheros en un directorio, catálogo de una biblioteca, etc Una ordenación de datos inicial puede facilitar el rendimiento de un algoritmo. Ej: encontrar valores repetidos Muchos de los cómputos invocan internamente un método de ordenación y el coste del método de ordenación utilizado determina el coste global. Algoritmos de ordenación Problema inicial Partimos de una estructura de datos lineal que contiene cierto número de elementos. Esos elementos son ordenables siguiendo algún criterio. Para comprobar si un algoritmo de ordenación es correcto y calcular su eficiencia es necesario estudiar los casos más favorables y desfavorables y casos especiales como: La estructura esté ordenada antes de empezar en orden creciente o decreciente. Que existan elementos repetidos Etc. Algoritmos de ordenación 4
InsertionSort Ordenación por inserción En qué consiste? Suponemos un array de elementos a[ ] Suponemos que el primer elemento a[0] está ordenado. Cogemos el siguiente elemento a[] y lo guardamos en una variable temporal tmp. Comparamos uno por uno con los elementos previamente ordenados para ver cual sería su sitio correcto. Lo colocamos en su ubicación correcta y Repetimos el proceso con el siguiente elemento suponiendo ahora que a[] y a[2] están ordenados y añadimos a[] Siempre suponemos que la matriz a[0]...a[p-] está ordenada cogemos el elemento a[p] y lo ubicamos correctamente Cogemos el elemento a[p+] Repetimos hasta que ya no quede ningún elemento nuevo que añadir Algoritmos de ordenación InsertionSort Implementación public static void insertionsort( int a[ ] ){ for( int p = ; p < a.length; p++ ){ //recorre desde p hasta fin del array int tmp = a[ p ]; int j; for(j = p ; j > 0 && tmp < ( a[ j - ] ); j-- ){//recorre desde p hasta principio del array a[ j ] = a[ j - ]; //desplazo hacia la dcha los elementos del vector mayores que tmp } a[ j ] = tmp; //inserto tmp en la posición en la que se para j (cuando dejo de desplazar elementos) } } Algoritmos de ordenación 6
InsertionSort Casos Extremos Cuando el array de entrada está ordenado en orden inverso Este sería el caso peor El bucle interno se ejecuta p+ veces por cada valor de P: N- Σ P= (p-)=+2++4+...n por tanto t ejecución sería Θ(N 2 ) Cuando el array de entrada está ordenado Este sería el caso mejor El bucle interno falla siempre y el t ejecución sería O(N) Estudiando el caso medio llegaríamos a que t ejecución es Θ(N 2 ) En problemas de ordenación el t ejecución depende no sólo del tamaño de los datos sino también de su orden específico Algoritmos de ordenación 7 Cómo medir grado de desorden? Inversiones Llamamos Inversión a cada pareja de elementos que no están ordenados entre sí. El número de inversiones es una medida del grado de desorden Un vector ordenado no contiene ninguna inversión Intercambiar 2 elementos desordenados elimina exactamente una inversión Ordenación: Si inicialmente tenemos I inversiones Tenemos que hacer I intercambios t ejecución sería O(I+N) El resto del trabajo es de orden N El nº medio de inversiones de un array de N elementos es N(N-)/4 t ejecución sería Ω(N 2 ) Ejemplo:{,,, 2, 6, } contiene 0 inversiones (,), (,2), (,6), (,), (,2), (,), (,2), (,6), (,) y (6,)
Algoritmos de ordenación Complejidad Los algortimos de ordenación funcionan eliminando inversiones Todo algoritmo de ordenación que ordene por intercambios de elementos adyacentes requiere en promedio t ejecución Ω(N 2 ) por ejemplo: InsertionSort SelectionSort BubleSort Para conseguir que se ejecute más eficientemente que t ejecución Ω(N 2 ) debe eliminar más de una inversión por intercambio Algoritmos de ordenación Ordenación rápida (Quicksort) Sea T[..n] una matriz de n elementos. Queremos ordenar estos elementos por orden ascendente. Pasos: Seleccionar un pivote. Poner todos los elementos mayores que el pivote a su derecha, y los menores a su izquierda. Aplicar recursivamente lo anterior a cada mitad de la matriz. Algoritmos de ordenación 0
Ordenación rápida (Quicksort)(II) Nos gustaría usar la mediana como pivote. Encontrar la mediana requiere un tiempo excesivo. Elegimos como pivote un elemento arbitrario. Queremos descomponer T[i..j] empleando como pivote p = T[i] Algoritmos de ordenación Ordenación rápida (Quicksort)(III) Inicializamos 2 punteros: k = i, L = j + Se incrementa k hasta que T[k] > p, y se decrementa L hasta que T[L] <= p Se intercambian T[k] y T[L] Se continúa mientras k > L Finalmente se intercambian T[i] y T[L] para poner el pivote en posición correcta. Algoritmos de ordenación 2
Ordenación rápida (Quicksort)(IV) algoritmo pivote(t[i..j]); var L) {Permuta los elementos de la matriz T[i..j] y proporciona un valor L tal que, al final, i <= L <= j; T[k] <= p para todo i <= k < L, T[L] = p, y T[k] > p para todo L < k <= j, en donde p es el valor iniciar de T[i]} p T[i] k i; L j + repetir k k + hasta que T[k] < p o k >= j repetir L L hasta que T[L] <= p mientras k < L hacer intercambiar T[k] y T[L] repetir k k + hasta que T[k] > p repetir L L hasta que T[L] <= p intercambiar T[i] y T[L] Algoritmos de ordenación Ordenación rápida (Quicksort)(V) Algoritmo de ordenación: algoritmo quicksort(t[i..j]) {Ordena la submatriz T[i..j] por orden no decreciente} si j i es suficientemente pequeño entonces insertar (T[i..j]) sino pivote(t[i..j], L) quicksort(t[i..l ]) quicksort(t[l +..J]) Para ordenar la matriz completa, basta con llamar a quicksort(t[..n]) Algoritmos de ordenación 4
Algoritmos de ordenación Ordenación rápida (Quicksort)(VI) Ejemplo. Matriz a ordenar: p = Se busca el primer elemento mayor que el pivote y el último elemento no mayor que el pivote: 6 2 4 6 2 4 6 2 4 Algoritmos de ordenación 6 Ordenación rápida (Quicksort)(VII) Se intercambian esos elementos: Se vuelve a explorar en ambas direcciones: Se intercambian 4 6 2 4 6 2 4 6 2
Ordenación rápida (Quicksort)(VIII) Se explora: 2 6 4 Los punteros se han cruzado: se coloca el pivote. 2 6 4 Se ordenan recursivamente las submatrices: 2 4 6 Algoritmos de ordenación 7 Ordenación rápida (Quicksort)(X) En término medio los subcasos no estarán demasiado desequilibrados. En ese caso, t(n) es O(n log n) Algoritmos de ordenación
Conclusiones InsertionSort Ω(N 2 ) es apropiado para pequeñas cantidades de datos QuickSort O(NlogN) tiene el mejor rendimiento pero es complicado de implementar es apropiado para cantidades grandes de datos. Algoritmos de ordenación Programación de Sistemas Algoritmos de Búsqueda
Índice Por qué es importante la búsqueda? Algunos ejemplos Búsqueda lineal Búsqueda Binaria Para todos ellos veremos: En qué consisten, Casos extremos Eficiencia Ejemplos de implementación Algoritmos de ordenación 2 Por qué es importante? Llamamos Búsqueda estática a la búsqueda que se hace sobre un array de elementos estáticos (que no modifican su valor en el t). Uno de los usos más frecuentes de los ordenadores es la búsqueda de información en estructuras de datos La eficiencia de un algoritmo de búsqueda depende de si el array sobre el que hacemos la búsqueda está o no ordenado. Ej: palabras de un diccionario, ficheros en un directorio, catálogo de una biblioteca, etc Muchos algoritmos de búsqueda invocan internamente un método de ordenación y el coste del método de ordenación utilizado determina el coste global. Algoritmos de ordenación 22
Problema inicial Dados: Una estructura de datos lineal que contiene cierto número de elementos array a[ ]. Un valor x del mismo tipo que los elementos almacenados en el array El algoritmo debe devolver La posición del elemento x en el array a[ ] o una indicación de que no existe Si x aparece más de una vez devuelve cualquiera de las posiciones en la que esté almacenado. Algoritmos de ordenación 2 LinearSearch Búsqueda Lineal En qué consiste? Partimos de: Un array de elementos a[ ] y Un elemento x Recorremos el array de izda a dcha comparando cada uno de sus elementos con x Si son iguales devolvemos la posición del elemento y termina la búsqueda Si son distintos seguimos buscando hasta llegar al final del array y si no lo encontramos lo notificamos con un mensaje o una excepción Algoritmos de ordenación 24
Búsqueda Lineal Implementación public static int LinearSearch(int a[], int x) { int resultado = -; for (int i = 0; i < a.length ; i++) { if (a[i] == x){ return resultado=i; }else{ resultado=-; } } return resultado; } Algoritmos de ordenación 2 Búsqueda Lineal Casos Extremos. Complejidad Búsqueda sin éxito t ejecución O(N) (Hay que recorrer todo el array) Búsqueda con éxito Caso peor t ejecución O(N) (Hay que recorrer todo el array) Caso medio t ejecución O(N) (Sólo recorremos la mitad del array) Algoritmos de ordenación 26
Búsqueda binaria Como buscar una palabra en un diccionario o un nombre en la guía telefónica. Es una reducción. Sea T[..n] un array ordenado por orden no decreciente. Problema: encontrar un elemento x en la matriz, si es que está. Algoritmos de ordenación 27 Búsqueda binaria (II) Solución obvia: examinar secuencialmente todos los elementos de T, hasta llegar al final de la matriz o hasta encontrar un elemento que no sea menor que x: Algoritmo secuencial(t[..n], x) {Búsqueda secuencial de x en una matriz} para i hasta n hacer si T[i] >= x entonces devolver i devolver n + Algoritmos de ordenación 2
Búsqueda binaria (III) Este algoritmo es O(r), siendo r el índice que se devuelve. (O() en el caso mejor y O(n) en el caso peor). Quedándome con el caso peor: O(n) Para acelerar la búsqueda: buscar x sólo en una de las dos mitades de T. Para saber con qué mitad quedarnos, comparamos x con un elemento de la matriz. Algoritmos de ordenación 2 Búsqueda binaria (IV) Sea k = n / 2 Si x <= T[k], buscamos en T[..k] Si x > T[k], buscamos en T[k+..n] Antes de comenzar a buscar, conviene comprobar que x <= T[n] El algoritmo quedaría: Algoritmos de ordenación 0
Búsqueda binaria (V) 2 4 6 7 0 - -2 0 2 2 26 x <= T[k]? i k j no i k j sí i k j sí ik j no ij i = j : alto Búsqueda de x = 2 Algoritmos de ordenación Búsqueda binaria (VI) En seudocódigo: algoritmo busquedabin(t[..n], x) si n = 0 o x > T[n] entonces devolver n + si no devolver binrec(t[..n], x) algoritmo binrec(t[i..j], x) {Búsqueda binaria de x en la submatriz T[i..j] con la seguridad de que T[i ] < x <= T[j]} si i = j entonces devolver i k (i + j) / 2 si x <= T[k] entonces devolver binrec(t[i..k], x) si no devolver binrec(t[k +..j], x) Algoritmos de ordenación 2
Búsqueda binaria (VII) Sea t(m) el tiempo requerido por una llamada a binrec(t[i..j], x), donde m = j i + Obviamente, el tiempo requerido por una llamada a busquedabin(t[..n], x) es t(n), salvo por una pequeña constante aditiva. t(m) = t(m/2) + g(m), m > y par, y g(m) es O() Utilizando el análisis general con L =, b = 2 y k = 0 t(m) es O(log m) Algoritmos de ordenación Búsqueda binaria (VIII) Versión iterativa: algoritmo biniter(t[..n], x) {búsqueda binaria iterativa de x en la matriz T} si x > T[n] entonces devolver n + i ; j n mientras i < j hacer {T[i ] < x <= T[j]} k (i + j) / 2 si x >= T[k] entonces j k sino i k + devolver i Algoritmos de ordenación 4
Conclusiones Para valores pequeños de N (ej N<6) puede no merecer la pena la búsqueda binaria porque utiliza aproximadamente mismo número de comparaciones que una búsqueda secuencial normal Las últimas iteraciones de una búsqueda binaria progresan lentamente Puede interesar solución híbrida Aplicamos búsqueda binaria que termina cuando el rango es pequeño A partir de ese momento aplicamos búsqueda lineal Algoritmos de ordenación