Motivao Dada uma tabela com uma chave e vrios valores por linha, quero rapidamente procurar, inserir e apagar registros baseados nas suas chaves Estruturas de busca sequencial/binria levam tempo at encontrar o elemento desejado. Ex: Arrays e listas Ex: rvores 5 2 6 1 7 8 4 9 5 2 6 1 4 8 2 6 4 1 9 Motivao Em algumas aplicaes, necessrio obter o valor com poucas comparaes, logo, preciso saber a posio em que o elemento se encontra, sem precisar varrer todas as chaves. A estrutura com tal propriedade chamada de tabela hash. 64 11 20 7 0 1 2 3 4 5 6 7 20 ? 20 mod 8 = 4 20 45 ? 45 mod 8 = 5 11 ? 11 mod 8 = 3 11 Funes Hashing Os registros com as chaves so armazenados nessa tabela, e endereados a partir de uma funo de transformao sobre a chave de pesquisa A essa funo d-se o nome de Funo HASHING Seja M o tamanho da tabela: A funo de hashing mapeia as chaves de entrada em inteiros dentro do intervalo [1..M] Formalmente: A funo de hashing h(k j ) [1,M] recebe uma chave k j {k 0 ,..,k m } e retorna um nmero i, que o ndice do subconjunto m i [1,M] onde o elemento que possui essa chave vai ser manipulado
Funes Hashing Mtodo pelo qual: As chaves de pesquisa so transformadas em endereos para a tabela (funo de transformao); Obtm-se valor do endereo da chave na tabela HASH Tal funo deve ser fcil de se computar e fazer uma distribuio equiprovvel das chaves na tabela Existem vrias funes Hashing, dentre as quais: Resto da Diviso Meio do Quadrado Mtodo da Dobra Mtodo da Multiplicao Hashing Universal
Resto da Diviso Forma mais simples e mais utilizada Nesse tipo de funo, a chave interpretada como um valor numrico que dividido por um valor O endereo de um elemento na tabela dado simplesmente pelo resto da diviso da sua chave por M (F h (x) = x mod M), onde M o tamanho da tabela e x um inteiro correspondendo chave 0 <= F(x) <= M Resto da Diviso
Ex: M=1001 e a seqncia de chaves: 1030, 839, 10054 e 2030 Chave Endereo 1030 29 10054 53 839 838 2030 29
Resto da Diviso Desvantagens Funo extremamente dependente do valor de M escolhido M deve ser um nmero primo Valores recomendveis de M devem ser >20 Funes Hash Seja qual for a funo, na prtica existem sinnimos chaves distintas que resultam em um mesmo valor de hashing. Quando duas ou mais chaves sinnimas so mapeadas para a mesma posio da tabela, diz- se que ocorre uma coliso.
Tratamento de Colises Algumas solues conhecidas Tabelas Hash- Colises Qualquer que seja a funo de transformao, existe a possibilidade de colises, que devem ser resolvidas, mesmo que se obtenha uma distribuio de registros de forma uniforme; Tais colises devem ser corrigidas de alguma forma; O ideal seria uma funo HASH tal que, dada uma chave 1 <= I <= 26, a probabilidade da funo me retornar a chave x seja PROB(F h (x)= I) = 1/26, ou seja, no tenha colises, mas tal funo difcil, se no impossvel O valor de h(k) o mesmo para 1030 e 2030: coliso Resto da Diviso - Coliso No exemplo dado, M=1001 e a seqncia de chaves: 1030, 839, 10054 e 2031
Chave Endereo 1030 29 10054 53 839 838 2030 29
Tratamento de Colises Alguns dos algoritmos de Tratamento de Colises so: Endereamento Fechado Endereamento Aberto Hashing Linear Hashing Duplo Endereamento Fechado Tambm chamado de Overflow Progressivo Encadeado Algoritmo: usar uma lista encadeada para cada endereo da tabela Vantagem: s sinnimos so acessados em uma busca. Processo simples. Desvantagens: necessrio um campo extra para os ponteiros de ligao. Tratamento especial das chaves: as que esto com endereo base e as que esto encadeadas
Endereamento Fechado No endereamento fechado, a posio de insero no muda. Todos devem ser inseridos na mesma posio, atravs de uma lista ligada em cada uma. 0 1 2 3 4 20 mod 5 = 0 20 18 mod 5 = 3 18 25 mod 5 = 0 coliso com 20 25 Endereamento Fechado Program TabelaHash; Const n = 50; Type Ponteiro = ^no; Type no = record item: integer; prox: Ponteiro; End; Var posicoes: array [1..n] of Integer; Procedure Inicia_Hash; Var i: integer; Begin for i:=1 to n do posicoes[i] := nil; End; A tabela hash, neste caso, contm um array de listas ligadas Endereamento Fechado Quando uma chave for inserida, a funo hash aplicada, e ela acrescentada lista adequada Procedure inserir(chave: integer); Var i: integer; aux: ponteiro; Begin i := hashCode(chave); aux := lista[i]; if aux = nil then begin new[lista[i]]; lista[i] := chave; lista[i].^.prox := nil; end else begin while aux^.prox <> nil do aux := aux^.prox; new(aux^.prox); aux := aux^.prox; aux^.prox := nil; aux^.item := chave; End; End; Assume-se que a funo hash est implementada /*Caso em que no existe o registro na posio ainda*/ Endereamento Fechado A busca feita do mesmo modo: calcula-se o valor da funo hash para a chave, e a busca feita na lista correspondente. Se o tamanho das listas variar muito, a busca pode se tornar ineficiente, pois a busca nas listas se torna seqencial 0 1 2 3 20 4 0 88 32 15 11 60 Endereamento Fechado obrigao da funo HASH distribuir as chaves entre as posies de maneira uniforme 0 0 1 2 3 4 5 6 15 10 31 4 88 13 20 Hashing Linear Tambm conhecido como Overflow Progressivo Consiste em procurar a prxima posio vazia depois do endereo-base da chave Vantagem: simplicidade Desvantagem: se ocorrerem muitas colises, pode ocorrer um clustering (agrupamento) de chaves em uma certa rea. Isso pode fazer com que sejam necessrios muitos acessos para recuperar um certo registro. O problema vai ser agravado se a densidade de ocupao para o arquivo for alta Hashing Linear 64 11 20 7 0 1 2 3 4 5 6 7 27 ? 27 mod 8 = 3 11 20 27 Hashing Linear - Implementao A tabela hash, neste caso, contm um array de objetos, e posies vazias so indicadas por -1. Neste caso, os objetos sero do tipo Integer: Program TabelaHash; Const n = 50; Var posicoes: array [1..n] of Integer; Procedure Inicia_Hash; Var i: integer; Begin for i:=1 to n do posicoes[i] := -1; End; Hashing Linear - Implementao Na insero, a funo hash calculada, e a posio incrementada, at que uma posio esteja livre Procedure inserir(chave: integer;) { Var i: integer; Begin i := FuncaoHash(chave); while (posicoes[i] <> -1) i := (i + 1) mod n; posicoes[i] := chave; End; Tambm assume-se que a funo hash est implementada Hashing Linear Valores: 52, 78, 48, 61, 81, 120, 79, 121, 92 Funo: hash(k) = k mod 13 Tamanho da tabela: 13 0 1 2 3 4 5 6 7 8 9 10 11 12 52 52 78 78 48 48 61 61 81 81 120 120 79 79 121 121 92 92 Hashing Duplo Tambm chamado de re-hash Ao invs de incrementar a posio de 1, uma funo hash auxiliar utilizada para calcular o incremento. Esta funo tambm leva em conta o valor da chave. Vantagem: tende a espalhar melhor as chaves pelos endereos. Desvantagem: os endereos podem estar muito distantes um do outro (o princpio da localidade violado), provocando seekings adicionais Hashing Duplo 64 11 20 53 7 0 1 2 3 4 5 6 7 27 ? H(27) = 27 mod 8 = 3 11 27 Inc(27) = 27*4+7 = 115 (3 + 115) mod 8 = 6 Hashing Duplo - Implementao A estrutura semelhante ao Hashing Linear: um vetor de chaves Program TabelaHash; Const n = 50; Var posicoes: array [1..n] of Integer; Procedure Inicia_Hash; Var i: integer; Begin for i:=1 to n do posicoes[i] := -1; End; Hashing Duplo - Implementao Na insero, a funo hash calculada. Caso exista conflito, chama-se uma funo hash alternativa at que uma posio esteja livre Procedure inserir(chave: integer); Var i: integer; Begin i := FuncaoHash(chave); while (posicoes[i] <> -1) i := FunoHash_Alternativa(chave); posicoes[i] := chave; End; Assume-se que essas funes esto implementadas Endereamento Aberto Remoo Para fazer uma busca com endereamento aberto, basta aplicar a funo hash, e a funo de incremento at que o elemento ou uma posio vazia sejam encontrados. Porm, quando um elemento removido, a posio vazia pode ser encontrada antes, mesmo que o elemento pertena a tabela: 64 11 20 7 Insero do 27 Remoo do 20 Busca pelo 27 11 27 20 11 27 27? No 11 Fim da busca? Sim Endereamento Aberto: Remoo Para contornar esta situao, mantemos um bit (ou um campo booleano) para indicar que um elemento foi removido daquela posio: 64 11 20 7 11 27 20 X 11 27 27? No 11 Fim da busca? No X 27 Esta posio estaria livre para uma nova insero, mas no seria tratada como vazia numa busca. Tabelas HASH Dinmica 64 1 2 11 4 9 7 Endereamento Aberto Expanso Na poltica de hashing, h que chamamos de fator de carga (load factor). Ele indica a porcentagem de clulas da tabela hash que esto ocupadas, incluindo as que foram removidas. Quando este fator fica muito alto (ex: excede 50%), as operaes na tabela passam a demorar mais, pois o nmero de colises aumenta. 9 ? 1 2 11 4 9 Tamanho = 13 Endereamento Aberto Expanso Quando isto ocorre, necessrio expandir o array que constitui a tabela, e reorganizar os elementos na nova tabela. Como podemos ver, o tamanho atual da tabela passa a ser um parmetro da funo hash. 34 40 X 11 95 34 40 11 95 Tamanho = 7 Endereamento Aberto Expanso O problema : Quando expandir a tabela? O momento de expandir a tabela pode variar Quando no for possvel inserir um elemento Quando metade da tabela estiver ocupada Quando o load factor atingir um valor escolhido A terceira opo a mais comum, pois um meio termo entre as outras duas. Quando no usar Hashing? Muitas colises diminuem muito o tempo de acesso e modificao de uma tabela hash. Para isso necessrio escolher bem: - a funo hash - o algoritmo de tratamento de colises - o tamanho da tabela Quando no for possvel definir parmetros eficientes, pode ser melhor utilizar rvores balanceadas (como AVL), em vez de tabelas hash. FIM