Você está na página 1de 11

INTELIGÊNCIA

ARTIFICIAL

Pedro Henrique Chagas Freitas


Estrutura de dados
para IA III
Objetivos de aprendizagem
Ao final deste texto, você deve apresentar os seguintes aprendizados:

„„ Definir as tabelas hash.


„„ Analisar a resolução de colisões em tabelas hash.
„„ Implementar as tabelas hash em Python.

Introdução
Neste capítulo, você estudará o funcionamento das tabelas hash, a re-
solução de colisões nelas e sua implementação por meio da linguagem
Python.

Tabelas hash
As tabelas hash também são conhecidas como tabelas de dispersão ou de
espelhamento e responsáveis pelo armazenamento de coleções de valores, em
que cada valor está associado a uma chave (PERKOVIC, 2016). As chaves,
por sua vez, devem ser distintas e utilizadas para realizar o mapeamento dos
valores nessa tabela, sendo que a função que faz esse mapeamento, na maioria
dos casos, se chama hashing (COPPIN, 2010).
Um exemplo clássico é a implementação de uma tabela de dispersão com
um vetor, que, neste caso, terá oito posições, logo, a função hashing será h(x)
= x MOD 8. Utiliza-se o MOD porque ele representa o resto de uma divisão,
como 10 MOD 8 = 2, em que 10/8 equivale ao quociente 1 e resto 2 ou 8*1
+ 2 = 10.
Pode-se sugerir que a função h(k) é a posição original da chave k, a qual
deve ser inserida nessa posição da tabela (DROZDEK, 2017). Assim, realiza-se
a inserção de uma coleção de valores, com suas respectivas chaves em uma
tabela de dispersão, conforme você pode ver no Quadro 1.
2 Estrutura de dados para IA III

Quadro 1. Tabela de dispersão

ID Chave Valor

1 25 LIA

2 18 ANA

5 5 RUI

7 31 GUT

Observe que, na tabela de dispersão no Quadro 1, há uma coleção de valores


e suas respectivas chaves e posições (ID). Diante disso, o valor GUT foi inserido
na posição (ID) 7 do quadro, considerando que a chave associada a ele é 31,
logo h(31) = 7. O que ocorreria se fosse inserido na tabela o valor ROT com
chave 33 na posição 1? Teria h(33) = 1, sendo que a posição 1 da tabela já está
sendo ocupada, neste caso, as chaves 25 e 33 colidiram (DROZDEK, 2017).
Assim, conclui-se que duas chaves x e y colidem, se h(x) = h (y).
Portanto, ocorrem colisões de chaves em uma tabela hash ou de dispersão,
porque, na maioria das vezes, o domínio das chaves é maior que a quantidade
de posições da tabela, sendo seu número de posições finito. Porém, não se pode
negar a inserção de uma chave se a tabela tiver posições livres, por isso, foram
criadas estratégias para lidar com essas colisões. Lembre-se que a utilização
da função hashing é recomendada para inserção em uma tabela com muitos
dados que tenham faixas de valores variáveis (COPPIN, 2010).
Fundamentalmente, as tabelas hash ou de dispersão implementam a inter-
ligação entre chaves e valores, realizando a resolução das colisões, o que tem
uma aplicação prática em bancos de dados normalizados ou nos distribuídos.
Nesses bancos, o controle de colisões é crítico, e a implementação dessas
tabelas essencial para o encadeamento das chaves e dos índices de valores,
conforme você pode observar no Quadro 1.
Estrutura de dados para IA III 3

As tabelas de dispersão foram criadas com o objetivo de realizar buscas rápidas para
a obtenção de valores desejados a partir de chaves. Alguns estudiosos atribuem sua
criação ao trabalho de Hans Peter Luhn na International Business Machines (IBM), em
1953, mas outros afirmam que as primeiras utilizações dessas tabelas ocorreram em
1960, com compiladores e interpretadores da linguagem BASIC, usada na dispersão
de variáveis e seu armazenamento (DROZDEK, 2017).

