以下のように、カスタムクラスをunordered_map
のキーとして使用しようとしています。
#include <iostream>
#include <algorithm>
#include <unordered_map>
using namespace std;
class node;
class Solution;
class Node {
public:
int a;
int b;
int c;
Node(){}
Node(vector<int> v) {
sort(v.begin(), v.end());
a = v[0];
b = v[1];
c = v[2];
}
bool operator==(Node i) {
if ( i.a==this->a && i.b==this->b &&i.c==this->c ) {
return true;
} else {
return false;
}
}
};
int main() {
unordered_map<Node, int> m;
vector<int> v;
v.Push_back(3);
v.Push_back(8);
v.Push_back(9);
Node n(v);
m[n] = 0;
return 0;
}
しかし、g ++では、次のようなエラーが表示されます。
In file included from /usr/include/c++/4.6/string:50:0,
from /usr/include/c++/4.6/bits/locale_classes.h:42,
from /usr/include/c++/4.6/bits/ios_base.h:43,
from /usr/include/c++/4.6/ios:43,
from /usr/include/c++/4.6/ostream:40,
from /usr/include/c++/4.6/iostream:40,
from 3sum.cpp:4:
/usr/include/c++/4.6/bits/stl_function.h: In member function ‘bool std::equal_to<_Tp>::operator()(const _Tp&, const _Tp&) const [with _Tp = Node]’:
/usr/include/c++/4.6/bits/hashtable_policy.h:768:48: instantiated from ‘bool std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_M_compare(const _Key&, std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_Hash_code_type, std::__detail::_Hash_node<_Value, false>*) const [with _Key = Node, _Value = std::pair<const Node, int>, _ExtractKey = std::_Select1st<std::pair<const Node, int> >, _Equal = std::equal_to<Node>, _H1 = std::hash<Node>, _H2 = std::__detail::_Mod_range_hashing, std::__detail::_Hash_code_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, std::__detail::_Default_ranged_hash, false>::_Hash_code_type = long unsigned int]’
/usr/include/c++/4.6/bits/hashtable.h:897:2: instantiated from ‘std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node* std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_M_find_node(std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node*, const key_type&, typename std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Hash_code_type) const [with _Key = Node, _Value = std::pair<const Node, int>, _Allocator = std::allocator<std::pair<const Node, int> >, _ExtractKey = std::_Select1st<std::pair<const Node, int> >, _Equal = std::equal_to<Node>, _H1 = std::hash<Node>, _H2 = std::__detail::_Mod_range_hashing, _Hash = std::__detail::_Default_ranged_hash, _RehashPolicy = std::__detail::_Prime_rehash_policy, bool __cache_hash_code = false, bool __constant_iterators = false, bool __unique_keys = true, std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Node = std::__detail::_Hash_node<std::pair<const Node, int>, false>, std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::key_type = Node, typename std::_Hashtable<_Key, _Value, _Allocator, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, __cache_hash_code, __constant_iterators, __unique_keys>::_Hash_code_type = long unsigned int]’
/usr/include/c++/4.6/bits/hashtable_policy.h:546:53: instantiated from ‘std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::mapped_type& std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::operator[](const _Key&) [with _Key = Node, _Pair = std::pair<const Node, int>, _Hashtable = std::_Hashtable<Node, std::pair<const Node, int>, std::allocator<std::pair<const Node, int> >, std::_Select1st<std::pair<const Node, int> >, std::equal_to<Node>, std::hash<Node>, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, false, false, true>, std::__detail::_Map_base<_Key, _Pair, std::_Select1st<_Pair>, true, _Hashtable>::mapped_type = int]’
3sum.cpp:149:5: instantiated from here
/usr/include/c++/4.6/bits/stl_function.h:209:23: error: passing ‘const Node’ as ‘this’ argument of ‘bool Node::operator==(Node)’ discards qualifiers [-fpermissive]
make: *** [threeSum] Error 1
私は、クラスNode
をハッシュする方法をC++に教える必要があると思いますが、どうするかはよくわかりません。どうやってこのタスクを達成できますか?
ユーザ定義のキータイプでstd::unordered_map
(または他の順序付けられていない連想コンテナの1つ)を使用できるようにするには、2つのことを定義する必要があります。
ハッシュ関数これはoperator()
をオーバーライドし、key-typeのオブジェクトを与えられたハッシュ値を計算するクラスでなければなりません。これを行うための特に簡単な方法の1つは、あなたのキータイプのためにstd::hash
テンプレートを特殊化することです。
同等性のための比較関数。ハッシュ関数がすべての個別のキーに対して一意のハッシュ値を常に提供するという事実にハッシュが頼ることはできないため(つまり衝突に対処できる必要があるため)、2つの与えられたキーを比較する方法が必要です。完全一致のために。これは、operator()
をオーバーライドするクラスとして、あるいはstd::equal
の特殊化として、あるいは最も簡単な方法として - あなたのキータイプのためにoperator==()
をオーバーロードすることによって実装することができます。
ハッシュ関数の難しさは、あなたのキータイプが複数のメンバからなる場合、通常ハッシュ関数に個々のメンバのハッシュ値を計算させ、そしてそれらを何らかの形でオブジェクト全体の1つのハッシュ値に結合させるということです。パフォーマンスが良い(つまり衝突が少ない)ためには、個々のハッシュ値を組み合わせる方法について慎重に検討し、異なるオブジェクトに対して同じ出力が頻繁に得られないようにする必要があります。
ハッシュ関数のかなり良い出発点は、個々のハッシュ値を組み合わせるためにビットシフトとビットごとのXORを使用することです。たとえば、次のようなキータイプを想定します。
struct Key
{
std::string first;
std::string second;
int third;
bool operator==(const Key &other) const
{ return (first == other.first
&& second == other.second
&& third == other.third);
}
};
これは単純なハッシュ関数です( cppreferenceの例で使用されているユーザー定義のハッシュ関数 のものから変更されたもの)。
namespace std {
template <>
struct hash<Key>
{
std::size_t operator()(const Key& k) const
{
using std::size_t;
using std::hash;
using std::string;
// Compute individual hash values for first,
// second and third and combine them using XOR
// and bit shifting:
return ((hash<string>()(k.first)
^ (hash<string>()(k.second) << 1)) >> 1)
^ (hash<int>()(k.third) << 1);
}
};
}
これで、key-typeに対してstd::unordered_map
をインスタンス化できます。
int main()
{
std::unordered_map<Key,std::string> m6 = {
{ {"John", "Doe", 12}, "example"},
{ {"Mary", "Sue", 21}, "another"}
};
}
ハッシュ値の計算には上で定義されたstd::hash<Key>
を、等価性検査のためにKey
のメンバー関数として定義されたoperator==
を自動的に使用します。
std
名前空間内でテンプレートを特殊化したくない場合は(この場合は完全に有効ですが)、ハッシュ関数を別のクラスとして定義し、それをマップのテンプレート引数リストに追加することができます。
struct KeyHasher
{
std::size_t operator()(const Key& k) const
{
using std::size_t;
using std::hash;
using std::string;
return ((hash<string>()(k.first)
^ (hash<string>()(k.second) << 1)) >> 1)
^ (hash<int>()(k.third) << 1);
}
};
int main()
{
std::unordered_map<Key,std::string,KeyHasher> m6 = {
{ {"John", "Doe", 12}, "example"},
{ {"Mary", "Sue", 21}, "another"}
};
}
より良いハッシュ関数を定義する方法上記のように、良いハッシュ関数を定義することは衝突を避けそして良い性能を得るために重要です。本当に良い方法では、すべてのフィールドの可能な値の分布を考慮し、可能な限り広く均一に分布する可能性のある結果の空間にその分布を投影するハッシュ関数を定義する必要があります。
これは難しいかもしれません。上記のXOR /ビットシフト方法はおそらく悪いスタートではありません。もう少しよいスタートのために、Boostライブラリからのhash_value
とhash_combine
関数テンプレートを使うことができます。前者は標準型のstd::hash
と同じように振る舞います(最近ではタプルや他の便利な標準型も含みます)。後者は、個々のハッシュ値を1つにまとめるのに役立ちます。これはBoostヘルパー関数を使ったハッシュ関数の書き換えです。
#include <boost/functional/hash.hpp>
struct KeyHasher
{
std::size_t operator()(const Key& k) const
{
using boost::hash_value;
using boost::hash_combine;
// Start with a hash value of 0 .
std::size_t seed = 0;
// Modify 'seed' by XORing and bit-shifting in
// one member of 'Key' after the other:
hash_combine(seed,hash_value(k.first));
hash_combine(seed,hash_value(k.second));
hash_combine(seed,hash_value(k.third));
// Return the result.
return seed;
}
};
そして、これは後押しをしないで、まだハッシュを組み合わせるための良い方法を使っている書き直しです:
namespace std
{
template <>
struct hash<Key>
{
size_t operator()( const Key& k ) const
{
// Compute individual hash values for first, second and third
// http://stackoverflow.com/a/1646913/126995
size_t res = 17;
res = res * 31 + hash<string>()( k.first );
res = res * 31 + hash<string>()( k.second );
res = res * 31 + hash<int>()( k.third );
return res;
}
};
}
私は、jogojapanが非常に良くて徹底的な answer を与えたと思います。私の投稿を読む前に、ぜひそれを見てください。しかし、私は以下を追加したいです。
unordered_map
)を使用する代わりに、operator==
の比較関数を個別に定義できます。これは、たとえば、2つのNode
オブジェクトのすべてのメンバーを互いに比較するために後者を使用したいが、一部の特定のメンバーだけをunordered_map
のキーとして比較したい場合に役立ちます。結局、あなたのNode
クラスに対して、コードは次のように書くことができます。
using h = std::hash<int>;
auto hash = [](const Node& n){return ((17 * 31 + h()(n.a)) * 31 + h()(n.b)) * 31 + h()(n.c);};
auto equal = [](const Node& l, const Node& r){return l.a == r.a && l.b == r.b && l.c == r.c;};
std::unordered_map<Node, int, decltype(hash), decltype(equal)> m(8, hash, equal);
ノート: