2013-07-20 2 views
7

Мне нужна быстрая, простая хеш-функция, которая создает уникальный идентификатор для пары значений uint32_t - то же самое значение хеша для (2,7) и (7,2).Коммутируемая хеш-функция для пар значений uint32_t

Любая идея?

+0

Не могли бы вы просто добавить результаты 'std :: hash' для' pair.first' и 'pair.second'? –

+2

Создайте uint64 путем битвыдвижения меньшего (или большего) числа двух чисел и добавления другого. Тогда вам просто нужно хэшировать 64-битный int. (В качестве альтернативы, хэш-функция скопирует пару, затем поменяйте элементы, чтобы гарантировать порядок, и примените реальный хэш на эту пару) –

+0

@ DavidRodríguez-dribeas: Да, между тем я придумал решение, спасибо. У меня уже были вещи с битрейтом, но ты вспомнил, что сравнение - это волшебство. – plasmacel

ответ

4

Чтобы ответить на мой собственный вопрос, решение:

uint64_t hash(uint32_t x, uint32_t y) 
{ 
    const uint64_t a = static_cast<uint64_t>(x); 
    const uint64_t b = static_cast<uint64_t>(y); 

    if (x < y) return (b << 32) | a; 
    else return (a << 32) | b; 
} 

Что можно улучшить в внеофисному версии

uint64_t hash(uint32_t x, uint32_t y) 
{ 
    const uint64_t a = static_cast<uint64_t>(x); 
    const uint64_t b = static_cast<uint64_t>(y); 

    const uint64_t h0 = (b << 32) | a; 
    const uint64_t h1 = (a << 32) | b; 

    return (x < y) ? h0 : h1; // conditional move (CMOV) instruction 
} 

Эти методы являются совершенными хэш-функции - они гарантируют ноль столкновений. Однако они имеют тот недостаток, что вы не можете хэш-значения выше 2^32 - 1.

+1

Мне нравится идея сдвига, это естественно, и нет необходимости доказывать уникальность. Если вы хотите иметь дело со значениями выше 2^32, вы можете вернуть строку как уникальный идентификатор, где вы зарезервируете специальный символ для разделения двух частей хэша (также может быть изменена база представления более 10). – pkacprzak

+0

" изменение представления базы до более чем 10 - хорошая идея также »- как вы это понимаете? – plasmacel

+1

, если вы представляете число в виде строки, вы можете использовать более крупный алфавит, чем {0,1, ..., 9}, например. шестнадцатеричной системы или даже больше. Чем больше базовое, тем короче. – pkacprzak

2
constexpr uint32_t hash_max = ...;  

constexpr uint32_t commutative_hash(uint32_t i, uint32_t j) { 
    return (i*j + (i*i)*(j*j) + (i*i*i)*(j*j*j)) % hash_max; 
}; 

Дополнительные скобки для компилятора - это будет легче оптимизировать это выражение.

Не используйте условную инструкцию (или std::max/std::min) , который разрушает трубопровод центрального процессора (и медленно), если вы хотите, чтобы сделать быструю функцию.

+0

Спасибо, но это очень медленно из-за большого количества умножений. См. Мой ответ. – plasmacel

+1

Могу поспорить, что моя функция быстрее. Вы сравнили свою функцию с моей? Я добавил объяснение в ответ, почему он быстрее. –

+0

@LeonidVolnitsky +1 для правильного объяснения. В некоторых случаях условное утверждение может смутить предсказание ветвлений. – NumberFour

 Смежные вопросы

  • Нет связанных вопросов^_^