Índice
Chaves de valores Hash de chaves e valores
0
John Smith Lisa Smith + 1-555-8976
1
:
Lisa Smith 872 John Smith +1-555-1234
873
Sam Doe 874 Sandra Dee +1-555-9655
:
Sandra Dee 998
Sam Doe +1-555-5030
999
Figura 1. Dispersão de chaves e índices de valores na tabela hash.

Colisões em tabelas hash


Para entender as colisões em tabelas hash, a melhor forma é mostrando como
uma colisão ocorre. Portanto, pressuponha que você tenha uma tabela de
dispersão para armazenamento de valores, conforme as chaves, logo, haverá
uma associação entre chave e valor, que responde aos seguintes parâmetros:

„„ W, o número de posições na tabela hash ou de dispersão.


„„ K, o número de chaves de outra tabela (chamada tabela key).
„„ X, o fator de associação entre as duas tabelas (X = K/M).
4 Estrutura de dados para IA III

A partir disso, você precisa aplicar uma função de associação entre as


chaves da tabela key e as posições da tabela de dispersão, ocorrendo a função
hashing, responsável por transformar cada chave em um índice da tabela hash
(DROZDEK, 2017). Logo, essa função implementa a seguinte requisição:
“qual posição da tabela hash deverá ser associada às chaves da tabela key”.
Assim, a função hashing inicializa uma alocação dinamicamente das chaves
na tabela de dispersão, associando esta à tabela key.
A função hashing ainda associa um valor hash, entre 0 e W – 1, a cada
chave. Por exemplo, caso existam cadastros de pessoa física (CPF) como chave
(lembrando que eles podem ser chaves, pois não se duplicam) e a necessidade
de associá-los aos registros gerais (RG), estes seriam a tabela hash; e CPF,
a tabela key.
Consequentemente, a função hashing produziria ao longo da associação
diversas colisões, que ocorrem quando duas chaves diferentes têm valor igual
para essa função e são alocadas na mesma posição da tabela hash, conforme
você pode conferir nos Quadros 2 e 3.

Quadro 2. Tabela hash

Chave Índice Valores

a 2 xyz

b 0 pqr

c 3 ijk

d 2 uvw

e … …

f … …

g … …

h … …
Estrutura de dados para IA III 5

Quadro 3. Tabela hash

Índice da tabela hash Chaves/valores

… (W – 1) … (W – 1)

Observe que houve colisões no índice hashing com associação da chave 2


duas vezes, respectivamente com as chaves a e d e os valores xyz e uvw. Como
foi citado anteriormente, esse tipo de situação é comum em banco de dados,
quando há a indexação de uma grande quantidade de dados em tabelas, logo,
as tabelas hash ou de dispersão costumam utilizar vetores na função hashing
para realizar a associação entre as tabelas (COPPIN, 2010).
Logo, conclui-se que há colisões quando os vetores apontarem para o
mesmo índice ou a função hashing coincidir duas chaves diferentes para o
mesmo índice, como ocorreu no exemplo anterior, com as chaves a e d. Por-
tanto, colisões são normais, mas a função hashing pode ser acompanhada de
diversas estratégias ou outras funções para evitar ao máximo o número de sua
ocorrência. Por melhor que seja o projeto da tabela hash, ela sempre terá coli-
sões, assim, deve-se implementar também o mecanismo de tratamento delas,
que dependem diretamente da finalidade para a qual essa tabela é utilizada.
Há dois mecanismos padrões que auxiliam no tratamento das colisões em
tabelas hash: o endereçamento aberto e o encadeamento. No endereçamento
aberto, tem-se o armazenamento de colisões na própria tabela, ocorrendo a
6 Estrutura de dados para IA III

busca por colisões e a resolução com a alocação destas em registros vazios,


cuja inserção segue a lógica first in, first out (FIFO) na qual se resolve as
colisões conforme elas são encontras (COPPIN, 2010). O problema desse tipo é
que cria uma sobrecarga na estrutura em que fica a tabela, pois, à medida que
há alocação, ocorrem as colisões e, em paralelo, a realocação dos elementos
colididos, podendo-se adicionar lógicas paralelas para auxiliar na mitigação
dessa sobrecarga (PERKOVIC, 2016).
Já no encadeamento, tem-se o atrelamento paralelo dos dados, assim, con-
forme há a associação da tabela hash e a ocorrência das colisões, realiza-se o
armazenamento dos dados em estruturas encadeadas fora dessa tabela. Dessa
forma, os elementos são armazenados em duas posições, uma de armazena-
mento na tabela e outra encadeada paralelamente. Caso aconteça uma colisão,
busca-se na tabela encadeada outra posição até verificar uma que a resolva,
armazenando-a nesta (DROZDEK, 2017).
Conclui-se que as tabelas hash são estruturas de dados que não permi-
tem o armazenamento repetido de elementos, por isso, haverá colisões e seu
tratamento.

Tabelas hash em Python


A seguir, você pode visualizar a implementação de uma tabela de dispersão
utilizando a linguagem Python. Nesse caso, utiliza-se a classe hash, oriunda
das bibliotecas do Python, para implementar a função hashing e os métodos
necessários para a criação da tabela, inserindo comentários nas linhas do
código.

# Tabela Hash

class Hash:

def _ _ init _ _ (self,tam):


self.tab = {}
self.tam _ max = tam

def funcaohash(self, chave):


v = int(chave)
Estrutura de dados para IA III 7

return v%self.tam _ max

def cheia(self):
return len(self.tab) == self.tam _ max

def imprime(self):
for i in self.tab:
print("Hash[%d] = " %i, end="")
print (self.tab[i])

def apaga(self, chave):


pos = self.busca(chave)
if pos != -1:
del self.tab[pos]
print("-> Dado da posicao %d apagado" %pos)
else:
print("Item nao encontrado")

def busca(self, chave):


pos = self.funcaohash(chave)
if self.tab.get(pos) == None:
return -1
if self.tab[pos] == chave:
return pos
return -1

def insere(self, item):


if self.cheia():
print("-> ATENÇÃO Tabela Hash CHEIA")
return
pos = self.funcaohash(item)
if self.tab.get(pos) == None:
self.tab[pos] = item
print("-> Inserido HASH[%d]" %pos)
else:
print("-> Ocorreu uma colisao na posicao %d" %pos)

# Fim da tabela Hash


8 Estrutura de dados para IA III

# Início do tratamento de colisões na tabela hash, utilizando


busca por endereçamento aberto

tamanhoHash = 7
tab = Hash(tamanhoHash)
print("\n")
print(" Tabela HASH Sem Colisões (%d itens) " %tamanhoHash)

for i in range (0,tamanhoHash,1):


print("\nInserindo elemento %d" %(i + 1));

item = input(" - Forneca valor numerico inteiro: ")


tab.insere(item)
item = input("\n - Insira o valor numérico inteiro para bus-
car: ")

pos = tab.busca(item)
if pos == -1:
print("Item não encontrado")
else:
print("Item encontrado na posição: ", pos)
item = input("\n – Insira o valor numérico inteiro para apa-
gar: ")
tab.apaga(item)

print("\nImprimindo conteudo")
tab.imprime()
print("\n")

COPPIN, B. Inteligência artificial. Rio de Janeiro: LTC, 2010. 664 p.


DROZDEK, A. Estrutura de dados e algoritmos em C++. 4. ed. São Paulo: Cengage Lear-
ning, 2017. 708 p.
PERKOVIC, L. Introdução à computação usando Python: um foco no desenvolvimento
de aplicações. Rio de Janeiro: LTC, 2016. 516 p.

Você também pode gostar