私は使っている
unordered_map<string, int>
そして
unordered_map<int, int>
どの場合にどのハッシュ関数が使用され、各場合に衝突の可能性は何ですか?それぞれの場合に、一意の文字列と一意のintをキーとしてそれぞれ挿入します。
文字列とintキーの場合のハッシュ関数のアルゴリズムとそれらの衝突統計を知りたいです。
関数オブジェクト 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です。
ハッシュアルゴリズムはコンパイラに依存しますが、GCC C++ 11向けに紹介します。 @Avidan Borisovが鋭意発見 文字列に使用されるGCCハッシュアルゴリズムは、Austin Applebyによる "MurmurHashUnaligned2"である。いくつかの検索を行ったところ、GithubでGCCのミラーコピーが見つかりました。したがって:
GCC C++ 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ハッシュ関数の2つのバージョン(1つはひどいもの、1つはかなり良いもの)については、他の回答を参照してください: https:// stackoverflow。 com/a/45641002/4561887 。