• Nenhum resultado encontrado

Tabelas de Dispersão (hash tabels)

N/A
N/A
Protected

Academic year: 2022

Share "Tabelas de Dispersão (hash tabels)"

Copied!
24
0
0

Texto

(1)

Estruturas de Dados Avançadas

23/03/16 © 2010 DI, PUC-Rio • Estruturas de Dados Avançadas • 2010.1 1

INF1010-3WB

Tabelas de Dispersão (hash tabels)

23/03/16 (c) Dept. Informática - PUC-Rio 2

• Busca (acesso) com vetor é muito eficiente…

Motivação

Informação 0

Informação 1 Informação 2

Informação 3

Informação (n-1) Info* v[N];

...

v[i]->...

(2)

23/03/16 (c) Dept. Informática - PUC-Rio 3

Nem sempre temos chaves numéricas pequenas…

struct aluno { int mat;

char nome[81];

char email[41];

char turma;

};

typedef struct aluno Aluno;

Como fazer para buscar pelo nome ou pela matrícula?

Idéia dos escaninhos

A primeira letra do nome define a caixa onde a informação é depositada…

) (

] , 0 [ :

x h x

N Info

Hash

(3)

23/03/16 (c) Dept. Informática - PUC-Rio 5

Tabelas de Dispersão (Hash Tables)

• utilizadas para buscar um elemento em ordem constante O(1)

• necessitam de mais memória, proporcional ao número de elementos armazenado

• ex.: {14,16,58,39,44,10,72,22}, chave kÎ[0,99]

• 8 elementos ÞN=13

12 11 10 9 8 7 6 5 4 3 2 1 0

22 - 72 58 44 - 16 - 14

39 10 - -

h [0,99]

h(k) = (k % 13)

h: [0,99] ®[0,12]

23/03/16 (c) Dept. Informática - PUC-Rio 6

Tabelas de Dispersão (Hash Tables)

• utilizadas para buscar um elemento em ordem constante O(1)

• necessitam de mais memória, proporcional ao número de elementos armazenado

• ex.: {14,16,58,39,44,10,72,22}, chave kÎ[0,99]

• 8 elementos ÞN=20

h [0,99]

h(k) = (k % 20)

h: [0,99] ®[0,12]

0 1 2 22 3 4 5 6 7 8 9 10 11 12 13 1414,44 15 16 16 17 18 58 19 39

(4)

23/03/16 (c) Dept. Informática - PUC-Rio 7

Função de dispersão (função de hash)

• mapeia uma chave de busca em um índice da tabela

• deve apresentar as seguintes propriedades:

– ser eficientemente avaliada (para acesso rápido)

– espalhar bem as chaves de busca (para minimizarmos colisões)

• colisão = duas ou mais chaves de busca são mapeadas para um mesmo índice da tabela de hash

h [0,99]

h(k) = (k % 13)

h: [0,99] ®[0,12]

Dimensão da tabela

• deve ser escolhida para diminuir o número de colisões

• costuma ser um valor primo

• a taxa de ocupação não deve ser muito alta:

– a taxa de ocupação não deve ser superior a 75%

– uma taxa de 50% em geral traz bons resultados

– uma taxa menor que 25% pode representar um gasto excessivo de memória

Fator de carga = (#entradas/tamanho)

(5)

23/03/16 (c) Dept. Informática - PUC-Rio 9

Exemplo de Tabelas de Dispersão

• tipo Aluno: define o tipo da estrutura de dados de interesse

• tipo Hash: define o tipo dos vetores de ponteiros para Aluno

struct aluno { int mat;

char nome[81];

char email[41];

char turma;

};

typedef struct aluno Aluno;

#define N 127

typedef Aluno* Hash[N];

int hash (int mat) {

return (mat%N);

}

Hashing Perfeito

n

Característica:

n

Para quaisquer chaves x e y diferentes e

pertencentes a A, a função utilizada fornece

saídas diferentes;

(6)

Exemplo de Hashing Perfeito

(1/6)

n Suponha que queiramos armazenar os dados referentes aos alunos de umadeterminada turmaque ingressaram em umcurso específico.

n Cada aluno é identificado unicamente pela sua matrícula.

n Na PUC-Rio, o número de matrícula dos alunos é dado por uma seqüência de8 dígitos.

struct aluno {

int mat; // 4 bytes = 4 bytes char nome[81]; // 1 byte * 81 = 81 bytes char email[41]; // 1 byte * 41 = 41 bytes }; Total = 126 bytes typedef struct aluno Aluno;

Exemplo de Hashing Perfeito

(2/6)

n O número de dígitosefetivos na matrícula são7, pois o último dígito é o deverificação (oucontrole).

n Para permitir um acesso a qualquer aluno em ordem constante, podemos usar o número de matrícula do aluno como índice de um vetor.

n Um problema é que pagamos um preço alto para ter esse acesso rápido. Porque !?

Resp:Visto que a matrícula é composta de 7 dígitos, então podemos esperar uma matrícula variando de0000000a9999999. Portanto, precisaríamos dimensionar um vetor comDEZ Milhõesde elementos.

(7)

Exemplo de Hashing Perfeito

(3/6)

n Como a estrutura de cada aluno ocupa 126 bytes, então seriam necessários reservar 1.260.000.000 bytes, ou seja, acima de1 GByte de memória.

n Como na prática teremos em torno de 50 alunos em uma turma cadastrados no curso, precisaríamos na realidade de 7.300 (=126*50) bytes.

Pergunta:Como amenizar o gasto excessivo de memória!?

Resp:Utilizando um vetor de ponteiros, em vez de um vetor de estruturas.

n Porque melhor vetor de ponteiros ?

n Alocação dos alunos cadastrados seria realizada dinamicamente.

n Portanto, considerando que cada ponteiro ocupa 4 bytes, o gasto excedente de memória seria40.000.000 bytes. (~= 38 MBytes)

Exemplo de Hashing Perfeito

(4/6)

n Apesar da diminuição do gasto de memória, este gasto é considerável e desnecessário.

n A forma de resolver o problema de gasto excessivo de memória, garantindo um acesso rápido, é através de Hashing.

Resp: Identificando as partes significativasda chave.

Pergunta: Como construir a Tabela Hashpara armazenar as informações dos alunos de uma determinada turma de um curso específico?

Analisando a chave: Desconsiderando o último dígito (controle), temos outros dígitos com significados especiais:

97 1 12 34 - 4

ano de ingresso período de ingresso código do curso chamada do aluno

(8)

Exemplo de Hashing Perfeito

(5/6)

n

Portanto, podemos considerar no cálculo de endereço parte do número da matrícula.

n

Esta parte mostra a dimensão que a Tabela Hash deverá ter.

n

Por exemplo, dimensionando com apenas 100 elementos, ou seja, Aluno* tabAlunos[100].

Pergunta:Qual a função que aplicada sobre matrículas de alunos da PUC-Rio retorna os índices da tabela?

Resp: Depende qual é a turma e o curso específico dos alunos que devem ser armazenados ?

Exemplo de Hashing Perfeito

(6/6)

n

Supondo que a turma seja do 2º semestre de 2005 (código 052) e do curso de Sistemas de Informação (código 35).

Qual seria a função de hashing perfeito !?

intfuncao_hash(intmatricula) { intvalor = matricula –0523500;

if((valor >= 0) & (valor <=99))then returnvalor;

else

return-1;

}

Acesso: dada mat à tabAlunos[funcao_hash(mat)]

(9)

23/03/16 (c) Dept. Informática - PUC-Rio 17

Estratégias para tratamento de colisão

• uso da primeira posição consecutiva livre(encadeamento interior):

– simples de implementar

– tende a concentrar os lugares ocupados na tabela

• uso de uma segunda função de dispersão (encadeamento aberto):

– evita a concentração de posições ocupadas na tabela – usa uma segunda função de dispersão para re-posicionar o

elemento

• uso de listas encadeadas(encadeamento exterior):

– simples de implementar

– cada elemento da tabela hash representa um ponteiro para uma lista encadeada

23/03/16 (c) Dept. Informática - PUC-Rio 18

Tratamento de Colisão

• Uso da posição consecutiva livre:

– estratégia geral:

• armazene os elementos que colidem em outros índices, ainda não ocupados, da própria tabela

– estratégias particulares:

• diferem na escolha da posição ainda não ocupada para armazenar um elemento que colide

(10)

23/03/16 (c) Dept. Informática - PUC-Rio 19

Uso da posição consecutiva livre

• Estratégia 1:

– se a função de dispersão mapeia a chave de busca

para um índice já ocupado, procure o próximo índice livre da tabela (usando incremento circular) para armazenar o novo elemento

x h(x) busca posição livre

Operação de busca

• suponha que uma chave x for mapeada pela função de hash h para um determinado índice h(x)

• procure a ocorrência do elemento a partir de h(x), até que o elemento seja encontrado ou

que uma posição vazia seja encontrada

(11)

23/03/16 (c) Dept. Informática - PUC-Rio 21

Operação de busca

• entrada: a tabela e a chave de busca

• saída: o ponteiro do elemento, se encontrado NULL, se o elemento não for encontrado

Aluno* hsh_busca (Hash tab, int mat) {

int h = hash(mat);

while (tab[h] != NULL) { if (tab[h]->mat == mat)

return tab[h];

h = (h+1) % N;

}

return NULL;

}

23/03/16 (c) Dept. Informática - PUC-Rio 22

Operação de inserção e modificação

• suponha que uma chave x for mapeada pela função de hash h para um determinado índice h(x)

• procure a ocorrência do elemento a partir de h(x), até que o elemento seja encontrado ou

que uma posição vazia seja encontrada

• se o elemento existir, modifique o seu conteúdo

• se não existir, insira um novo na primeira posição livre

que encontrar na tabela, a partir do índice mapeado

(12)

23/03/16 (c) Dept. Informática - PUC-Rio 23

Inserção/modificação

Aluno* hsh_insere(Hash tab,int mat, char* n, char* e, char t) {

int h = hash(mat);

while (tab[h] != NULL) { if (tab[h]->mat == mat)

break;

h = (h+1) % N;

}

if (tab[h]==NULL) { /* não encontrou o elemento */

tab[h] = (Aluno*) malloc(sizeof(Aluno));

tab[h]->mat = mat;

}

/* atribui/modifica informação */

strcpy(tab[h]->nome,n);

strcpy(tab[h]->email,e);

tab[h]->turma = t;

return tab[h];

}

Tratamento de Colisão

• Uso de uma segunda função de dispersão:

– exemplo:

• primeira função de hash:

• segunda função de hash:

onde xrepresenta a chave de busca e Na dimensão da tabela

– se houver colisão, procure uma posição livre na tabela com incrementos dados por h’(x)

• em lugar de tentar

(h(x)+1)%N

, tente

(h(x)+h’(x))%N

)

2

%(

2 )

('x =N− −x Nh

N x x h( )= %

(13)

23/03/16 (c) Dept. Informática - PUC-Rio 25

Uso de uma segunda função de dispersão (cont)

• cuidados na escolha da segunda função de dispersão:

– nunca pode retornar zero

• pois isso não faria com que o índice fosse incrementado – não deve retornar um número divisor da dimensão da tabela

• pois isso limitaria a procura de uma posição livre a um sub-conjunto restrito dos índices da tabela

• se a dimensão da tabela for um número primo, garante-se automaticamente que o resultado da função não será um divisor

23/03/16 (c) Dept. Informática - PUC-Rio 26

Busca com segunda função de colisão

static int hash2 (int mat) {

return N - 2 - mat%(N-2);

}

Aluno* hsh_busca (Hash tab, int mat) {

int h = hash(mat);

int h2 = hash2(mat);

while (tab[h] != NULL) { if (tab[h]->mat == mat)

return tab[h];

h = (h+h2) % N;

}

return NULL;

}

(14)

23/03/16 (c) Dept. Informática - PUC-Rio 27

Tratamento de Colisão

• Uso de listas encadeadas

– cada elemento da tabela hash representa um ponteiro para uma lista encadeada

• todos os elementos mapeados para um mesmo índice são armazenados na lista encadeada

• os índices da tabela que não têm elementos associados representam listas vazias

x h(x)

Tratamento de Colisão

• Exemplo:

– cada elemento armazenado na tabela será um elemento de uma lista encadeada

– a estrutura da informação deve prever um ponteiro adicional para o próximo elemento da lista

struct aluno { int mat;

char nome[81];

char turma;

char email[41];

struct aluno* prox; /* encadeamento na lista de colisão */

};

typedef struct aluno Aluno;

(15)

23/03/16 (c) Dept. Informática - PUC-Rio 29 /* função para inserir ou modificar um determinado elemento */

Aluno* hsh_insere (Hash tab, int mat, char* n, char* e, char t) {

int h = hash(mat);

Aluno* a = tab[h];

while (a != NULL) { if (a->mat == mat)

break;

a = a->prox;

}

if (a==NULL) { /* não encontrou o elemento */

/* insere novo elemento no início da lista */

a = (Aluno*) malloc(sizeof(Aluno));

a->mat = mat;

a->prox = tab[h];

tab[h] = a;

}

/* atribui ou modifica informação */

strcpy(a->nome,n);

strcpy(a->email,e);

a->turma = t;

return a;

}

23/03/16 (c) Dept. Informática - PUC-Rio 30

Exemplo: número de ocorrências de palavras

• Especificação:

– programa para exibir quantas vezes cada palavra ocorre em um dado texto

• entrada: um texto T

• saída: uma lista de palavras, em ordem decrescente do número de vezes que cada palavra ocorre em T

– observações:

• uma palavra é uma seqüência de uma ou mais letras (maiúsculas ou minúsculas)

• por simplicidade, caracteres acentuados não são considerados

(16)

23/03/16 (c) Dept. Informática - PUC-Rio 31

Exemplo: número de ocorrências de palavras

• Armazenamento das palavras lidas e da sua freqüência:

– tabela de dispersão

– usa a própria palavra como chave de busca

• Função para obter a freqüência das palavras:

– dada uma palavra, tente encontrá-la na tabela – se não existir, armazene a palavra na tabela

– se existir, incremente o número de ocorrências da palavra

• Função para exibir as ocorrências em ordem decrescente:

– crie um vetor armazenando as palavras da tabela de dispersão – ordene o vetor

– exiba o conteúdo do vetor

Exemplo: número de ocorrências de palavras

• Tabela de dispersão:

– usa a lista encadeada para o tratamento de colisões

#define NPAL 64 /* dimensão máxima de cada palavra */

#define NTAB 127 /* dimensão da tabela de dispersão */

/* tipo que representa cada palavra */

struct palavra { char pal[NPAL];

int n; /* contador de ocorrências */

struct palavra* prox; /* colisão com listas */

};

typedef struct palavra Palavra;

/* tipo que representa a tabela de dispersão */

typedef Palavra* Hash[NTAB];

(17)

23/03/16 (c) Dept. Informática - PUC-Rio 33

Exemplo: número de ocorrências de palavras

• Leitura de palavras:

– captura a próxima seqüência de letras do arquivo texto – entrada: ponteiro para o arquivo de entrada

cadeia de caracteres armazenando a palavra capturada – saída: inteiro, indicando leitura bem sucedida (1) ou não (0) – processamento:

• capture uma palavra, pulando os caracteres que não são letras e armazenando a seqüência de letras a partir da posição do cursor do arquivo

• para identificar se um caractere é letra ou não,

use a função isalpha disponibilizada pela interface ctype.h

23/03/16 (c) Dept. Informática - PUC-Rio 34

static int le_palavra (FILE* fp, char* s) {

int i = 0;

int c;

/* pula caracteres que não são letras */

while ((c = fgetc(fp)) != EOF) { if (isalpha(c))

break;

}

if (c == EOF) return 0;

else

s[i++] = c /* primeira letra já foi capturada */

/* lê os próximos caracteres que são letras */

while ( i<NPAL-1 && (c = fgetc(fp)) != EOF && isalpha(c)) s[i++] = c;

s[i] = '\0';

return 1;

}

(18)

23/03/16 (c) Dept. Informática - PUC-Rio 35

Exemplo: número de ocorrências de palavras

• Função para inicializar a tabela:

– atribui NULL a cada elemento

static void inicializa (Hash tab) {

int i;

for (i=0; i<NTAB; i++) tab[i] = NULL;

}

Exemplo: número de ocorrências de palavras

• Função de dispersão:

– mapeia a chave de busca (uma cadeia de caracteres) em um índice da tabela

– soma os códigos dos caracteres que compõem a cadeia e tira o módulo dessa soma para se obter o índice da tabela static int hash (char* s)

{

int i;

int total = 0;

for (i=0; s[i]!='\0'; i++) total += s[i];

return total % NTAB;

}

(19)

23/03/16 (c) Dept. Informática - PUC-Rio 37

Exemplo: número de ocorrências de palavras

• Função para acessar os elementos na tabela:

– entrada: uma palavra (chave de busca)

– saída: o ponteiro da estrutura Palavra associada – processamento:

• se a palavra ainda não existir na tabela, crie uma nova palavra e

forneça como retorno essa nova palavra criada

23/03/16 (c) Dept. Informática - PUC-Rio 38

static Palavra *acessa (Hash tab, char* s) {

Palavra* p;

int h = hash(s);

for (p=tab[h]; p!=NULL; p=p->prox) { if (strcmp(p->pal,s) == 0)

return p;

}

/* insere nova palavra no inicio da lista */

p = (Palavra*) malloc(sizeof(Palavra));

strcpy(p->pal,s);

p->n = 0;

p->prox = tab[h];

tab[h] = p;

return p;

}

(20)

23/03/16 (c) Dept. Informática - PUC-Rio 39

Exemplo: número de ocorrências de palavras

• Trecho da função principal:

– acessa cada palavra

– incrementa o seu número de ocorrências

...

inicializa(tab);

while (le_palavra(fp,s)) { Palavra* p = acessa(tab,s);

p->n++;

} ...

Exemplo: número de ocorrências de palavras

• Exibição do resultado ordenado:

– crie dinamicamente um vetor para armazenar as palavras

• vetor de ponteiros para a estrutura Palavra

• tamanho do vetor = número de palavras armazenadas na tabela – coloque o vetor em ordem decrescente do número de

ocorrências de cada palavra

• se duas palavras tiverem o mesmo número de ocorrências, use a ordem alfabética como critério de desempate

(21)

23/03/16 (c) Dept. Informática - PUC-Rio 41

/*

função para percorrer a tabela e contar o número de palavras

entrada: tabela de dispersão

*/

static int conta_elems (Hash tab) {

int i;

Palavra* p;

int total = 0;

for (i=0; i<NTAB; i++) {

for (p=tab[i]; p!=NULL; p=p->prox) total++;

}

return total;

}

23/03/16 (c) Dept. Informática - PUC-Rio 42

/*

função para criar dinamicamente o vetor de ponteiros entrada: número de elementos

tabela de dispersão

*/

static Palavra** cria_vetor (int n, Hash tab) {

int i, j=0;

Palavra* p;

Palavra** vet = (Palavra**) malloc(n*sizeof(Palavra*));

/* percorre tabela preenchendo vetor */

for (i=0; i<NTAB; i++) {

for (p=tab[i]; p!=NULL; p=p->prox) vet[j++] = p;

}

return vet;

}

(22)

23/03/16 (c) Dept. Informática - PUC-Rio 43

Exemplo: número de ocorrências de palavras

• Ordenação do vetor (de ponteiros para Palavra):

– utilize a função qsortda biblioteca padrão

– defina a função de comparação apropriadamente

static int compara (const void* v1, const void* v2) {

Palavra** p1 = (Palavra**)v1;

Palavra** p2 = (Palavra**)v2;

if ((*p1)->n > (*p2)->n) return -1;

else if ((*p1)->n < (*p2)->n) return 1;

else return strcmp((*p1)->pal,(*p2)->pal);

}

Exemplo: número de ocorrências de palavras

• Impressão da tabela:

static void imprime (Hash tab) {

int i;

int n;

Palavra** vet;

/* cria e ordena vetor */

n = conta_elems(tab);

vet = cria_vetor(n,tab);

qsort(vet,n,sizeof(Palavra*),compara);

/* imprime ocorrências */

for (i=0; i<n; i++)

printf("%s = %d\n",vet[i]->pal,vet[i]->n);

/* libera vetor */

free(vet);

}

(23)

23/03/16 (c) Dept. Informática - PUC-Rio 45

Exemplo: número de ocorrências de palavras

• Função Principal:

#include <stdio.h>

#include <string.h>

#include <ctype.h>

#include <stdlib.h>

... /* funções auxiliares mostradas acima */

23/03/16 (c) Dept. Informática - PUC-Rio 46

int main (int argc, char** argv) {

FILE* fp;

Hash tab;

char s[NPAL];

if (argc != 2) {

printf("Arquivo de entrada nao fornecido.\n");

return 0; }

/* abre arquivo para leitura */

fp = fopen(argv[1],"rt");

if (fp == NULL) {

printf("Erro na abertura do arquivo.\n");

return 0; }

/* conta ocorrência das palavras */

inicializa(tab);

while (le_palavra(fp,s)) { Palavra* p = acessa(tab,s);

p->n++; }

/* imprime ordenado */

imprime (tab);

return 0;

}

(24)

23/03/16 (c) Dept. Informática - PUC-Rio 47

Resumo

• Tabelas de dispersão (hash tables):

– utilizadas para buscar um elemento em ordem constante O(1)

• Função de dispersão (função de hash):

– mapeia uma chave de busca em um índice da tabela

• Estratégias para tratamento de colisão:

– uso da primeira posição consecutiva livre – uso de uma segunda função de dispersão – uso de listas encadeadas

0 1 N

39 14 - 16 - 44 58

h k

h(k)

Referências

Waldemar Celes, Renato Cerqueira, José Lucas Rangel, Introdução a Estruturas de Dados, Editora Campus (2004)

Capítulo 18 – Tabelas de dispersão

Referências

Documentos relacionados

No mundo contemporâneo, a questão das migrações forçadas, embora não seja nova, constitui grave problema para diversos Estados e para as pessoas que participam desses fluxos,

As doações são feitas através de recursos financeiros, convertidos em produtos, presentes e benefícios para as crianças, ou através de doações de roupas,

Jovina - Cotia - SP Tiragem: 10.000 exemplares - ATENÇÃO Os artigos publicados neste jornal são de inteira responsabilidade dos autores e não refletem necessariamente a opinião

Mas como todos os seres humanos têm esses sonhos perversos, incestuosos e assassinos, e não apenas os neuróticos, podemos chegar à conclusão de que também os que hoje

Parágrafo terceiro – Caso a proposta seja aprovada, os artigos em língua estrangeira (exceto em espanhol) deverão ser traduzidos para o português, observando-se o

É o que abordo na segunda metade da primeira parte do trabalho, em que procuro lançar luzes sobre como a transição para o chamado Estado Regulador impactou

A pesquisa foi realizada com os educadores e gestores de escolas municipais e estaduais da cidade de Vilhena-RO, tendo como objetivo compreender a importância

apela para a realização de iniciativas que promovam informação integrada aos viajantes e bilhética intermodal; solicita a aplicação de medidas que visem melhorar a qualidade