Você está na página 1de 16

EIC - Escola de Informtica & Computao

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

Aluno: Lawrence Fernandes

Estudo documentado sobre funes para String Hashing

Rio de Janeiro
Junho de 2015

EIC - Escola de Informtica & Computao


Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

Sumrio
1. Introduo............................................................................................................................
2. Hashing................................................................................................................................
3. Tabela de Hashing................................................................................................................
4. Funes de Hashing.............................................................................................................
5. Colises................................................................................................................................
6. Anlise de Eficincia............................................................................................................
7. Observaes Finais.............................................................................................................14
8. Bibliografia........................................................................................................................ 15

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

1. Introduo
Uma das coisas que difere nossa sociedade moderna de outras anteriores a nossa
necessidade por informao e, talvez mais importante, a impacincia que temos em obt-la
afinal, conforme j dizia Benjamin Franklin ainda no sculo XVIII, tempo dinheiro. Um
dos problemas bsicos da computao encontrar algo que foi armazenado previamente em
algum lugar, de forma eficiente com relao ao tempo de resposta. Desejamos rpido
acesso a grandes volumes de dados, algo que contraditrio em termos computacionais. [1]
Imaginemos a seguinte situao: uma empresa precisa manter uma tabela com os
nomes de seus funcionrios e demais dados destes. Primeiramente, nesse caso necessrio
utilizar uma estrutura de dados que permita o armazenamento flexvel dos dados, pois
funcionrios podem ser demitidos ou novos funcionrios podem ser contratados. Podemos
ento utilizar listas ou uma rvore para a implementao.
As listas so simples de implementar, mas possuem um tempo mdio de acesso T = n/2,
o que as torna impraticveis para grandes quantidades de dados. Para se ter uma ideia, em
uma lista com 100.000 dados, para se recuperar 3 elementos em sequncia faremos um total
de 150.000 acessos a nodos. As rvores por outro lado so estruturas mais complexas, mas
possuem um tempo mdio de acesso T= log Gn, onde G = grau da rvore. No obstante, a
organizao de uma rvore depende da ordem de entrada dos dados e, para evitar a
deteriorao, existem modelos de rvores que se reorganizam sozinhas, sendo as rvores
AVL e B as mais populares. [3]
Assim sendo, levando-se em conta a complexidade de implementao e a eficincia do
algoritmo para o problema levantado, uma possvel soluo seria o uso de hashing.
Mtodos hashing vem sendo utilizados em diversas aplicaes: corretores ortogrficos,
sistemas de gerenciamento de banco de dados, assembler, compiladores, entre outros.
Como veremos, existem diversas formas de hashing, as quais so utilizadas para diferentes
problemas. Esse trabalho procura explicar os conceitos bsicos dessa tcnica, focando na
anlise da eficincia de funes voltadas para o hashing de cadeias de caracteres (String
Hashing), o qual muito utilizado para indexao de dados.

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

2. Hashing
Hashing uma forma relativamente fcil de se implementar e intuitiva de se organizar
grandes quantidades de dados, consistindo basicamente na transformao de uma cadeia de
caracteres (string), em um valor de comprimento fixo normalmente menor ou chave que
representa a string original. Por este motivo, amplamente usado para indexar e recuperar
itens em um banco de dados, visto que mais rpido encontrar determinado item utilizando
a chave de hash, que mais curta, do que encontr-lo usando o valor original.
O hashing possui como ideia central a diviso de um universo de dados a ser
organizado em subconjuntos mais gerenciveis, da o seu nome (to hash, do ingls, verbo
picar). [2] Possui dois conceitos centrais: tabela de hashing e funo de hashing, os quais
sero abordados em maiores detalhes. De maneira resumida, a tabela a estrutura que vai
armazenar os valores originais (chaves de hash ou hash keys) e convertidos pela funo em
seu respectivo lugar tambm chamado bucket. Alm destes, um terceiro conceito
importante, o de colises, ser abordado posteriormente.

Fig. 1: Funcionamento da tcnica de hashing


