Programación (PRG) PRÁCTICA 10. Algoritmos de búsqueda Facultad de Informática Departamento de Sistemas Informáticos y Computación Universidad Politécnica de Valencia Curso 2002/2003 1. Introducción El objetivo de esta práctica es estudiar el comportamiento de dos algoritmos de búsqueda de elementos dentro de vectores. Los algoritmos a estudiar son ya conocidos por el alumno, siendo uno la búsqueda secuencial y otro la binaria. Esta práctica trata de medir el trabajo realizado por cada uno de los algoritmos utilizando una medida a posteriori, es decir, midiendo el trabajo que ha realizado el ordenador una vez resuelto el problema. El alumno deberá relacionar los resultados obtenidos con los resultados esperados, derivados del estudio a priori (es decir, a partir del código fuente) del algoritmo visto en clase. 2. Búsqueda secuencial Este es el algoritmo básico de búsqueda de elementos dentro de un vector. Empieza a buscar el elemento en la primera posición y, si no lo encuentra, continúa en la siguiente posición, y ası́ hasta encontrarlo. Los algoritmos de búsqueda pueden terminar de dos maneras distintas: con éxito, si encuentran el elemento, o sin éxito, si el elemento no estaba en el vector. A continuación se muestra el algoritmo básico de búsqueda secuencial en vectores de enteros: int bs(int x, int V[], int i, int j) { while (i <= j && x!=V[i]) i++; /* i<j */ if (i<=j) return i; /* Encontrado */ else return -1; /* No encontrado */ } 1 Prácticas PRG. Facultad de Informática DSIC Curso 2002/2003 PRÁCTICA 10. Algoritmos de búsqueda Los parámetros de la función anterior son: x: El elemento a buscar. V: El vector de enteros donde se realizará la búsqueda. i: Posición inicial de la búsqueda. j: Posición final de la búsqueda. La función devuelve la posición del vector donde se ha encontrado el elemento, o bien -1 si no lo ha encontrado. El algoritmo anterior se puede mejorar de varias formas [1]: Mediante un centinela. Se sitúa el valor que se está buscando al final del vector, de manera que se asegura el éxito de la búsqueda. Ası́, la comprobación de llegada al final del vector dentro del bucle se puede eliminar. El bucle anterior quedarı́a: while (x!=V[i]) i++; Si el vector está ordenado. Entonces, cuando se encuentra un valor mayor que el buscado, el algoritmo puede terminar, ya que el elemento buscado no está en el vector. Esta modificación mejora la velocidad del algoritmo en el caso de que el elemento no esté en el vector. Si se sabe a priori la frecuencia de acceso a los elementos. Entonces se podrı́an colocar los elementos más frecuentemente buscados al principio del vector. Este orden aumenta la velocidad de búsqueda en caso de éxito. Estas aceleraciones necesitan que el vector se pueda modificar. Cuando el vector está implementado de tal forma que no se pueda modificar y no se tiene ninguna información adicional sobre la disposición de sus elementos (por ejemplo, que estén ordenados), habrá que utilizar el algoritmo básico. 3. Búsqueda binaria Este algoritmo necesita que el vector esté ordenado. El método de búsqueda utilizado por este algoritmo consiste en estudiar el elemento central del vector. En función de que sea mayor o menor que el elemento buscado, se puede restringir la búsqueda a una mitad del vector. Después, se repite el mismo proceso con la mitad seleccionada. De esta forma, o bien se encuentra el elemento buscado en una de las posiciones medias que se han estudiado (salida con éxito), o bien el vector a estudiar se queda sin elementos (salida sin éxito). Página 2 de 6 Prácticas PRG. Facultad de Informática DSIC Curso 2002/2003 PRÁCTICA 10. Algoritmos de búsqueda A continuación se muestra una implementación en C de dicho algoritmo vista en clase: int bb(int x, int V[], int i, int j) { int k; while (i < j) { k = (i+j)/2; if (x<V[k]) j = k-1; else if (x>V[k]) i = k+1; else return k; } if (x!=V[i]) return -1; else return i; } /* i<=k<j */ /* x<V[k] */ /* x>V[k] */ /* x==V[k] */ /*i==j*/ /* caso- */ /* -base */ Los argumentos y la salida de la función de búsqueda binaria son los mismos que los de la búsqueda secuencial. 4. Estudio del coste temporal de un algoritmo En la Práctica 6 ya se estudió la forma de calcular experimentalmente el coste de los algoritmos. En este apartado se recordará cómo medir el coste temporal de un segmento de un programa, es decir, calcular el tiempo que ha tardado en ejecutarse dicho segmento. Para medir tiempos, se puede utilizar la función clock del lenguaje C, que se encuentra definida en el fichero de cabecera time.h. La cabecera de la función es: clock_t clock(void); La función clock devuelve una aproximación del tiempo de procesador consumido por el programa en ejecución. El valor que devuelve es de tipo entero, e indica la cantidad de clocks que ha pasado desde el inicio del programa. Para convertir este valor en segundos, hay que dividirlo por la constante CLOCKS_PER_SEC. Por ello, si se desea medir la cantidad de segundos que ha consumido un conjunto de instrucciones del programa, se deberı́a utilizar una construcción como la que sigue: int t0,t1; double total_secs; [...] t0=clock(); /* Segmento de código a medir */ t1=clock(); total_secs=((double)(t1-t0))/CLOCKS_PER_SEC; El segmento de código a medir puede ser un conjunto de instrucciones, Página 3 de 6 Prácticas PRG. Facultad de Informática DSIC Curso 2002/2003 PRÁCTICA 10. Algoritmos de búsqueda una llamada a función o, en general, un segmento contiguo del programa que es el que realiza el trabajo cuyo coste se desea medir. ¡Cuidado!. Asegúrate de medir únicamente lo que te interesa medir. Es un error común incluir dentro de la medida de tiempo lı́neas de código que no pertenecen al algoritmo que se debe medir. Por ejemplo, si se desea medir el coste de ordenar un vector dado, el siguiente ejemplo muestra cómo no se debe realizar la medida: t0=clock(); printf("El tiempo total es: "); ordena_vector(v,0,tam); t1=clock(); printf("%12.8lf segs\n",((double)(t1-t0))/CLOCKS_PER_SEC); Tiempos 0. Si, en algún experimento de medida de tiempos obtienes como resultado “0” segundos, el experimento no será válido. No hay ningún ordenador que pueda hacer un trabajo, por pequeño que sea, en tiempo cero. El problema es la precisión del reloj, es decir, las unidades de la función clock son demasiado grandes para medir el tiempo de ejecución del algoritmo. En este caso, lo que hay que hacer es ejecutar un número de veces (n) el algoritmo cuyo coste se desea medir, hasta que la suma de los tiempos totales sea medible con cierta precisión por la función clock. Posteriormente, para calcular el tiempo que ha tardado una ejecución del algoritmo, tan sólo habrá que dividir el tiempo total de ejecución por n. 5. Ejercicios propuestos 1. Estudia el caso peor de los algoritmos de búsqueda secuencial y binaria. En ambos algoritmos, el caso peor aparece cuando el elemento buscado no está en el vector. Para realizar el estudio temporal, se deberá medir el tiempo que tarda cada algoritmo en buscar un elemento que no está en el vector para tallas 2000, 4000, 6000 . . . 20000. Para ello, define e inicializa un vector del tamaño máximo, y para cada talla n utiliza únicamente los primeros n elementos. Puedes utilizar la plantilla que se encuentra en el fichero: /misc/practicas/asignaturas/prgfi/p10/midebusq.c La salida del programa deberá ser tres columnas, parecidas a las siguientes, en las que se indica la talla del vector, el tiempo en segunPágina 4 de 6 Prácticas PRG. Facultad de Informática DSIC Curso 2002/2003 PRÁCTICA 10. Algoritmos de búsqueda dos que se ha tardado en buscar un elemento en un vector mediante búsqueda secuencial y búsqueda binaria, respectivamente. # Talla 2000 4000 6000 8000 10000 12000 14000 16000 18000 20000 Tiempo bs 0.0000260000 0.0000500000 0.0000742000 0.0001042000 0.0001242000 0.0001482000 0.0001742000 0.0002004000 0.0002224000 0.0002482000 Tiempo bb 0.0000002900 0.0000003010 0.0000003400 0.0000003400 0.0000003610 0.0000003600 0.0000003610 0.0000003600 0.0000003800 0.0000003810 Posteriormente, utiliza el programa gnuplot para mostrar (comando plot) las dos gráficas correspondientes a los puntos obtenidos (X=talla, Y=tiempo), y ajusta (comando fit) los puntos anteriores mediante las funciones que definen el coste teórico de los algoritmos. 2. Modifica el programa anterior para que, además de contar tiempo, cuente comparaciones. El resultado del programa tendrı́a 5 columnas (talla, tiempo bs, comparaciones bs, tiempo bb, comparaciones bb). ¿Existe alguna relación entre los resultados obtenidos para el tiempo de búsqueda y los obtenidos para las comparaciones efectuadas por cada algoritmo?. 3. Dadas las funciones obtenidas por el ajuste por mı́nimos cuadrados sobre los puntos experimentales, predecir el comportamiento temporal de cada algoritmo de búsqueda en el caso peor, para las tallas mostradas en la siguiente tabla: Talla del vector 50000 100000 500000 1000000 4. Tiempo bs Tiempo bb Implementa alguna de las aceleraciones del algoritmo de búsqueda secuencial mostradas en el Apartado 2 y estudia la aceleración obtenida, comparando el algoritmo original con el modificado, siguiendo los pasos del Ejercicio 1. Página 5 de 6 Prácticas PRG. Facultad de Informática DSIC Curso 2002/2003 PRÁCTICA 10. Algoritmos de búsqueda Referencias [1] D.E. Knuth El arte de programar ordenadores. Vol. 3. Clasificación y búsqueda. Ed. Reverté. 1987. Página 6 de 6