Qual algoritmo de hash é melhor para exclusividade e velocidade? Exemplos de usos (bons) incluem dicionários de hash.
Eu sei que existem coisas como SHA-256 e outras, mas esses algoritmos são projetados para serem seguros , o que geralmente significa que eles são mais lentos que os algoritmos menos exclusivos . Eu quero um algoritmo de hash projetado para ser rápido, mas ainda assim ser único para evitar colisões.
algorithms
hashing
Earlz
fonte
fonte
Respostas:
Testei alguns algoritmos diferentes, medindo a velocidade e o número de colisões.
Eu usei três conjuntos de chaves diferentes:
"1"
para"216553"
(pense em códigos postais e como um hash ruim derrubou o msn.com )Para cada corpus, foi registrado o número de colisões e o tempo médio gasto em hash.
Eu testei:
xor
vez de+
)Resultados
Cada resultado contém o tempo médio de hash e o número de colisões
Notas :
As colisões realmente acontecem?
Sim. Comecei a escrever meu programa de teste para ver se as colisões de hash realmente acontecem - e não são apenas uma construção teórica. Eles realmente acontecem:
Colisões FNV-1
creamwove
colide comquists
Colisões FNV-1a
costarring
colide comliquid
declinate
colide commacallums
altarage
colide comzinke
altarages
colide comzinkes
Colisões Murmur2
cataract
colide comperiti
roquette
colide comskivie
shawl
colide comstormbound
dowlases
colide comtramontane
cricketings
colide comtwanger
longans
colide comwhigs
Colisões DJB2
hetairas
colide commentioner
heliotropes
colide comneurospora
depravement
colide comserafins
stylist
colide comsubgenera
joyful
colide comsynaphea
redescribed
colide comurites
dram
colide comvivency
Colisões DJB2a
haggadot
colide comloathsomenesses
adorablenesses
colide comrentability
playwright
colide comsnush
playwrighting
colide comsnushing
treponematoses
colide comwaterbeds
Colisões CRC32
codding
colide comgnu
exhibiters
colide comschlager
Colisões SuperFastHash
dahabiah
colide comdrapability
encharm
colide comenclave
grahams
colide comgramary
night
colide comvigil
nights
colide comvigils
finks
colide comvinic
Randomnessification
A outra medida subjetiva é a distribuição aleatória dos hashes. O mapeamento das HashTables resultantes mostra a distribuição uniforme dos dados. Todas as funções hash mostram boa distribuição ao mapear linearmente a tabela:
Ou como um mapa de Hilbert (o XKCD é sempre relevante ):
Exceto quando hash cadeias numéricas (
"1"
,"2"
, ...,"216553"
) (por exemplo, zip codes ), onde os padrões começam a surgir na maioria dos algoritmos de hash:SDBM :
DJB2a :
FNV-1 :
Todos, exceto o FNV-1a , que ainda parecem bastante aleatórios para mim:
Na verdade, Murmur2 parece ter ainda melhor aleatoriedade com
Numbers
queFNV-1a
:O extra
*
na tabela indica quão ruim é a aleatoriedade. ComFNV-1a
sendo o melhor eDJB2x
ser o pior:Originalmente, escrevi este programa para decidir se eu precisava me preocupar com colisões: sim.
E, em seguida, tornou-se garantir que as funções de hash fossem suficientemente aleatórias.
Algoritmo FNV-1a
O hash FNV1 vem em variantes que retornam hashes de 32, 64, 128, 256, 512 e 1024 bits.
O algoritmo FNV-1a é:
Onde as constantes
FNV_offset_basis
eFNV_prime
dependem do tamanho do hash de retorno desejado:Veja a página principal do FNV para detalhes.
Todos os meus resultados estão com a variante de 32 bits.
FNV-1 melhor que FNV-1a?
Não. O FNV-1a está bem melhor. Houve mais colisões com o FNV-1a ao usar a palavra em inglês corpus:
Agora compare letras minúsculas e maiúsculas:
Nesse caso, o FNV-1a não é "400%" pior que o FN-1, apenas 20% pior.
Penso que o mais importante é que existem duas classes de algoritmos quando se trata de colisões:
E ainda há a distribuição uniforme dos hashes:
Atualizar
Murmúrio? Claro, por que não
Atualizar
@whatshisname imaginou como seria o desempenho de um CRC32 , acrescentando números à tabela.
CRC32 é muito bom . Poucas colisões, porém mais lentas, e a sobrecarga de uma tabela de pesquisa de 1k.
Cortar todas as coisas erradas sobre a distribuição de CRC - meu mal
Até hoje eu usava o FNV-1a como meu algoritmo de hash de tabela de hash de fato . Mas agora estou mudando para o Murmur2:
E eu realmente, realmente espero que haja algo de errado com o
SuperFastHash
algoritmo que eu encontrei ; é muito ruim ser tão popular quanto é.Update: A partir da página inicial MurmurHash3 no Google :
Então acho que não sou só eu.
Atualização: eu percebi por que
Murmur
é mais rápido que os outros. MurmurHash2 opera em quatro bytes de cada vez. A maioria dos algoritmos é byte a byte :Isso significa que, à medida que as teclas ficam mais longas, o Murmur tem a chance de brilhar.
Atualizar
Os GUIDs são projetados para serem exclusivos, não aleatórios
Uma publicação oportuna de Raymond Chen reitera o fato de que os GUIDs "aleatórios" não devem ser usados por sua aleatoriedade. Eles, ou um subconjunto deles, não são adequados como chave de hash:
Aleatoriedade não é o mesmo que evitar colisões; é por isso que seria um erro tentar inventar seu próprio algoritmo de "hash" usando um subconjunto de um guia "aleatório":
Nota : Mais uma vez, coloquei "GUID aleatório" entre aspas, porque é a variante "aleatória" de GUIDs. Uma descrição mais precisa seria
Type 4 UUID
. Mas ninguém sabe o que são os tipos 4 ou 1, 3 e 5. Portanto, é mais fácil chamá-los de GUIDs "aleatórios".Todas as palavras em inglês mirrors
fonte
Se você deseja criar um mapa de hash a partir de um dicionário imutável, considere https://en.wikipedia.org/wiki/Perfect_hash_function - durante a construção da função e da tabela de hash, você pode garantir, para um determinado conjunto de dados, que não haverá colisões.
fonte
Aqui está uma lista de funções de hash, mas a versão curta é:
fonte
O CityHash do Google é o algoritmo que você está procurando. Não é bom para criptografia, mas é bom para gerar hashes exclusivos.
Leia o blog para mais detalhes e o código está disponível aqui .
CityHash é escrito em C ++. Também há uma porta C simples .
Sobre o suporte de 32 bits:
fonte
plain C port
link está quebradoPlotamos uma comparação rápida de velocidade de diferentes algoritmos de hash ao fazer o hash de arquivos.
Os gráficos individuais diferem apenas ligeiramente no método de leitura e podem ser ignorados aqui, pois todos os arquivos foram armazenados em um tmpfs. Portanto, a referência não era vinculada a IO se você está se perguntando.
Algoritmos incluem:
SpookyHash, CityHash, Murmur3, MD5, SHA{1,256,512}
.Conclusões:
CRC
instruções SSE 4.2s , que minha CPU não possui. O SpookyHash estava no meu caso sempre um pouquinho antes do CityHash.A fonte usada para as parcelas:
fonte
Os algoritmos SHA (incluindo SHA-256) foram projetados para serem rápidos .
De fato, sua velocidade pode ser um problema às vezes. Em particular, uma técnica comum para armazenar um token derivado de senha é executar um algoritmo de hash rápido padrão 10.000 vezes (armazenando o hash do hash do hash do hash da senha ...).
Resultado:
fonte
bcrypt
,. Use as ferramentas certas..rodata
e / ou estado. Quando você deseja um algoritmo para uma hashtable, geralmente possui teclas muito curtas e muitas delas, mas não precisa das garantias adicionais de uma criptografia. Eu mesmo uso uma Jenkins ajustada de uma vez.A suposição de que as funções de hash criptográfico são mais exclusivas está errada e, de fato, pode ser demonstrado que, na prática, muitas vezes é invertido. Em verdade:
O que significa que uma função hash não criptográfica pode ter menos colisões que uma função criptográfica para um conjunto de dados "bom" - conjuntos de dados para os quais foi projetado.
Podemos demonstrar isso com os dados da resposta de Ian Boyd e um pouco de matemática: o problema do aniversário . A fórmula para o número esperado de pares em colisão, se você escolher
n
números inteiros aleatoriamente do conjunto,[1, d]
é esta (retirada da Wikipedia):Ao
n
ligar = 216.553 ed
= 2 ^ 32, obtemos cerca de 5,5 colisões esperadas . Os testes de Ian mostram principalmente resultados em torno desse bairro, mas com uma exceção dramática: a maioria das funções teve zero colisão nos testes consecutivos de números. A probabilidade de escolher 216.553 números de 32 bits aleatoriamente e obter zero colisão é de cerca de 0,43%. E isso é apenas para uma função - aqui temos cinco famílias distintas de funções de hash com zero colisão!Então, o que estamos vendo aqui é que os hashes que Ian testou estão interagindo favoravelmente com o conjunto de dados de números consecutivos - ou seja, estão dispersando entradas minimamente diferentes mais amplamente do que uma função de hash criptográfica ideal. (Observação: isso significa que a avaliação gráfica de Ian de que o FNV-1a e o MurmurHash2 "parecem aleatórios" para ele no conjunto de dados de números pode ser refutada de seus próprios dados. Zero colisão em um conjunto de dados desse tamanho, para ambas as funções de hash, é surpreendentemente não-aleatório!)
Isso não é uma surpresa, pois esse é um comportamento desejável para muitos usos de funções de hash. Por exemplo, chaves de tabela de hash geralmente são muito semelhantes; A resposta de Ian menciona um problema que o MSN já teve com tabelas de hash de código postal . Este é um uso em que a prevenção de colisões em entradas prováveis vence o comportamento aleatório.
Outra comparação instrutiva aqui é o contraste nos objetivos de design entre as funções de CRC e hash criptográfico:
Portanto, para a CRC, é novamente bom ter menos colisões do que aleatórias em entradas minimamente diferentes. Com hashes criptográficos, isso é um não-não!
fonte
Use SipHash . Tem muitas propriedades desejáveis:
Rápido. Uma implementação otimizada leva cerca de 1 ciclo por byte.
Seguro. O SipHash é um forte PRF (função pseudo-aleatória). Isso significa que é indistinguível de uma função aleatória (a menos que você conheça a chave secreta de 128 bits). Conseqüentemente:
Não é necessário se preocupar com o fato de as sondas da tabela de hash se tornarem tempo linear devido a colisões. Com o SipHash, você sabe que, em média, obterá um desempenho médio de caso, independentemente das entradas.
Imunidade a ataques de negação de serviço baseados em hash.
Você pode usar o SipHash (especialmente a versão com uma saída de 128 bits) como um MAC (código de autenticação de mensagens). Se você receber uma mensagem e uma tag SipHash, e a tag for a mesma que a da execução do SipHash com sua chave secreta, você saberá que quem criou o hash também possui sua chave secreta e que nem a mensagem nem o hash foram alterados desde então.
fonte
Depende dos dados que você está fazendo o hash. Alguns hash funcionam melhor com dados específicos, como texto. Alguns algoritmos de hash foram projetados especificamente para serem bons para dados específicos.
Paul Hsieh fez uma vez hash rápido . Ele lista o código fonte e explicações. Mas já estava vencido. :)
fonte
Java usa este algoritmo simples de multiplicar e adicionar:
Provavelmente existem muito melhores por aí, mas isso é bastante difundido e parece ser uma boa troca entre velocidade e singularidade.
fonte
Primeiro de tudo, por que você precisa implementar seu próprio hash? Para a maioria das tarefas, você deve obter bons resultados com estruturas de dados de uma biblioteca padrão, supondo que exista uma implementação disponível (a menos que você esteja fazendo isso apenas para sua própria educação).
No que diz respeito aos algoritmos de hash reais, o meu favorito é o FNV. 1
Aqui está um exemplo de implementação da versão de 32 bits em C:
fonte
*
e^
:h = (h * 16777619) ^ p[i]
==>h = (h ^ p[i]) * 16777619