Что такое хэш-функция по умолчанию, используемая в С++ std:: unordered_map?

Я использую

unordered_map<string, int>

и

unordered_map<int, int>

Какая хэш-функция используется в каждом случае и какова вероятность столкновения в каждом случае? Я буду вставлять уникальную строку и уникальный int как ключи в каждом случае соответственно.

Мне интересно знать алгоритм хеш-функции в случае строковых и int-ключей и их статистики столкновений.

Ответ 1

Используется объект функции std::hash<>.

Стандартные специализации существуют для всех встроенных типов, а некоторые другие стандартные типы библиотек таких как std::string и std::thread. См. Полный список ссылок.

Для других типов, которые будут использоваться в std::unordered_map, вам придется специализироваться на std::hash<> или создать свой собственный функциональный объект.

Вероятность столкновения полностью зависит от реализации, но учитывая тот факт, что целые числа ограничены между определенным диапазоном, а строки теоретически бесконечно длинны, я бы сказал, что вероятность столкновения со строками намного выше.

Что касается реализации в GCC, специализация для встроенных типов просто возвращает битовый шаблон. Здесь, как они определены в bits/functional_hash.h:

  /// Partial specializations for pointer types.
  template<typename _Tp>
    struct hash<_Tp*> : public __hash_base<size_t, _Tp*>
    {
      size_t
      operator()(_Tp* __p) const noexcept
      { return reinterpret_cast<size_t>(__p); }
    };

  // Explicit specializations for integer types.
#define _Cxx_hashtable_define_trivial_hash(_Tp)     \
  template<>                        \
    struct hash<_Tp> : public __hash_base<size_t, _Tp>  \
    {                                                   \
      size_t                                            \
      operator()(_Tp __val) const noexcept              \
      { return static_cast<size_t>(__val); }            \
    };

  /// Explicit specialization for bool.
  _Cxx_hashtable_define_trivial_hash(bool)

  /// Explicit specialization for char.
  _Cxx_hashtable_define_trivial_hash(char)

  /// ...

Специализация для std::string определяется как:

#ifndef _GLIBCXX_COMPATIBILITY_CXX0X
  /// std::hash specialization for string.
  template<>
    struct hash<string>
    : public __hash_base<size_t, string>
    {
      size_t
      operator()(const string& __s) const noexcept
      { return std::_Hash_impl::hash(__s.data(), __s.length()); }
    };

Дальнейший поиск приводит нас к:

struct _Hash_impl
{
  static size_t
  hash(const void* __ptr, size_t __clength,
       size_t __seed = static_cast<size_t>(0xc70f6907UL))
  { return _Hash_bytes(__ptr, __clength, __seed); }
  ...
};
...
// Hash function implementation for the nontrivial specialization.
// All of them are based on a primitive that hashes a pointer to a
// byte array. The actual hash algorithm is not guaranteed to stay
// the same from release to release -- it may be updated or tuned to
// improve hash quality or speed.
size_t
_Hash_bytes(const void* __ptr, size_t __len, size_t __seed);

_Hash_bytes является внешней функцией из libstdc++. Немного больше поиска привело меня к этому файлу, в котором говорится:

// This file defines Hash_bytes, a primitive used for defining hash
// functions. Based on public domain MurmurHashUnaligned2, by Austin
// Appleby.  http://murmurhash.googlepages.com/

Таким образом, алгоритм хэширования по умолчанию, используемый GCC для строк, это MurmurHashUnaligned2.

Ответ 2

Хотя алгоритмы хеширования зависят от компилятора, я представлю его для GCC С++ 11. @Авидан Борисов остро узнал, что алгоритм хэширования GCC, используемый для строк, - "MurmurHashUnaligned2", Остин Эпплби. Я сделал несколько поисков и нашел зеркальную копию GCC на Github. Поэтому:

Функции хеширования GCC С++ 11, используемые для unordered_map (шаблон хэш-таблицы) и unordered_set (шаблон набора хэшей) выглядит следующим образом.

код:

// Implementation of Murmur hash for 32-bit size_t.
size_t _Hash_bytes(const void* ptr, size_t len, size_t seed)
{
  const size_t m = 0x5bd1e995;
  size_t hash = seed ^ len;
  const char* buf = static_cast<const char*>(ptr);

  // Mix 4 bytes at a time into the hash.
  while (len >= 4)
  {
    size_t k = unaligned_load(buf);
    k *= m;
    k ^= k >> 24;
    k *= m;
    hash *= m;
    hash ^= k;
    buf += 4;
    len -= 4;
  }

  // Handle the last few bytes of the input array.
  switch (len)
  {
    case 3:
      hash ^= static_cast<unsigned char>(buf[2]) << 16;
      [[gnu::fallthrough]];
    case 2:
      hash ^= static_cast<unsigned char>(buf[1]) << 8;
      [[gnu::fallthrough]];
    case 1:
      hash ^= static_cast<unsigned char>(buf[0]);
      hash *= m;
  };

  // Do a few final mixes of the hash.
  hash ^= hash >> 13;
  hash *= m;
  hash ^= hash >> 15;
  return hash;
}

Для дополнительных функций хеширования, включая djb2, и двух версий хеширующих функций K & R (по-видимому, ужасно, один очень хорошо), см. мой другой ответ здесь: <а6 > .