Tabelas de Dispersão (Hash( Tables) Sumário Definição. Características. Resolução de colisões: dispersão aberta e dispersão fechada. Teste quadrático. Desempenho de tabelas de dispersão. Interface de tabela de dispersão. Implementação de tabela de dispersão fechada com teste quadrático. HASH (v2) - 1 Tabela de Dispersão Árvores binárias (de pesquisa) simples pior caso nas operações de manipulação é O(N) pior caso surge sistematicamente emusos correntes (e: elementos previamente ordenados) Tabela de dispersão pesquisa baseada na geração de um inteiro a partir da chave tempo médio constante para inserção, remoção e pesquisa não requer gestão de memória especial nemcomparação de elementos ocorrência do pior caso tem probabilidade muito baia Aplicações tabelas de símbolos dos compiladores HASH (v2) - 2
Resolver colisões - dispersão com listas Manter lista de elementos colocados na mesma entrada 1 lista com cabeçalho em cada entrada da tabela escolhe-se inserção a ocorrer na cabeça ou na cauda da lista eemplo com função de dispersão hash() = mod 10 e inserção na cauda: 0 0 1 1 81 0, 1, 4, 81, 64 1º último 2 3 4 4 64 5 6 7 8 9 HASH (v2) - 3 Resolver colisões - dispersão aberta Perante colisão procura-se célula alternativa Tenta-se sequência de células H 0 (), H 1 (), H 2 (),... H i () = ( hash() + f(i) ) mod TamanhoTabela Teste linear: procurar sequencialmente a partir de H 0 primeira posição livre a seguir à inicial H 0 é usada - tenta-se H 0 +1, H 0 +2,... pesquisa dá a volta no fim da tabela f(i) = i Teste quadrático procurar próima posição livre a seguir a H com passo quadrático - tenta-se H 0 +1 2, H 0 + 2 2, H 0 + 3 2,... é preciso garantir que se pode percorrer a tabela toda - ver adiante f(i) = i 2 HASH (v2) - 4
Desempenho na dispersão com listas Factor de carga l razão entre o número de elementos na tabela e o tamanho da tabela Dispersão com listas comprimento médio de cada lista é λ tempo médio de pesquisa: avaliação da função de dispersão + percurso na lista - pesquisa sem sucesso: número médio de ligações a percorrer é λ - pesquisa com sucesso: número médio de ligações a percorrer é 1 + λ/2 bomdesempenho para λ próimo de 1 HASH (v2) - 5 Desempenho na Dispersão Aberta (1) Dispersão aberta com função linear - número de tentativas para inserção e para pesquisa sem sucesso: 1/2 ( 1 + 1/(1- λ) 2 ) - número de tentativas para pesquisacom sucesso: 1/2 ( 1 + 1/(1- λ) ) caso ideal (semclustering) - número de tentativas para inserção e para pesquisa sem sucesso: 1/(1- λ) - número de tentativas para pesquisacom sucesso: 1/ λ ln (1/(1- λ) ) função quadrática elimina clustering primário - na prática, eficiência próima do caso ideal λ= 0.2 Ins, Falha: 1.3 Sucesso: 1.1 λ= 0.8 Ins, Falha: 13 Sucesso: 3 λ= 0.2 Ins, Falha: 1.3 Sucesso: 1.1 λ= 0.8 Ins, Falha: 5.0 Sucesso: 2.0 HASH (v2) - 6
Desempenho na Dispersão Aberta (2) 10000 nº de posições acedidas para inserção e pesquisa 1000 100 10 13 5 3 2 1 0,0 0,1 0,2 0,3 0,4 0,5 0,6 0,7 0,8 0,9 1,0 factor de carga função linear, elemento ineistente função linear, elemento eistente caso ideal (sem clustering), elemento ineistente caso ideal (sem clustering), elemento eistente com dispersão aberta, o factor de carga não deve eceder 0,5 para se garantir um bom desempenho! HASH (v2) - 7 Dispersão com Teste Quadrático Teorema Usando teste quadrático e uma tabela cujo tamanho é um número primo, um novo elemento pode sempre ser inserido se a tabela não estiver preenchida a mais de 50% -Mostra-se que as primeiras Tam/2 posições alternativas são todas distintas, por redução ao absurdo h() + i 2 (mod Tam) = h() + j 2 (mod Tam) com i j e 0 < i,j < Tam/2 h() + i 2 = h() + j 2 (mod Tam) i 2 = j 2 (mod Tam) i 2 - j 2 = 0 (mod Tam) ( i-j )( i+j ) = 0 (mod Tam) (o mesmo que ( i-j )( i+j ) = k Tam) Sendo Tam número primo, ( i-j ) ou ( i+j ) tem de ser 0 (mod Tam). Sendo i e j distintos, ( i-j ) não é 0; sendo 0 < i,j < Tam/2, ( i+j ) também não é 0. Então as primeiras Tam/2 posições alternativas são distintas HASH (v2) - 8
Dispersão com Teste Quadrático Teorema A geração de posições alternativas no teste quadrático pode ser feita com apenas uma multiplicação Seja H 0 a posição inicial, H i-1 a última posição calculada e H i a próima H i = H 0 + i 2 (mod Tam) H i - H i-1 = i 2 - (i-1) 2 (mod Tam) H i = H i-1 + 2i - 1 (mod Tam) O valor de H i pode assim ser obtido sem operações pesadas de multiplicação Para calcular o mod: 2i-1 é menor que Tam H i-1 + 2i - 1 ou é menor que Tam (caso em que o mod se dispensa) ou é pouco maior que Tam (caso em que o modse reduz a subtrair Tam) HASH (v2) - 9 Eliminação com Dispersão Aberta Para eliminar um elemento da tabela, marca-se como apagado, para que posteriores pesquisas funcionem correctamente 3 estados: posição ocupada, livre ou marcada como apagada posição activa: ocupada ou marcada como apagada Eemplo, com hash() = mod 10 0 0 0 0 add(13) 1 2 3 4 5 6 7 13 add(23) 1 2 3 4 5 6 7 13 23 remove(13) 1 2 3 4 5 6 7 13 23 1 2 contains(23) 3 marca posição como apagada 4 5 6 7 13 23 como a posição está marcada como apagada, procura na posição seguinte 8 8 8 8 9 9 9 9 HASH (v2) - 10
Tabela de Dispersão em HashSet package weiss.util; import java.io.serializable; public class HashSet etends AbstractCollection implements Set private static final int DEFAULT_TABLE_SIZE = 101; private int currentsize = 0; private int occupied = 0; private int modcount = 0; private HashEntry [ ] array; public HashSet( ) allocatearray( DEFAULT_TABLE_SIZE ); clear( ); public HashSet( Collection other ) allocatearray( other.size( ) * 2 ); clear( ); Iterator itr = other.iterator( ); while( itr.hasnet( ) ) add( itr.net( ) ); HASH (v2) - 11 Tabela de Dispersão em HashSet public int size( ) return currentsize; /** * Thismethodis not partof standard Java1.2. * Like contains, it checks if is in the set. * If it is, it returns the reference to the matching * object; otherwise it returns null. */ public Object getmatch( Object ) int currentpos = findpos( ); if( array[ currentpos ] == null ) return null; return array[ currentpos ].element; HASH (v2) - 12
Tabela de Dispersão em HashSet /** * Tests if some item is in this collection. * @param any object. * @return true if this collection contains an item equal to. */ public boolean contains( Object ) return isactive( array, findpos( ) ); /** * Tests if item in pos is active. * @param pos a position in the hash table. * @param arr the HashEntry array (can be oldarray during rehash). * @return true if this position is active. */ private static boolean isactive( HashEntry [ ] arr, int pos ) return arr[ pos ]!= null && arr[ pos ].isactive; HASH (v2) - 13 Tabela de Dispersão em HashSet /** * Adds an item to this collection. */ public boolean add( Object ) int currentpos = findpos( ); if( isactive( array, currentpos ) ) return false; array[ currentpos ] = new HashEntry(, true ); currentsize++; occupied++; modcount++; if( occupied > array.length / 2 ) rehash( ); return true; HASH (v2) - 14
Tabela de Dispersão em HashSet /** * Private routine to perform rehashing. * Can be called by both add and remove. */ private void rehash( ) HashEntry [ ] oldarray = array; // Create a new, empty table allocatearray( netprime( 4 * size( ) ) ); currentsize = 0; occupied = 0; // Copy table over for( int i = 0; i < oldarray.length; i++ ) if( isactive( oldarray, i ) ) add( oldarray[ i ].element ); HASH (v2) - 15 Tabela de Dispersão em HashSet /** * Removes an item from this collection. */ public boolean remove( Object ) int currentpos = findpos( ); if(!isactive( array, currentpos ) ) return false; array[ currentpos ].isactive = false; currentsize--; modcount++; if( currentsize < array.length / 8 ) rehash( ); return true; HASH (v2) - 16
Tabela de Dispersão em HashSet public void clear( ) currentsize = occupied = 0; modcount++; for( int i = 0; i < array.length; i++ ) array[ i ] = null; public Iterator iterator( ) return new HashSetIterator( ); HASH (v2) - 17 Tabela de Dispersão em HashSet private class HashSetIterator implements Iterator private int epectedmodcount = modcount; private int currentpos = -1; private int visited = 0; public boolean hasnet( ) if( epectedmodcount!= modcount ) throw new ConcurrentModificationEception( ); return visited!= size( ); HASH (v2) - 18
Tabela de Dispersão em HashSet public Object net( ) if(!hasnet( ) ) throw new NoSuchElementEception( ); do currentpos++; while( currentpos < array.length &&!isactive( array, currentpos ) ); visited++; return array[ currentpos ].element; HASH (v2) - 19 Tabela de Dispersão em HashSet public void remove( ) if( epectedmodcount!= modcount ) throw new ConcurrentModificationEception( ); if( currentpos == -1!isActive( array, currentpos ) ) throw new IllegalStateEception( ); array[ currentpos ].isactive = false; currentsize--; visited--; modcount++; epectedmodcount++; HASH (v2) - 20
Tabela de Dispersão em HashSet private static class HashEntry implements Serializable public Object element; // the element public boolean isactive; // false if marked deleted public HashEntry( Object e ) this( e, true ); public HashEntry( Object e, boolean i ) element = e; isactive = i; HASH (v2) - 21 Tabela de Dispersão em HashSet private int findpos( Object ) int collisionnum = 0; int currentpos = ( == null )? 0 : Math.abs(.hashCode( ) % array.length ); while( array[ currentpos ]!= null ) if( == null ) if( array[ currentpos ].element == null ) break; else if(.equals( array[ currentpos ].element ) ) break; currentpos += 2 * ++collisionnum - 1; // Compute ith probe if( currentpos >= array.length ) // Implement the mod currentpos -= array.length; return currentpos; HASH (v2) - 22
Tabela de Dispersão em HashSet private void allocatearray( int arraysize ) array = new HashEntry[ arraysize ]; private static int netprime( int n ) if( n % 2 == 0 ) n++; for( ;!isprime( n ); n += 2 ) ; return n; HASH (v2) - 23 Tabela de Dispersão em HashSet private static boolean isprime( int n ) if( n == 2 n == 3 ) return true; if( n == 1 n % 2 == 0 ) return false; for( int i = 3; i * i <= n; i += 2 ) if( n % i == 0 ) return false; return true; HASH (v2) - 24
Implementação de Dicionários (maps) com Tabelas de Dispersão (1) Um dicionário, ou função finita definida em etensão (map), é um conjunto de pares (chave, valor) tal que não eistem dois pares com chaves iguais Tanto a chave como o valor podem ser de um tipo arbitrariamente compleo Eemplo: chave nº da conta; valor nome do dono da conta - É função (finita): (1, "João"), (3, "João"), (5, "Rui") chave valor - Não é função (finita) (1, "João"), (1, "Ana "), (5, "Rui") (mas é uma relação binária) chave valor HASH (v2) - 25 Implementação de Dicionários (maps) com Tabelas de Dispersão (2) Implementação com tabela de dispersão (classe HashMap) um elemento da tabela é um par (chave, valor) (classe Pair) a função de dispersão (hashcode) e a função de comparação (equals) baseiam-se apenas na chave do par as funções de pesquisa (containskey, get) e eliminação (remove) têm como argumento a chave - funções implementadas na classe abstracta MapImpl a função de inserção (put) tem como argumentos a chave e o valor - função implementada na classe abstracta MapImpl pode-se modificar o valor correspondente a uma dada chave (put) - função implementada na classe abstracta MapImpl HASH (v2) - 26
Tabela de Dispersão em HashMap package weiss.util; /** * Hash table implementation of the Map. */ public class HashMap etends MapImpl public HashMap( ) super( new HashSet( ) ); public HashMap( Map other ) super( other ); HASH (v2) - 27 Tabela de Dispersão em HashMap protected Map.Entry makepair( Object key, Object value ) return new Pair( key, value ); protected Set makeemptykeyset( ) return new HashSet( ); protected Set clonepairset( Set pairset ) return new HashSet( pairset ); HASH (v2) - 28
Tabela de Dispersão em HashMap private static final class Pair implements Map.Entry public Pair( Object k, Object v ) key = k; value = v; public Object getkey( ) return key; public Object getvalue( ) return value; HASH (v2) - 29 Tabela de Dispersão em HashMap public int hashcode( ) return key.hashcode( ); public boolean equals( Object other ) if( other instanceof Map.Entry ) return getkey( ).equals( ((Map.Entry) other).getkey() ); else return false; private Object key; private Object value; HASH (v2) - 30