Hashing: conceitos hashing é uma técnica conhecida como espalhamento, mapeamento ou randomização que tenta distribuir dados em posições aleatórias de uma tabela (array) associa cada objeto (de um determinado tipo) a uma chave, permitindo uma indexação a chave é o atributo pelo qual um elemento pode ser identificado dos demais exemplo de indexação: string (cadeia de caracteres) inteiro Algoritmos 1057 Hashing 706 TABELA 425 Hashing: conceitos por que usar hashing? Em algumas estruturas a busca leva tempo até encontrar o elemento desejado. Exemplos: vetores e listas árvores 5 2 6 1 7 8 4 9 5 2 6 1 4 algumas aplicações que são beneficiadas pelo uso de hashing são os dicionários de arquivos (de banco de dados) e arquivos relacionados aos sistemas operacionais e compiladores 1 2 4 8 6 9 1
Hashing: conceitos outro exemplo de indexação: tabuleiro do jogo da velha O O O = 0 = 1 = 2 O O 0 1 0 2 0 2 1 0 0 0 1 2 3 4 5 6 7 8 = 1 x 3 1 + 2 x 3 3 + 2 x 3 5 + 1 x 3 6 = 3 + 54 + 486 + 729 = 1272 se fôssemos indexar todas as possíveis representações do tabuleiro, de quantas chaves necessitaríamos? Hashing: conceitos exemplo de indexação: tabuleiro do jogo da velha cada tabuleiro é mapeado em um valor único, entre 0 e 19682 e, portanto, é possível recuperar o tabuleiro a partir de sua chave através da chave, podemos indexar os objetos, por exemplo, em um array, caso o tamanho seja suficiente: 0 1 2 3 4 5 6... 19681 19682 O O O O O O O O O O O O O O O O O O O O qual seria a chave para indexar este objeto? O 2
Hashing: tabela a tabela hash é uma estrutura de dados (array) que associa chaves de pesquisa (hash) a valores e são utilizadas quando o número de chaves efetivamente armazenadas é muito menor que o universo de chaves ao invés de organizar a tabela segundo o valor relativo de cada chave em relação às demais, a tabela hash leva em conta somente o seu valor absoluto, interpretado como um valor numérico armazena-se os elementos em um array na posição indicada pelo número associado ao elemento a idéia é que cada elemento em um conjunto possua um número associado a ele e quaisquer dois elementos distintos possuam números associados diferentes Hashing: tabela se conseguirmos associar a cada elemento a ser armazenado um número, poderemos realizar as operações de inserção, remoção e busca em tempo constante as tabelas hash são usadas para indexação de grandes volumes de informação (como bases de dados) porque melhoram o desempenho em pesquisas e recuperações (acessos) em tabelas hash, usamos uma função para calcular onde colocamos cada elemento, ou seja, esta função mapeia um símbolo para valores inteiros e tem o objetivo de reduzir o número de índices e posições da tabela a manipular 3
Hashing: tabela considere uma tabela com m posições, que contenha n elementos define-se o fator de carga f c de uma tabela hash como sendo o número médio de elementos armazenados em uma cadeia, ou seja, n f c = m esse valor é usado para avaliar quão eficiente as remoções e recuperações podem ser e para determinar quando é necessário reestruturar a tabela (re-hash) quanto menor o fator de carga, menos tempo as funções de busca e inserção irão consumir deseja-se que o fator de carga seja próximo de 1, porque desta forma pode-se encontrar uma chave com 1 ou 2 comparações se ele for muito pequeno, tem-se uma tabela vazia e uma utilização ineficiente do espaço Hashing: função esta função (de mapeamento) que associa a cada elemento de um conjunto U (universo das chaves) um número que sirva de índice em uma tabela (array) é chamada de função hash a função hash é utilizada para calcular a posição que um elemento deve ser armazenado a partir de uma chave k, ou seja, ela mapeia o universo U de chaves nas posições de uma tabela hash T[0...m-1] uma função hash deve satisfazer as seguintes condições: ser simples de calcular assegurar que elementos distintos possuam índices distintos gerar uma distribuição equilibrada para os elementos dentro do array criar uma função hash satisfazendo as condições colocadas nem sempre é uma tarefa fácil e existem muitos modelos para sua criação 4
Hashing: função uma boa função hash é aquela em que toda entrada (slot) do array possui a mesma probabilidade de ser atingida pela função dizemos que uma função hash está adequada se cada chave tem a mesma chance de cair em qualquer uma das posições da tabela a grande vantagem na utilização de hashing está no desempenho, ou seja, no tempo esperado para a recuperação dos elementos enquanto a busca linear tem complexidade temporal (do número de elementos N) a busca binária tem complexidade logarítmica (log 2 N) o tempo de busca usando hashing é praticamente independente do número de chaves armazenadas na tabela aplicando a função hash no momento de armazenar e no momento de buscar a chave, a busca pode se restringir diretamente àquela posição da tabela gerada pela função Hashing: função / colisões chama-se h de função hash e h(k), de valor hash de k usa-se h para calcular a posição onde o elemento x será armazenado a partir de sua chave k 5
Hashing: função / colisões idealmente, cada chave processada por uma função hash deveria gerar uma posição diferente na tabela mas, devido ao fato de existirem mais chaves que posições, é comum que várias chaves sejam mapeadas para a mesma posição da tabela quando isto ocorre, dizemos que houve uma colisão alguns autores usam o termo sinônimos para se referir a dois elementos com mesmo valor de hash Exemplo: seja h(k) = k mod 8 e h(k) = k mod 15 45 mod 8 = 5 1256 mod 15 = 11 21 mod 8 = 5 356 mod 15 = 11 93 mod 8 = 5 506 mod 15 = 11 45, 21 e 93 são sinônimos e 1256, 356 e 506, também / colisões uma colisão ocorre quando a posição em que devemos inserir um elemento já está ocupada não há funções h que não produzam colisões como escolher funções hash adequadas? a função h deve distribuir uniformemente as chaves nas m posições da tabela: k: h( k ) = p 1 P( k) = para cadap = 0,1,2..., m 1 m a soma das probabilidades de ocorrência de chaves k mapeadas para p deve ser igual a 1/m, para todas as posições p da tabela a função h deve espalhar bem os elementos para minimizar o número de colisões: muitas colisões eliminam a vantagem da técnica 6
funções genéricas que funcionem bem na maior parte das situações: utilização do método da divisão utilização do método da multiplicação a maior parte das funções hash supõe que o universo de chaves é o conjunto dos números naturais se as chaves não são números naturais, deve-se encontrar um modo de interpretá-las como números naturais método da divisão considere que a chave é um número natural k o método consiste em usar o resto da divisão de k pelo número de posições (m) na tabela define-se a função com sendo: h( k) = k modm o resto dessa divisão inteira, um valor entre 0 e m-1, é considerado o endereço de uma tabela hash de m posições 7
método da divisão para reduzir colisões, é recomendável que m não seja potências de 2 e 10 e, preferencialmente, seja um número primo se m é muito grande a função não distribui bem os elementos exemplo, se a tabela hash tem tamanho m = 12 chave é k = 100, então h(k) = 4 pelo fato de só exigir uma única operação (de divisão), o hash por divisão é bastante rápido método da multiplicação a idéia é multiplicar o valor de k por uma constante real (A) menor que 1 do resultado, ignora-se a parte inteira, obtendo-se um resultado intermediário aparentemente aleatório ' menor que 1 esse resultado é multiplicado por m, o que resulta em um valor entre 0 e m-1, cuja parte fracionária é ignorada a função pode ser escrita assim: h ( k) = m[( ka) mod1] x é a parte inteira de x onde (ka mod 1) significa a parta fracionária de ka, ou seja ka ka 8
método da multiplicação a principal vantagem deste esquema é que a escolha de m deixa de ser problemática não há restrições de que m seja uma potência de 2, portanto pode-se escolher potências de 2 para m o valor da constante A é que pode influenciar na qualidade da função a escolha ótima de A depende das características dos dados sobre os quais está sendo feito o hash estudos matemáticos de Knuth indicam que um bom valor para A em situações genéricas é A = (5 ½ - 1)/2 0.6180339887.. método da multiplicação exemplo: suponha m = 2 7 = 128 tem-se k = 13 usa-se A = 0.6180339887 ka mod 1 = 0.0344418531 m x 0.0344418531 = 4.4085571968 h(k) = 4 qual é o valor de h(k) se k = 123? e se diminuirmos o número de posições na tabela hash de 2 7 para 2 5, qual o valor de h(k=123)? E de h(k=13)? 9
Hashing: colisões o que fazer quando mais de um elemento for inserido na mesma posição de uma tabela hash? o processamento de tabelas hash demanda a existência de algum mecanismo para o tratamento de colisões as formas mais usuais de tratamento de colisão são: por endereçamento aberto (hashing aberto ou double) a estratégia é utilizar o próprio espaço da tabela que ainda não foi ocupado para armazenar a chave que gerou a colisão por encadeamento (hashing fechado) para cada posição onde ocorre colisão cria-se uma área de armazenamento auxiliar (lista ligada), externa à área da tabela hash Hashing: tabelas e funções Exercício 1) Explique o que são tabelas hash e compare-as com as tabelas de acesso direto. Exercício 2) O que significa dizer que o fator de carga de uma tabela hash não deve exceder 1? Exercício 3) O que é uma função hash? Quais as características de uma boa função hash? Exercício 4) Se U é o conjunto de chaves e se a tabela tem m posições, então a função hash (h) mapeia elementos do conjunto de chaves em posições da tabela na forma h: U {0,1,2,...,m-1}. Qual é a função h(k) que mapeia h: U {0,1,2,...,8}? 10
Hashing: tabelas e funções Exercício 5) Seja h(k) = k mod 100. Como o valor de h(k) depende dos dígitos que formam k? Exercício 6) Suponha que se está utilizando uma tabela hash com m entradas e que a função hash é h(k) = 11k mod m, quando a entrada é a k-ésima letra do alfabeto (por exemplo, C corresponde a k = 3). Desenhe a tabela hash que ilustra o processo de inserção das chaves E A S Y P R O G T H I N G quando m=16, calcule o fator de carga desta tabela e identifique as possíveis colisões. Exercício 7) Seja um conjunto de elementos que é constituído de strings de no máximo 8 caracteres. Uma função hash pode ser obtida somando os valores ASCII dos caracteres e aplicando o método da divisão, supondo que a quantidade de posições da tabela seja m = 10007 (número primo). Dê exemplos de como ficará a tabela hash e analise se a distribuição nessa tabela ficou equilibrada. Se não, sugira alguma solução mantendo m = 10007. Hashing: tabelas e funções Exercício 8) Considere uma tabela hash com m=1000 posições e função hash: h(k) = k mod m a) calcule as posições onde os elementos de chaves 61, 62, 63, 64 e 65 serão colocados na tabela b) calcule onde os mesmos elementos seriam colocados se a função hash fosse h(k) = m [(ka) mod 1)] com A = (5 1/2 1)/2 c) compare e comente os resultados de b) com os de a) 11