Який алгоритм хешування найкращий для унікальності та швидкості?


1388

Який алгоритм хешування найкращий для унікальності та швидкості? Приклад (хорошого) використання включає хеш-словники.

Я знаю, що є такі речі, як SHA-256 і подібні, але ці алгоритми розроблені для захисту , що означає, що вони повільніше, ніж алгоритми, які є менш унікальними . Я хочу, щоб алгоритм хешування був розроблений швидко, але залишався досить унікальним, щоб уникнути зіткнень.


9
З якою метою, безпекою чи іншим?
Орлінг

19
@Orbling, для реалізації хеш-словника. Таким чином, зіткнення слід звести до мінімуму, але це зовсім не має мети безпеки.
Граф

4
Зауважте, що вам потрібно буде очікувати хоча б певних зіткнень у вашій хеш-таблиці, інакше таблиця повинна бути величезною, щоб мати можливість обробляти навіть порівняно невелику кількість клавіш ...
Дін Хардінг,

19
Чудовий пост! Чи можете ви також перевірити xxHash (творця або LZ4) Yann Collet, який вдвічі швидший, ніж Мурмур? Домашня сторінка: code.google.com/p/xxhash Додаткова інформація: fastcompression.blogspot.fr/2012/04 / ...

24
@zvrba Залежить від алгоритму. bcrypt призначений для повільного використання.
Ізката

Відповіді:


2461

Я випробував кілька різних алгоритмів, вимірюючи швидкість та кількість зіткнень.

Я використовував три різні набори ключів:

Для кожного корпусу реєстрували кількість зіткнень та середній витрачений час хешування.

Я тестував:

Результати

Кожен результат містить середній час хешу та кількість зіткнень

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 не призначені для використання для їх випадковості. Вони або їх підмножина непридатні як хеш-ключ:

Навіть алгоритм 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.

Усі дзеркала англійських слів


41
Було б дуже цікаво побачити, як SHA порівнює, не тому, що це хороший кандидат для алгоритму хешування тут, але було б дуже цікаво побачити, як будь-який криптографічний хеш порівнюється з цим, зробленим для алгоритмів швидкості.
Майкл

8
Новий хеш під назвою "xxHash", від Ян Коллет, робив раунди нещодавно. Я завжди підозрюю новий хеш. Було б цікаво побачити це у вашому порівнянні (якщо вам не набридло людям, які пропонують додати випадкові хеші, про які вони чули ...)
th_in_gs

7
Справді. Номери продуктивності, оголошені на сторінці проекту xxHash, виглядають вражаючими, можливо, занадто великими, щоб бути правдою. Ну, принаймні, це проект з відкритим кодом: code.google.com/p/xxhash
ATTracker

9
Привіт Іне, моя реалізація SuperFastHash Delphi правильна. При впровадженні я створив тестовий набір в C і Delphi для порівняння результатів моєї реалізації та еталонної реалізації. Різниць немає. Отже, те, що ви бачите, - це фактична шкідливість хешу ... (Тому я також опублікував реалізацію MurmurHash : landman-code.blogspot.nl/2009/02/… )
Дейві

19
Чи усвідомлює афіша, що це не просто приголомшлива відповідь - це світовий фактичний довідковий ресурс з цього приводу? Кожен раз, коли мені потрібно мати справу з хешами, це вирішує мою проблему так швидко і авторитетно, що мені більше нічого не потрібно.
MaiaVictor

59

Якщо ви хочете створити хеш-карту з незмінного словника, ви можете розглянути ідеальне хешування https://en.wikipedia.org/wiki/Perfect_hash_function - під час побудови хеш-функції та хеш-таблиці ви можете гарантувати, для даного набору даних, не буде зіткнень.


2
Ось докладніше про (мінімальний) Perfect Hashing burtleburtle.net/bob/hash/perfect.html, включаючи дані про продуктивність, хоча він не використовує найсучасніший процесор тощо.
Еллі Кессельман,

4
Це досить очевидно, але варто зазначити, що для того, щоб гарантувати відсутність зіткнень, ключі повинні мати такий же розмір, як і значення, якщо тільки немає обмежень щодо значень, якими алгоритм може користуватися великою літерою.
devios1

1
@ devios1 Ваше твердження безглуздо. По-перше, значення в хеш-таблиці, ідеальні чи ні, не залежать від ключів. По-друге, досконала хеш-таблиця - це лише лінійний масив значень, індексований результатом функції, яка була створена так, що всі індекси є унікальними.
Джим Балтер

1
@MarcusJ Ідеальний хеш, як правило, використовується з менш ніж 100 клавішами, але подивіться на cmph.sourceforge.net ... все ще далеко від вашого діапазону.
Джим Балтер

