1. Introducción 2. La clase imagen

Anuncio
P R ÁCTICA 2 (A)
TABLAS DE DISPERSI ÓN
E STRUCTURAS DE DATOS Y ALGORITMOS
FACULTAD DE I NFORM ÁTICA
C URSO 2009-2010
1.
Introducción
Esta práctica tiene como objetivo aprender a implementar y a evaluar tablas de dispersión, ası́ como aprender a evaluar
su rendimiento. En esta práctica se va a trabajar con tablas de dispersión que almacenan imágenes [7] y les asocian una
cadena de caracteres.
2.
La clase imagen
En la primera práctica se introdujo una clase para representar imágenes. Dicha clase contenı́a un conjunto básico de
métodos. En esta segunda práctica vamos a enriquecer dicha clase con algunos métodos adicionales que nos resultarán
útiles. Los métodos que vamos a incorporar son el constructor de copia y además sobrecargaremos el operador de igualdad
para poder comparar dos imágenes. Para sobrecargar el operador de igualdad resulta muy útil tener el sobrecargado los
operadores del igualdad y desigualdad de pı́xeles (class Pixel). De esta manera la clase pı́xel deberá quedar como:
class Pixel {
public:
unsigned char r,g,b;
Pixel(unsigned char r=0, unsigned char g=0, unsigned char b=0);
void to_gray_level();
bool operator==(const Pixel& der) const;
bool operator!=(const Pixel& der) const;
};
mientras que la clase imagen quedará como:
class ImageColor {
public :
ImageColor ();
ImageColor (int width , int height);
ImageColor (std::istream & fich_in);
ImageColor(const ImageColor &der); //Ejercicio 1
˜ImageColor ();
int getWidth () const;
int getHeight () const;
int coord2index (int row , int col) const;
bool read_ppm (std::istream & fich_in);
void write_ppm (std::ostream & fich_out) const;
Pixel& operator ()(int row , int col);
void to_gray_level();
bool operator==(const ImageColor & der) const; //Ejercicio 1
private :
Pixel * data ;
int width , height ;
};
Ejercicio 1. Escribe los métodos constructor de copia y el método de sobrecarga del operador de igualdad de la clase
ImageColor para poder comparar dos imágenes. Escribe un programa que te permita comprobar el buen funcionamiento
1
de los métodos implementados. Para ello puedes tomar como referencia el programa de pasar a gris y la imagen que se
utilizaba en la práctica 1.
3.
Tablas de dispersión
En esta práctica se pretende trabajar con tablas de dispersión para guardar un conjunto de imágenes. En esta tabla,
el método de resolución de colisiones que vamos a utilizar será el de “encadenamiento separado”. Cada imagen de este
conjunto llevará asociada un breve descripción acerca de lo que representa la imagen. Puesto que estamos interesados en
realizar una lectura algo más eficiente de las imágenes, éstas estarán guardadas en formato binario. Por este motivo hemos
ampliado la funcionalidad del método de lectura de la práctica 1. Si bien no se pide que implementes esta nueva función,
sı́ es importante que la consultes y trates de entenderla.
La clase que utilizaremos para representar la tabla de dispersión será como sigue (parte del código puedes encontrarlo
en ∼/asigDSIC/FI/eda/PR2):
#ifndef HASH_TABLE_H
#define HASH_TABLE_H
#include "imagen.h"
class hash_node {
public:
ImageColor * image;
// Puntero a una imagen
char * description;
// Descripción de la imagen
unsigned int hash_value;
// Guardamos el valor calculado por la
// función de dispersión sobre la imagen para ahorra cáculos
hash_node *next;
// Puntero al siguiente nodo
hash_node(ImageColor *img,
// realiza una copia local
char * desc,
// realiza una copia local
unsigned int hash_val,
hash_node *nxt);
˜hash_node();
// libera la copia local de imagen y descricion
};
// define el tipo "puntero a una función de recibe una imagen etc."
typedef unsigned int (* Hash_function_pointer)(ImageColor *image);
// tres ejemplos de funcion que dispersa una imagen en un unsigned int
unsigned int hash_suma(ImageColor * image);
unsigned int hash_division(ImageColor * image);
unsigned int hash_multiplicativa(ImageColor * image);
class hash_table{
hash_node **table;
int tableSize;
int numElem;
// vector de "cubetas" o cabezas de listas
// Talla del vector table
// Número de imagenes insertadas
Hash_function_pointer hash_function; // puntero a la func. dispersion
float rehashing_value;
void rehash();
// Redispersion:
// Si se supera se hace rehashing
// Dobla el numero de cubetas
public:
hash_table(Hash_function_pointer hash_func,
int nslots=1024,
float rehashing_val=3.0);
˜hash_table ();
// hace una copia local de la imagen
void insert(ImageColor *ima, char *desc);
// Devuelve true si el elemento está en el conjunto, en cuyo
// caso deja en desc un puntero a la descripción
bool search (ImageColor *ima, const char* &desc) const;
// Devuelve el numero de elementos del conjunto
int getSize() const { return numElem; }
};
#endif // HASH_TABLE_H
A continuación comentamos algunos detalles de esta clase. Cada cubeta de la tabla de dispersión se implementará como una lista enlazada de nodos del tipo hash_node. Cada nodo de estas listas guarda una imagen. Puesto que el computo
de la función de dispersión puede ser costoso, guardaremos en una variable hash_value dicho valor. Esto nos permitirá ahorrar algunos cálculos, puesto que para determinar si dos imágenes son iguales, sólo las compararemos cuando el
contenido la variable hash_value de ambas sea igual.
En esta implementación hemos proporcionado tres funciones de dispersión distintas que generan un valor numérico a
partir de una imagen (hay que hacer módulo el número de cubetas si se quiere utilizar como ı́ndice de la tabla). La tabla
debe usar la misma función durante toda su utilización, pero puedes elegir una de las tres para compararlas.
En el constructor, además de especificar qué función de dispersión vamos a utilizar (observa que no es posible cambiar dicha función más adelante), podemos indicar el número de cubetas y el factor de carga elegido para realizar una
redispersión.
Ejercicio 2. Implementa el método el método constructor y el método destructor de la clase hash_table. Compı́lalos
junto con el programa ejercicio2 (que encontrarás en ∼/asigDSIC/FI/eda/PR2) y comprueba que no da errores de
ejecución.
Ejercicio 3. Implementa el método insert de la clase hash_table. Compı́lalo junto con el programa ejercicio3
(que encontrarás en ∼/asigDSIC/FI/eda/PR2) y comprueba que no da errores de ejecución.
Ejercicio 4. Implementa el método search de la clase hash_table. Una posible manera de comprobar el correcto
funcionamiento del programa implementado es escribir un programa que después de cargar un conjunto de imágenes,
busque en la tabla una imagen guardada en un fichero y escriba su descripción. Escribe dicho programa de acuerdo con la
sintaxis:
ejercicio4 fichero_indice_de_imagenes fichero_con_imagen_a_buscar
Como ı́ndice puedes utilizar el fichero que encontrarás en ∼/asigDSIC/FI/eda/DATOS/IMAGENES/indice. En este fichero,
cada lı́nea contiene el nombre de un fichero que contiene una imagen y su descripción. Es conveniente que no copies las
imágenes en tu HOME puesto que no tendrás suficiente espacio. Cópialas en el directorio /tmp o, mejor aun, no copies
las imágenes y úsalas desde la ruta original.
Cuando insertamos un elemento que no estaba en la tabla, se incrementa en uno el número de elementos. En ese
momento se recalcula el factor de carga (número de elementos dividido por número de cubetas). Si el nuevo factor de
carga supera el lı́mite especificado, debemos doblar el número de cubetas y redispersar los nodos de todas las listas
enlazadas.
Ejercicio 5. Implementa el método de insertar, incluyendo el método de redispersión. Comprueba su correcto funcionamiento.
4.
Estudio del comportamiento de las tablas de dispersión
Para estudiar el comportamiento de las tablas de dispersión es muy útil sacar estadı́sticas sobre la forma en que las
funciones consiguen repartir los elementos entre las cubetas.
Ejercicio 6. Implementa los métodos siguientes en la clase hash_table:
double get_load_factor() const;
double get_standard_deviation() const;
void get_histogram(ostream& fich) const;
El método histogram recibe un fichero abierto en modo escritura (por ejemplo, la salida estándar) y escribe por dicho
fichero el histograma de ocupación como una secuencia de lı́neas con dos valores:
longitud_lista número_cubetas
...
Este formato permite visualizar el histograma de ocupación con la herramienta gnuplot usando el comando plot:
gnuplot> plot ’histograma.txt’ using 1:2 with boxes
Ejercicio 7. Se propone usar el programa implementado para estudiar las propiedades de las tres funciones de dispersión
descritas. Para cada función hay que obtener el correspondiente histograma de ocupación de cubetas para diferentes
números de cubetas.
5.
Ejercicios adicionales
Ejercicio. Sobrecarga el operador de asignación en la clase ImageColor.
Ejercicio. Las funciones de dispersión estudiadas en esta práctica hacen un cálculo a partir de todos los pı́xeles de la
imagen. Define nuevas funciones de dispersión que utilicen sólo una parte de los pı́xeles (la mitad, los que están en
posiciones pares, etc). Estudia el comportamiento de la tabla de dispersión con las nuevas funciones.
Ejercicio. Sobrecarga el operador de igualdad para la clase hash_table de tal forma que devuelva true si dos tablas
son iguales y false en caso contrario. La dos tablas pueden tener un número de cubetas y una función de dispersión
diferente.
Ejercicio. Escribe un constructor de copia en la clase hash_table que reproduzca la tabla.
Ejercicio. Escribe una clase para trabajar con tablas de dispersión que almacenen cadenas de caracteres. Define una
función de dispersión apropiada. Estudia el comportamiento de la tabla de dispersión implementada con los datos que
puedes encontrar en ∼/asigDSIC/FI/eda/DATOS/datos palabras.gz (el fichero descomprimido ocupa 85 MB).
Referencias
[1] D.E. Knuth “Sorting and Searching”, vol. 3, “The Art of Computer Programming”, Addison-Wesley, 1973.
[2] T. Cormen, Ch. Leiserson, R. Rivest, “Introduction to Algorithms”, MIT, 1990.
[3] B.J. McKenzie, R. Harries, T. Bell “Selecting a Hashing Algorithm”, Software–Practice and Experience, Vol. 20(2), pp. 209–224,
1990.
[4] G.H. Gonnet, R. Baeza-Yates, “Handbook of algorithms and data structures: in Pascal and C”, Addison-Wesley, 2nd ed., 1991.
[5] M.A. Weiss, “Estructuras de datos y algoritmos”, Addison-Wesley, 1995.
[6] G.L. Heileman “Data structures, algorithms and object-oriented programming”, McGraw-Hill, 1996.
[7] J. M. Geusebroek, G. J. Burghouts, and A. W. M. Smeulders, “The Amsterdam library of object images”, Int. J. Comput. Vision,
61(1), 103-112, January, 2005
Descargar