Podemos ententer o hashing como o oposto dos algoritmos de ordenao. Enquanto
estes organizam os registros em algum padro, o hashing espalha os registros pela tabela
hash de uma forma completamente aleatria na teoria. por isso que o hashing
apropriado para implementar um relacionamento entre elementos mas no se presta a

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

operaes que tentam fazer uso de quaisquer outros relacionamentos entre os dados. Na
execuo da tabela hash, outros relacionamentos podem ser destrudos. [1]

3. Tabela de hashing
Tabela de hashing, tambm conhecida como tabela de disperso, tabela de
espalhamento, tabela de escrutnio ou simplismente tabela hash, um vetor
(unidimensional ou multidimensional) indexado por um nmero inteiro de valor calculado
por uma funo ndice chamada funo hash.
A histria da tabela de disperso incerta. H quem credite sua criao a H. P. Luhn
que, em 1953, teve a ideia enquanto trabalhava na IBM. Outra hiptese de que foram
inventadas pelos autores de compiladores para linguagens de programao nos anos 1960,
como truque para manter as listas de variveis de usurio e seus respectivos valores. Um
interpretador de linguagem BASIC, por exemplo, no muito mais do que uma calculadora
com uma tabela de disperso em que as variveis e seus valores so armazenados. [2]
Operaes tpicas de tabela de hash so:
Inicializao
Insero
Recuperao
Eliminao
A vantagem das tabelas de disperso que estas no requerem espao para
armazenamento de ndice, ao passo que a insero em outras estruturas, tais como rvores,
normalmente requerem um ndice. Tal ndice poderia ser feito na forma de uma fila. Alm
disso, hashing permite alteraes rpidas. A desvantagem a carencia de localidade e
recuperao sequencial por chave. [3] Consequentemente, no possivel
recuperar/imprimir todos os elementos em ordem de chave nem tampouco outras operaes
que exijam sequncia dos dados. Tambm no possvel realizar operaes do tipo
recuperar o elemento com a maior ou a menor chave . Portanto, a insero e a recuperao
ocorrem de forma mais dinmica. Outra desvantagem a impossibilidade de se utilizar
chaves duplicadas, o que um problema quando o valor das chaves pequeno (por
exemplo 1 ou 2 dgitos). [3]
4

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

Em termos de tempo de acesso, a implementao tpica busca uma funo de disperso


que seja de complexidade
O(1), no importando o nmero de registros na tabela
(desconsiderando colises). O ganho com relao a outras estruturas associativas (como um
vetor simples) passa a ser maior conforme a quantidade de dados aumenta. A tabela pode
ser implementada de duas formas: utilizando vetor simples ou vetor de listas.

4. Funes de hashing
A funo de espalhamento ou funo de disperso a responsvel por gerar um ndice
a partir de determinada chave. O ideal para a funo de hashing que sejam sempre
fornecidos ndices nicos para as chaves de entrada. A funo perfeita seria a que, para
quaisquer entradas A e B, sendo A diferente de B, fornecesse sadas diferentes. Quando as
entradas A e B so diferentes e, passando pela funo de espalhamento, geram a mesma
sada, acontece o que chamamos de coliso.
Na prtica, funes de hashing perfeitas ou quase perfeitas so encontradas apenas
onde a coliso intolervel (por exemplo, nas funes de disperso da criptografia). Nas
tabelas de disperso comuns, a coliso apenas indesejvel, diminuindo o desempenho do
sistema.
Segundo Alan L.Tharp, as funes de hashing existentes so:
Key mod N
Key mod P
Truncagem
Folding ou Desdobramento
Squaring ou Meio do Quadrado
Converso Radix
Hashing Polinomial
As funes mdulo tambm so conhecidas como mtodo do resto da diviso (mtodo
da congruncia linear), ou simplesmente mtodo da diviso. As funes de quadratura e
converso radix tambm so conhecidas como mtodo da multiplicao (mtodo da
congruncia linear).
5

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

Existe uma grande variedade de funes hash, sendo classificadas em diferentes tipos,
a citar: hashing dinmico, hashing criptogrfico, hashing geomtrico, hashing robusto,
hashing bloom e string hashing. [6] Em
Choosing Best Hashing Strategies and Hash
Functions, Mahima Singh e Deepak Garg fornecem um estudo sobre qual tipo de hashing
deve ser escolhido, de acordo com as necessidades especficas de cada tipo de problema.

5. Colises
Conforme explicado anteriormente, quando a funo de disperso retorna o mesmo
ndice para duas chaves diferentes, temos uma situao chamada coliso. Por conta disso, a
funo deve ser projetada para evitar ao mximo a ocorrncia de colises, no entanto, por
mais bem projetada que seja a funo de disperso, sempre existir a possibilidade de
ocorrer colises o que extremamente indesejado em determinadas aplicaes,
principalmente no hashing criptogrfico.
A razo pela qual til estar ciente de uma variedade de funes hash que para um
dado conjunto de dados, uma funo hash pode distribuir as chaves mais uniformemente
sobre o espao de endereo do que outra. Quanto menor for o nmero de colises, menor o
nmero de vezes que ser necessrio procurar em outro lugar pelo registro desejado, o que
por sua vez ir manter o nmero mdio de sondas ou acessos perto de um aumentando
assim a eficiencia do algoritmo.
Um mtodo para reduzir colises mudar a funo hashing. Outro mtodo reduzir o
packing factor (fator de enchimento, densidade ou carga) do arquivo. O packing factor de
um arquivo a razo entre o nmero de registros armazenados e o tamanho do arquivo. A
desvantagem de diminuir o fator de enchimento para reduzir o nmero de colises que um
fator inferior toma mais espao para guardar o mesmo nmero de registos. [1]
Embora alterar a funo hash ou diminuir o fator de enchimento possa reduzir o
nmero de colises, geralmente no pode elimin-lo. Quando uma coliso ocorre,
precisamos de um procedimento para posicionar as chaves idnticas em locais diferentes,
de modo a evitar a perda de informaes por sobescrita. U m bom mtodo de resoluo de
colises essencial, no importando a qualidade da funo de espalhamento.

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

Segundo Alan L.Tharp, os mtodos de tratamento de coliso so classificados como:


Resoluo de coliso com links (com ponteiros)
Resoluo de coliso sem links (sem ponteiros)
Posicionamento esttico de registros
Posicionamento dinmico de registros
Resoluo de coliso com pseudolinks
A implementao com ponteiros, tambm conhecida como Hashing Aberto ou
Encadeamento Separado (Separate Chainning Hashing), a mais comum atualmente, sendo
mais intuitiva para ns, que usamos linguagens que lidam com ponteiros e estamos
acostumados com a ideia. Na poca do COBOL, era o modo menos comum.
Como
estrutura, pode ser utilizada uma lista encadeada ou rvore. [3]
Como desvantagem do uso de ponteiros, temos o gasto com o espao de
armazenamento para os campos de ligao, em adio ao necessrio para o registro em si.
Para poucos registros isto no deve ser um problema, mas para milhares de registros, este
espao adicional pode ser fisicamene e/ou monetariamente invivel.
Na implentao sem o uso de ponteiros, utiliza-se uma convenso ou um conjunto de
regras para decidir onde inserir os sinnimos. Ao invs de se procurar o prximo endereo
da lista, faz-se um clculo do novo endereo atravs do conjunto de regras. A desvantagem
de no utilizar ponteiros que isso pode gerar uma maior quantidade de tentativas no
momento da recuperao do arquivo de interesse, o que diminui a performance. Para
situaes especficas, determinado mtodo pode ser prefervel. [1]

6. Anlise de Eficincia
Nessa sesso, analisaremos a eficincia de distintas funes voltadas para a aplicao
com strings. Para tanto, utilizaremos como chaves uma lista com os 100 nomes mais
utilizados no Brasil em 2014, segundo o site BabyCenter. Em relao s funes hash,
analisaremos as implementaes mais populares, listadas na tabela abaixo, traando um
paralelo (quando possvel) com a classificao utilizada por Alan Tharp discriminada na
tabela, na sesso Tipo.

Prof. Renato Mauro


Nome
Mtodo de Horner
Aditivo
XOR
Shift-add-XOR
K&R (Lose lose)
Berstein (djb2)
Sdbm
One-at-a-time (Jenkins)
Java hashCode()
Larson

Disciplina: Organizao de Estruturas de Arquivos


Tipo
Key mod N / Key mod P
Folding

Operao
Mdulo
Adio
XOR
Shift / Adio / XOR
Adio
Shift / Adio
Adio / Shift / Subtrao
XOR / Adio
Produto / Potenciao
Produto / Adio

Tabela 1: Descrio das funes hash analisadas

Mtodo de Horner
Em anlise numrica , o Mtodo de Horner (tambm conhecido como algoritmo de
Horner, esquema de Horner ou, ainda, multiplicao alinhada ), em homenagem a William
George Horner , um algoritmo eficiente para a avaliao dos
polinmios na forma
monmial. O mtodo de Horner descreve um processo manual, atravs do qual pode-se
aproximar as razes de uma equao polinomial, e tambm pode ser visto como um
algoritmo rpido para dividir um polinmio por um polinmio linear com a regra de
Ruffini. [7]
Para implement-lo, consideramos c ada caractere da string como um nmero entre 0 e
255, segundo a tabela ASCII. Suponha que s uma cadeia de comprimento 2. Ento o
nmero correspondente : s[0] * 256 + s[1]. Por exemplo, se s "AB" ento o nmero
correspondente 65 * 256 + 66, ou seja, 16706. [6] Abaixo a funo implementada em C.
unsigned int horner_hash(unsigned char *v) {
unsigned int i, M = 100, hash = v[0];
for (i = 1; v[i] != '\0'; i++)
hash = (hash * 256 + v[i]) % M;
return hash;
}

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

Na funo, foi utilizado os tipos unsigned int e unsigned char para impedir resultados
negativos. Primeiro, passa-se o valor da string (char *v) na funo e converte-se a mesma
para seu valor segundo a tabela ASCII, atravs do loop for. Este valor ento dividido pela
varivel M, e o resto da diviso o hash da chave fornecida. Como utilizamos 100
variveis, parece lgico fazermos M assumir este valor, o que nos d uma funo Key mod
N. Nessas condies, o total de colises foi 65. Ao escolhermos o primeiro nmero primo
maior que a tabela, ou seja, o nmero 101, passamos a ter uma funo Key mod P, o que
reduz o nmero de colises para 59.
No obstante, a base da representao no precisa ser igual ao nmero 256, de valores
possveis de cada caractere. Para que o espalhamento seja melhor, recomenda-se usar um
nmero primo como base. [6] Repetindo o teste com o nmero 251, observamos apenas 52
colises com M valendo 100 e, curiosamente, 59 colises com M valendo 101.
Como podemos observar, o nmero utilizado para a representao parece ter maior
relevncia na funo, em termos de eficincia no espalhamento, do que o nmero utilizado
para representar o tamanho da tabela. Assim sendo, realizamos mais um teste, seguindo a
recomendao clssica de utilizar uma tabela 30% maior que o total de objetos (chaves).
Nesse caso, M passou a valer 130 (funo Key mod N), e o nmero 251 foi mantido, o que
gerou 52 colises. Repetindo o teste com M valendo 131 (funo Key mod P), observamos
uma reduo para 45 colises o que comprova a igual importncia do tamanho da tabela
hash. Abaixo, a verso mais eficiente de todas, com M sendo 30% maior e primo, e
utilizando-se o nmero 251 como base da representao.
unsigned int horner_hash(unsigned char *v) {
unsigned int i, M = 131, hash = v[0];
for (i = 1; v[i] != '\0'; i++)
hash = (hash * 251 + v[i]) % M;
return hash;
}

Aditivo
Outro mtodo igualmente popular e at mais simples de implementar, o mtodo
aditivo. Essa funo, implementada abaixo, simplesmente soma o nmero de caracteres de

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

cada string. Alm de simples, a funo se mostrou bastante eficiente, reduzindo para 39 o
nmero colises.
unsigned int add_hash (unsigned char *v) {
unsigned int hash = 0;
while(*v) hash += *v++;
return hash;
}

XOR
Esta funo aplica lgica binria (bitwise operation), atravs do operador binrio XOR,
em cada caractere da string. Observamos um total de 55 colises. Nota-se que o valor de
hash gerado nunca ser maior do que 255 com este mtodo. [8] Portanto, esta funo uma
m escolha para grandes tabelas de hash. Abaixo a implementao.
unsigned int xor_hash (unsigned char *v) {
unsigned int hash = 0;
while(*v) hash ^= *v++;
return hash;
}

Shift-add-XOR
Esta funo combina as duas funes anteriores (aditiva e XOR) com uma operao de
deslocamento, o que nos proporcionou uma reduo para apenas 28 colises.
unsigned int sax_hash (unsigned char *v) {
unsigned int hash = 0;
while(*v) hash ^= (hash << 5) + (hash >> 2) + *v++;
return hash;
}

Lose lose (K&R)


Essa funo hash apareceu no livro The C Programming Language , escrito por Brian
Kernighan e Dennis Ritchie, no qual eles avisam: "Este no o melhor algoritmo possvel,
mas tem o mrito da extrema simplicidade."[4] No teste, ocorreu um total de 39 colises.

10

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

unsigned int hash(unsigned char *v) {


unsigned int hash = 0;
int c;
while (c = *v++) hash += c;
return hash;
}

Berstein (djb2)
Essa funo hash foi criada por Daniel J. Berstein. No teste, ocorreram 28 colises.
unsigned long hash(unsigned char *v) {
unsigned long hash = 5381;
int c;
while (c = *v++)
hash = ((hash << 5) + hash) + c; /* hash = hash * 33 + c
*/
return hash;
}

Sdbm
Este algoritmo foi criado para a biblioteca de banco de dados sdbm (a reimplementao
de domnio pblico do ndbm, de Berkeley). A funo real hash(i) = hash(i - 1) * 65599 +
v[i]. O que est includo abaixo a verso mais rpida, usada no Gawk (GNU Awk, verso
livre da linguagem de processamento de texto AWK). No teste, ocorreram 28 colises.
unsigned long hash(unsigned char *v) {
unsigned long hash = 0;
int c;
while (c = *v++)
hash = c + (hash << 6) + (hash << 16) - hash;
return hash;
}

11

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

One-at-a-time (Jenkins)
As funes hash Jenkins so uma coleo de funes hash no- criptogrficas criadas
por Bob Jenkins. A primeira delas foi formalmente publicada em 1997. A implementao
padro da linguagem de programao Perl inclui as funes hash One-at-Time (utilizada
como padro) e SipHash, ambas de Jenkins. No teste, ocorreram 28 colises.
unsigned long hash(unsigned char *v, unsigned long len) {
unsigned long hash, i;
for(hash = i = 0; i < len; ++i) {
hash += v[i];
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}

Java hashCode()
Na linguagem Java, o cdigo de hash para uma string calculado como: s [0] * 31 ^
(n-1) + s [1] * 31 ^ (n - 2) + ... + s [n - 1], onde s [i] o caractere i da string, n o
comprimento da string, e ^ indica exponenciao. ( O valor de hash de uma string vazia
zero). Abaixo uma verso dessa funo, implementada em C. O total de colises foi de 29.
#include <math.h>
unsigned int hash (unsigned char *v, unsigned int len) {
unsigned int hash = 0, i;
for(i = 0; i < len; ++i)
hash += v[i] * pow(31,i);
return hash;
}

12

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

Larson
Esta funo leva o nome de seu criador,
Paul Larson, da Microsoft Research. Ele
investigou uma srie de funes hash em uma variedade de conjuntos de dados e descobriu
que uma multiplicao simples por 101 apresenta bons resultados. No teste, ocorreram 29
colises.
unsigned int hash(unsigned char* v) {
unsigned int hash = 0;
while (*v)
hash = hash * 101 + *v++;
return hash;
}

7. Concluses Finais
Atravs desse estudo pudemos comparar a eficincia de algumas das principais funes
utilizadas para string hashing. Em virtude da existncia de diversas pesquisas sobre o
assunto, incluindo comparaes empricas envolvendo um conjunto maior e mais variado
de funes e datasets, esse estudo buscou apenas focar nas implementaes mais simples,
as quais certamente poderiam ser utilizadas por alunos de graduao.
Durante os testes, podemos comprovar que determinadas funes clssicas, tidas como
referncia, muitas das vezes no apresentam a eficincia necessria/esperada. Assim sendo,
a maior conquista desse estudo, se que podemos dizer assim, que nos proporciona a
importncia de se pesquisar e estudar constantemente, visto a grande variedade de solues
existentes para os problemas da computao. Ainda mais importante, esse estudo nos
mostra a importncia de se testar a eficincia de diferentes implementaes possveis, antes
de aceitar alguma soluo como a nica correta, ignorando os problemas que essa deciso
pode acarretar no desenvolvimento de um software.
No total, foram testadas 10 funes de hash. Como resultado, as funes que tiveram o
pior desempenho foram o mtodo XOR e o mtodo de Horner (diviso), tendo o primeiro
registrado 55 colises e o segundo 45 (no melhor caso). Em seguida, tivemos o mtodo da
adio e o algoritmo de K&R, ambos empatados com 39 colises. Com desempenho
melhor temos a implementao em C do Java hashCode() e o algoritmo de Larson, ambos

13

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

com 29 colises. As outras 4 funes analisadas (Shift-add-XOR, djb2, sdbm e one-at-atime) apresentaram desempenho idntico, todas contabilizando 28 colises.
Como consideraes finais, importante frisar que um maior conjunto de dados, bem
como tipos de dados diferentes (por exemplo texto de livros, lista de palavras comuns,
nomes de variveis, entre outros) produziria uma anlise muito mais precisa sobre a
eficincia das funes em questo. Para um estudo mais detalhado, ver Hash Functions: Na
Empirical Comparison , por Peter Kankowski. [9] Nessa pesquisa, o autor realiza a
comparao de um conjunto maior de funes, utilizando um dataset maior e mais
diversificado, alm de arquiteturas diferentes de processadores, de modo a comparar a
velocidade de cada funo.

14

Prof. Renato Mauro

Disciplina: Organizao de Estruturas de Arquivos

8. Bibliografia
[1] THARP, Alan L.
File Organization and Processing
University: Wiley, 1988. 416 p.

, 1 ed. North Carolina State

[2] BURTCH , Ken O. Scripts de Shell Linux com Bash


Moderna, 2005. 522 p.

. 1 ed. Rio de Janeiro: Cincia

[3] VON WANGENHEIM, Aldo. Estruturas de Dados - T.332: Hashing. 1997. Disponvel
em: <http://www.inf.ufsc.br/~ine5384-hp/Hashing/>. Acesso em: 13 jun. 2015.
[4] Kernighan, Brian W.; Dennis M. Ritchie. The C Programming Language.
Englewood Cliffs, NJ: Prentice Hall, 1978. 228 p.

1 ed.

[5] SINGH, Mahima; GARG, Deepak.


Choosing Best Hashing Strategies and Hash
Functions. IEEE International Advance Computing Conference, Patiala, ndia, 2009.
[6] FEOFILOFF, Paulo. MAC0122 Princpios de Desenvolvimento de Algoritmos:
Tabelas de disperso (hash tables). 2002. Disponvel em:
<http://www.ime.usp.br/~pf/mac0122-2002/aulas/hashing.html>. Acesso em: 14 jun. 2015.
[7] Esquema de Horner. In: Wikipdia: a enciclopdia livre. Disponvel em:
<https://pt.wikipedia.org/wiki/Esquema_de_Horner>. Acesso em: 22 jun. 2015.
[8] Hash function comparison (C, sh). In: Literate Programs. Disponvel em:
<http://en.literateprograms.org/Hash_function_comparison_(C,_sh)#XOR>. Acesso em: 23
jun. 2015.
[9] KANKOWSKI, Peter. Hash functions: An empirical comparison.
<http://www.strchr.com/hash_functions>. Acesso em: 20 jun. 2015.

Disponvel em:

15

Você também pode gostar