1
@DavidCary Ніщо за вашим посиланням не підтримує вашу заяву. Можливо, ви переплутали O (1) з "не зіткненнями", але вони зовсім не те саме. Звичайно, ідеальне хешування гарантує відсутність зіткнень, але для цього потрібно, щоб усі клавіші були заздалегідь відомі і їх було порівняно мало. (Але дивіться посилання на cmph вище.)
Джим Балтер

34

Ось список хеш-функцій, але коротка версія:

Якщо ви просто хочете мати хорошу хеш-функцію і не можете чекати, djb2це одна з найкращих функцій хеш-рядків, яку я знаю. Він має чудовий розподіл і швидкість для багатьох різних наборів клавіш і розмірів столів

unsigned long
hash(unsigned char *str)
{
    unsigned long hash = 5381;
    int c;

    while (c = *str++)
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

    return hash;
}

6
Насправді djb2 чутливий до нуля, як і більшість таких простих хеш-функцій, тому ви можете легко зламати такі хеші. Це погано зміщення занадто багато зіткнень і погане розподілу, він ламає на більшість smhasher випробувань якості: Див github.com/rurban/smhasher/blob/master/doc/bernstein Його база даних CDB використовує його, але я б не використати його з громадським доступом.
рубан

2
DJB досить поганий з точки зору продуктивності та дистрибуції. Я б не користувався цим сьогодні.
Конрад Мейєр

@ConradMeyer Я б заперечував, що DJB може бути прискорений у три рази, як у цьому моєму питанні, і тоді він, мабуть, обіграє більшість корисних алгоритмів. Щодо розповсюдження, я згоден. Хеш-зіткнення навіть для двох літер не може бути дуже хорошим.
maaartinus

28

CityHash від Google - це алгоритм, який ви шукаєте. Це не добре для криптографії, але добре для створення унікальних хешів.

Прочитайте блог для отримання більш детальної інформації, і код доступний тут .

CityHash написаний на C ++. Там також є звичайний порт C .

Про підтримку 32-розрядних програм:

Всі функції CityHash налаштовані на 64-бітні процесори. Однак, вони будуть працювати (крім нових, які використовують SSE4.2) у 32-бітному коді. Хоча вони будуть не дуже швидкими. Ви можете використовувати Murmur або щось інше в 32-бітному коді.


11
Чи вимовляється CityHash схожим на "City Sushi?"
Ерік

2
Подивіться також на SipHash, він призначений замінити MurmurHash / CityHash / тощо. : 131002.net/siphash
Török Edwin

3
Також дивіться FarmHash, спадкоємця CitHash. code.google.com/p/farmhash
stevendaniels

7
xxHash стверджує, що на 5 разів швидше, ніж CityHash.
Глиняні мости

plain C portпосилання розірвано
makerj

20

Я побудував коротке порівняння швидкості різних алгоритмів хешування під час хешування файлів.

Окремі сюжети лише незначно відрізняються за способом читання і тут можна ігнорувати, оскільки всі файли зберігалися у форматі tmpfs. Тому тест не був обмеженим IO, якщо вам цікаво.

Алгоритми включають в себе: SpookyHash, CityHash, Murmur3, MD5, SHA{1,256,512}.

Висновки:

  • Некриптографічні хеш-функції, такі як Murmur3, Cityhash та Spooky, досить близькі між собою. Слід зазначити, що Cityhash може бути швидшим на процесорах з CRCінструкцією SSE 4.2s , якої у мого ЦП немає. SpookyHash в моєму випадку завжди був крихітним перед CityHash.
  • MD5, здається, є хорошим компромісом при використанні криптографічних хеш-функцій, хоча SHA256 може бути більш захищеним від зіткнення вразливості MD5 та SHA1.
  • Складність всіх алгоритмів лінійна - що насправді не дивно, оскільки вони працюють блочно. (Я хотів побачити, чи має значення спосіб читання, тому ви можете просто порівняти найправіші значення).
  • SHA256 був повільніше, ніж SHA512.
  • Я не досліджував випадковість хеш-функцій. Але ось хороше порівняння хеш-функцій, яких немає у відповіді Ієна Бойдса . Це вказує на те, що CityHash має певні проблеми у кутових випадках.

Джерело, яке використовується для сюжетів:


1
Графік лінійної шкали відрізає мітку осі y, яка говорить про те, яку величину будує. Я думаю, це, мабуть, був би "час у секундах", такий же, як логарифмічна шкала. Це варто виправити.
Крейг МакКуін

18

Алгоритми SHA (включаючи SHA-256) розроблені так, щоб вони були швидкими .

Насправді їх швидкість може бути проблемою іноді. Зокрема, поширеною технікою збереження маркеру, отриманого паролем, є запуск стандартного алгоритму швидкого хешу в 10000 разів (зберігання хеша хеша хеша хеша ... пароля).

#!/usr/bin/env ruby
require 'securerandom'
require 'digest'
require 'benchmark'

def run_random_digest(digest, count)
  v = SecureRandom.random_bytes(digest.block_length)
  count.times { v = digest.digest(v) }
  v
end

Benchmark.bmbm do |x|
  x.report { run_random_digest(Digest::SHA256.new, 1_000_000) }
end

Вихід:

Rehearsal ------------------------------------
   1.480000   0.000000   1.480000 (  1.391229)
--------------------------- total: 1.480000sec

       user     system      total        real
   1.400000   0.000000   1.400000 (  1.382016)

57
Це, безумовно, відносно алгоритму криптографічного хешування . Але ОП просто хоче зберігати значення в хеш-таблицю, і я не думаю, що криптографічна хеш-функція дійсно підходить для цього.
Дін Хардінг

6
Питання, що піднімається (дотично, тепер з'являється), є предметом криптографічних хеш-функцій. Це той біт, на який я відповідаю.
yfeldblum

15
Просто для того, щоб відмовити людей від ідеї "Зокрема, поширеною технікою зберігання маркера, отриманого паролем, є запуск стандартного алгоритму швидкого хешу в 10000 разів", - хоча це є загальним, це просто нерозумно. Існують алгоритми , розроблені для цих сценаріїв, наприклад, bcrypt. Використовуйте правильні інструменти.
TC1

3
Криптографічні хеші розроблені таким чином, щоб вони мали високу пропускну спроможність, але це часто означає, що вони мають високі .rodataвитрати на налаштування, вилучення та / або державні витрати. Коли ви хочете алгоритм для хештеля, у вас зазвичай є дуже короткі клавіші, і їх багато, але додаткові гарантії криптографічних даних не потребують. Я сам використовую підкручений Дженкінс один на один.
mirabilos

1
@ChrisMorgan: замість того, щоб використовувати криптографічно захищений хеш, HashTable DoS можна вирішити набагато ефективніше, використовуючи хеш-рандомізацію, так що кожен запуск програми або навіть на кожному хештелі, так що дані не збираються групуватися в одне відро кожного разу .
Лежать Раян

14

Я знаю, що є такі речі, як SHA-256 і подібні, але ці алгоритми розроблені для захисту , що означає, що вони повільніше, ніж алгоритми, які є менш унікальними .

Припущення про те, що криптографічні хеш-функції є більш унікальними, є помилковим, і насправді це може бути показано, що воно на практиці часто відстає. По правді кажучи:

  1. Криптографічні хеш-функції в ідеалі повинні відрізнятися від випадкових ;
  2. Але з некриптографічними хеш-функціями бажано взаємодіяти з ймовірними входами .

Що означає, що некриптографічна хеш-функція може мати меншу кількість зіткнень, ніж криптографічна для "хорошого" набору даних - наборів даних, для яких вона була розроблена.

Насправді ми можемо продемонструвати це за допомогою даних у відповіді Іена Бойда та трохи математики: проблема дня народження . Формула очікуваної кількості стикаються пар, якщо вибирати nцілі числа навмання з набору [1, d], така:

n - d + d * ((d - 1) / d)^n

Підключивши n= 216,553 і d= 2 ^ 32 ми отримуємо близько 5,5 очікуваних зіткнень . Тести Йена в основному показують результати навколо цього мікрорайону, але за одним драматичним винятком: більшість функцій отримали нульові зіткнення в тестах послідовних чисел. Ймовірність вибору випадкових 216,553 32-бітних чисел та отримання нульових зіткнень становить приблизно 0,43%. І це лише для однієї функції - тут у нас є п'ять різних сімейств хеш-функцій з нульовими зіткненнями!

Тож, що ми бачимо тут, це те, що хеші, які тестував Іан, вигідно взаємодіють з набором послідовних чисел, тобто вони розсіюють мінімально різні входи ширше, ніж ідеальна криптографічна хеш-функція. (Бічна примітка: це означає, що графічна оцінка Яна, що FNV-1a та MurmurHash2 "виглядає випадково" для нього в наборі даних про числа, може бути спростована з його власних даних. Нульові зіткнення на наборі даних такого розміру для обох хеш-функцій, разюче невипадково!)

Це не є несподіванкою, оскільки це бажана поведінка для багатьох застосувань хеш-функцій. Наприклад, клавіші хеш-таблиць часто дуже схожі; Відповідь Яна згадує про проблему, яку MSN колись мав з хеш-таблицями ZIP кодів . Це використання, коли уникнення зіткнень на ймовірних входах перемагає над випадковою поведінкою.

Ще одне повчальне порівняння тут - контраст цілей дизайну між CRC та криптографічними хеш-функціями:

  • CRC призначений для збору помилок, що виникають внаслідок галасливих каналів зв'язку , які, ймовірно, будуть невеликою кількістю бітових переворотів;
  • Крипто-хеші призначені для вилучення модифікацій, зроблених зловмисниками , яким надано обмежені обчислювальні ресурси, але довільно багато кмітливості.

Тож для CRC знову добре мати менші зіткнення, ніж випадкові на мінімально різних входах. З криптовалютами це - ні-ні!


10

Використовуйте SipHash . Він має багато бажаних властивостей:

  • Швидкий. Оптимізована реалізація займає близько 1 циклу на байт.

  • Безпечний. SipHash - сильний PRF (псевдовипадкова функція). Це означає, що він не відрізняється від випадкової функції (якщо ви не знаєте 128-бітний секретний ключ). Звідси:

    • Не потрібно турбуватися про те, щоб зонди хеш-таблиці стали лінійним часом через зіткнення. Завдяки SipHash ви знаєте, що в середньому ви будете отримувати середню ефективність, незалежно від вкладених даних.

    • Імунітет до хеш-відмови в службових атаках.

    • Ви можете використовувати SipHash (особливо версію із 128-бітовим виходом) як MAC (Код автентифікації повідомлення). Якщо ви отримаєте повідомлення та тег SipHash, і тег такий же, як у запуску SipHash із вашим секретним ключем, то ви знаєте, що хто створив хеш, той також мав ваш секретний ключ, і що ні повідомлення, ні хеш були змінені з тих пір


1
Хіба SipHash не буде зайвим, якщо вам не потрібна безпека? Потрібен 128-бітний ключ, який є просто прославленим хеш-зерном. Не кажучи вже про MurmurHash3 має 128-бітний вихід, а SipHash має лише 64-бітний вихід. Очевидно, що більший дайджест має менший шанс зіткнення.
bryc

@bryc Різниця полягає в тому, що SipHash і надалі буде поводитись добре, навіть при шкідливому введенні. Хеш-таблиця на основі SipHash може використовуватися для даних з потенційно ворожих джерел і може використовувати такий алгоритм, як лінійне зондування, який дуже чутливий до деталей хеш-функції.
Демі

9

Це залежить від даних, які ви хешуєте. Деякі хешування краще працювати з конкретними даними, такими як текст. Деякі алгоритми хешуваннявання були спеціально розроблені так, щоб вони були корисними для конкретних даних.

Пол Хсі одного разу зробив швидкий хеш . Він перераховує вихідний код та пояснення. Але його вже побили. :)


6

Java використовує цей простий алгоритм множення та додавання:

Хеш-код об’єкта String обчислюється як

 s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1]

використовуючи INT арифметику, де s[i]є я -й символ рядка, nдовжина рядка, і ^вказує , зведення в ступінь. (Хеш-значення порожнього рядка дорівнює нулю.)

Напевно, там є набагато кращі, але це досить поширене і, здається, є хорошим компромісом між швидкістю та унікальністю.


12
Я б не використовував той самий, який використовується тут, оскільки все-таки відносно легко виробляти зіткнення з цим. Це точно не страшно, але є набагато кращі. І якщо немає суттєвої причини бути сумісним з Java, його не слід вибирати.
Йоахім Зауер

4
Якщо ви все-таки обрали такий спосіб хешування з якихось причин, ви могли б принаймні використовувати кращий праймер, як 92821, як множник. Це значно зменшує зіткнення. stackoverflow.com/a/2816747/21499
Hans-Peter Störr

1
Ви також можете використовувати FNV1a замість цього. Це також простий хеш на основі множення, але він використовує більший множник, який краще розгортає хеш.
bryc

4

Перш за все, для чого потрібно реалізувати власне хешування? У більшості завдань ви повинні отримати хороші результати зі структурами даних зі стандартної бібліотеки, якщо припустити, що доступна реалізація (якщо ви це не робите для власної освіти).

Що стосується власне алгоритмів хешування, моїм особистим фаворитом є FNV. 1

Ось приклад реалізації 32-бітної версії в C:

unsigned long int FNV_hash(void* dataToHash, unsigned long int length)
{
  unsigned char* p = (unsigned char *) dataToHash;
  unsigned long int h = 2166136261UL;
  unsigned long int i;

  for(i = 0; i < length; i++)
    h = (h * 16777619) ^ p[i] ;

  return h;
}

2
Варіант FNV-1a трохи кращий з випадковістю. Поміняйте порядок *і ^: h = (h * 16777619) ^ p[i]==>h = (h ^ p[i]) * 16777619
Ian Boyd
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.