Я випробував кілька різних алгоритмів, вимірюючи швидкість та кількість зіткнень.
Я використовував три різні набори ключів:
Для кожного корпусу реєстрували кількість зіткнень та середній витрачений час хешування.
Я тестував:
Результати
Кожен результат містить середній час хешу та кількість зіткнень
Hash Lowercase Random UUID Numbers
============= ============= =========== ==============
Murmur 145 ns 259 ns 92 ns
6 collis 5 collis 0 collis
FNV-1a 152 ns 504 ns 86 ns
4 collis 4 collis 0 collis
FNV-1 184 ns 730 ns 92 ns
1 collis 5 collis 0 collis▪
DBJ2a 158 ns 443 ns 91 ns
5 collis 6 collis 0 collis▪▪▪
DJB2 156 ns 437 ns 93 ns
7 collis 6 collis 0 collis▪▪▪
SDBM 148 ns 484 ns 90 ns
4 collis 6 collis 0 collis**
SuperFastHash 164 ns 344 ns 118 ns
85 collis 4 collis 18742 collis
CRC32 250 ns 946 ns 130 ns
2 collis 0 collis 0 collis
LoseLose 338 ns - -
215178 collis
Примітки :
Чи трапляються насправді колізії?
Так. Я почав писати свою тестову програму, щоб побачити, чи справді трапляються хеш-колізії - і це не просто теоретична конструкція. Вони справді бувають:
Зіткнення FNV-1
creamwove
зіткнення с quists
Зіткнення FNV-1a
costarring
зіткнення с liquid
declinate
зіткнення с macallums
altarage
зіткнення с zinke
altarages
зіткнення с zinkes
Сутички Murmur2
cataract
зіткнення с periti
roquette
зіткнення с skivie
shawl
зіткнення с stormbound
dowlases
зіткнення с tramontane
cricketings
зіткнення с twanger
longans
зіткнення с whigs
Зіткнення DJB2
hetairas
зіткнення с mentioner
heliotropes
зіткнення с neurospora
depravement
зіткнення с serafins
stylist
зіткнення с subgenera
joyful
зіткнення с synaphea
redescribed
зіткнення с urites
dram
зіткнення с vivency
Зіткнення DJB2a
haggadot
зіткнення с loathsomenesses
adorablenesses
зіткнення с rentability
playwright
зіткнення с snush
playwrighting
зіткнення с snushing
treponematoses
зіткнення с waterbeds
Зіткнення CRC32
codding
зіткнення с gnu
exhibiters
зіткнення с schlager
Сутички SuperFastHash
dahabiah
зіткнення с drapability
encharm
зіткнення с enclave
grahams
зіткнення с gramary
- ... чиніть 79 зіткнень ...
night
зіткнення с vigil
nights
зіткнення с vigils
finks
зіткнення с vinic
Випадковість
Інший суб'єктивний захід полягає в тому, наскільки випадковим чином розподілені хеші. Картографування отриманих HashTables показує, наскільки рівномірно розподіляються дані. Усі хеш-функції показують хороший розподіл при лінійному зіставленні таблиці:
Або як карта Гільберта ( XKCD завжди актуальна ):
Крім випадків , коли хешування число рядків ( "1"
, "2"
, ..., "216553"
) (наприклад, поштові індекси ), де моделі починають з'являтися в більшості алгоритмів хешування:
SDBM :
DJB2a :
FNV-1 :
Усі, крім FNV-1a , які все ще виглядають для мене досить випадково:
Насправді, у Мурмура2 є навіть краща випадковість, Numbers
ніж FNV-1a
:
Коли я дивлюсь на FNV-1a
карту «цифр», я думаю, що бачу тонкі вертикальні візерунки. З Мурмуром я взагалі не бачу зразків. Що ти думаєш?
Додаткове *
в таблиці позначає, наскільки погана випадковість. З FNV-1a
найкращими та DJB2x
найгіршими:
Murmur2: .
FNV-1a: .
FNV-1: ▪
DJB2: ▪▪
DJB2a: ▪▪
SDBM: ▪▪▪
SuperFastHash: .
CRC: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
Loselose: ▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪
▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪▪
Я спочатку написав цю програму, щоб вирішити, чи потрібно мені навіть хвилюватися через зіткнення: так.
А потім це перетворилося на переконання, що хеш-функції були досить випадковими.
Алгоритм FNV-1a
Хеш FNV1 поставляється у варіантах, які повертають хеші 32, 64, 128, 256, 512 та 1024 біт.
Алгоритм FNV-1a :
hash = FNV_offset_basis
for each octetOfData to be hashed
hash = hash xor octetOfData
hash = hash * FNV_prime
return hash
Де постійні FNV_offset_basis
і FNV_prime
залежать від потрібного розміру хеша повернення:
Hash Size
===========
32-bit
prime: 2^24 + 2^8 + 0x93 = 16777619
offset: 2166136261
64-bit
prime: 2^40 + 2^8 + 0xb3 = 1099511628211
offset: 14695981039346656037
128-bit
prime: 2^88 + 2^8 + 0x3b = 309485009821345068724781371
offset: 144066263297769815596495629667062367629
256-bit
prime: 2^168 + 2^8 + 0x63 = 374144419156711147060143317175368453031918731002211
offset: 100029257958052580907070968620625704837092796014241193945225284501741471925557
512-bit
prime: 2^344 + 2^8 + 0x57 = 35835915874844867368919076489095108449946327955754392558399825615420669938882575126094039892345713852759
offset: 9659303129496669498009435400716310466090418745672637896108374329434462657994582932197716438449813051892206539805784495328239340083876191928701583869517785
1024-bit
prime: 2^680 + 2^8 + 0x8d = 5016456510113118655434598811035278955030765345404790744303017523831112055108147451509157692220295382716162651878526895249385292291816524375083746691371804094271873160484737966720260389217684476157468082573
offset: 1419779506494762106872207064140321832088062279544193396087847491461758272325229673230371772250864096521202355549365628174669108571814760471015076148029755969804077320157692458563003215304957150157403644460363550505412711285966361610267868082893823963790439336411086884584107735010676915
Детальну інформацію див. На головній сторінці FNV .
Усі мої результати з 32-бітовим варіантом.
FNV-1 краще, ніж FNV-1a?
Ні. FNV-1a все навколо краще. Було більше зіткнень із FNV-1a при використанні англійського слова corpus:
Hash Word Collisions
====== ===============
FNV-1 1
FNV-1a 4
Тепер порівняйте малі та великі літери:
Hash lowercase word Collisions UPPERCASE word collisions
====== ========================= =========================
FNV-1 1 9
FNV-1a 4 11
У цьому випадку FNV-1a не на "400%" гірший, ніж FN-1, лише на 20%.
Я вважаю, що найважливішим виводом є те, що існує два класи алгоритмів, коли мова йде про зіткнення:
- зіткнення рідкісні : FNV-1, FNV-1a, DJB2, DJB2a, SDBM
- зіткнення поширені : SuperFastHash, Loselose
І ось, наскільки рівномірно розподілені хеші:
- видатний розподіл: Murmur2, FNV-1a, SuperFastHas
- відмінний розподіл: FNV-1
- хороший розподіл: SDBM, DJB2, DJB2a
- жахливий розподіл: Loselose
Оновлення
Шум? Звісно, чому б ні
Оновлення
@whatshisname цікаво, як буде працювати CRC32 , додавши цифри до таблиці.
CRC32 досить непоганий . Кілька зіткнень, але повільніше, та накладніші результати пошуку 1 к.
Обрізати всі помилкові речі про розповсюдження CRC - це моє погано
До сьогоднішнього дня я збирався використовувати FNV-1a як мій алгоритм хешування хеш-таблиць де-факто . Але тепер я переходжу на Murmur2:
- Швидше
- Краща випадкова класифікація всіх класів введення
І я дуже, дуже сподіваюсь, що з SuperFastHash
алгоритмом, який я знайшов , щось не так ; це дуже погано, щоб бути такою ж популярною.
Оновлення: З домашньої сторінки MurmurHash3 на Google :
(1) - SuperFastHash має дуже погані колізійні властивості, що було зафіксовано в інших місцях.
Тож я гадаю, що це не лише я.
Оновлення: я зрозумів, чому Murmur
швидше за інших. MurmurHash2 працює одночасно по чотири байти. Більшість алгоритмів - байт :
for each octet in Key
AddTheOctetToTheHash
Це означає, що в міру збільшення клавіш Мурмур отримує шанс світити.
Оновлення
Вчасний пост Реймонда Чена ще раз підтверджує той факт, що "випадкові" GUID не призначені для використання для їх випадковості. Вони або їх підмножина непридатні як хеш-ключ:
Навіть алгоритм GUID версії 4 не гарантується непередбачуваним, оскільки алгоритм не визначає якість генератора випадкових чисел. Стаття Вікіпедії для GUID містить первинні дослідження, які дозволяють передбачити, що майбутні та попередні GUID можна прогнозувати на основі знань про стан генератора випадкових чисел, оскільки генератор не є криптографічно сильним.
Randomess - це не те, що уникнення зіткнення; саме тому було б помилкою спробувати винайти власний алгоритм "хешування", взявши якийсь підмножину "випадкових" настанов:
int HashKeyFromGuid(Guid type4uuid)
{
//A "4" is put somewhere in the GUID.
//I can't remember exactly where, but it doesn't matter for
//the illustrative purposes of this pseudocode
int guidVersion = ((type4uuid.D3 & 0x0f00) >> 8);
Assert(guidVersion == 4);
return (int)GetFirstFourBytesOfGuid(type4uuid);
}
Примітка . Знову я ставлю "випадковий GUID" у лапки, тому що це "випадковий" варіант GUID. Більш точний опис був би Type 4 UUID
. Але ніхто не знає, що таке тип 4, або типи 1, 3 і 5. Тож їх просто простіше назвати "випадковими" GUID.
Усі дзеркала англійських слів