Я випробував кілька різних алгоритмів, вимірюючи швидкість та кількість зіткнень.
Я використовував три різні набори ключів:
Для кожного корпусу реєстрували кількість зіткнень та середній витрачений час хешування.
Я тестував:
Результати
Кожен результат містить середній час хешу та кількість зіткнень
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.
Усі дзеркала англійських слів