Мне нужна быстрая, простая хеш-функция, которая создает уникальный идентификатор для пары значений uint32_t
- то же самое значение хеша для (2,7)
и (7,2)
.Коммутируемая хеш-функция для пар значений uint32_t
Любая идея?
Мне нужна быстрая, простая хеш-функция, которая создает уникальный идентификатор для пары значений uint32_t
- то же самое значение хеша для (2,7)
и (7,2)
.Коммутируемая хеш-функция для пар значений uint32_t
Любая идея?
Чтобы ответить на мой собственный вопрос, решение:
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
.
Мне нравится идея сдвига, это естественно, и нет необходимости доказывать уникальность. Если вы хотите иметь дело со значениями выше 2^32, вы можете вернуть строку как уникальный идентификатор, где вы зарезервируете специальный символ для разделения двух частей хэша (также может быть изменена база представления более 10). – pkacprzak
" изменение представления базы до более чем 10 - хорошая идея также »- как вы это понимаете? – plasmacel
, если вы представляете число в виде строки, вы можете использовать более крупный алфавит, чем {0,1, ..., 9}, например. шестнадцатеричной системы или даже больше. Чем больше базовое, тем короче. – pkacprzak
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
) , который разрушает трубопровод центрального процессора (и медленно), если вы хотите, чтобы сделать быструю функцию.
Спасибо, но это очень медленно из-за большого количества умножений. См. Мой ответ. – plasmacel
Могу поспорить, что моя функция быстрее. Вы сравнили свою функцию с моей? Я добавил объяснение в ответ, почему он быстрее. –
@LeonidVolnitsky +1 для правильного объяснения. В некоторых случаях условное утверждение может смутить предсказание ветвлений. – NumberFour
Не могли бы вы просто добавить результаты 'std :: hash' для' pair.first' и 'pair.second'? –
Создайте uint64 путем битвыдвижения меньшего (или большего) числа двух чисел и добавления другого. Тогда вам просто нужно хэшировать 64-битный int. (В качестве альтернативы, хэш-функция скопирует пару, затем поменяйте элементы, чтобы гарантировать порядок, и примените реальный хэш на эту пару) –
@ DavidRodríguez-dribeas: Да, между тем я придумал решение, спасибо. У меня уже были вещи с битрейтом, но ты вспомнил, что сравнение - это волшебство. – plasmacel