如何在c++中生成整数对的无序集合?
How can I make an unordered set of pairs of integers in C++?
下面的程序不编译一组无序的整数对,但它可以编译整数对。unordered_set
及其成员函数可以用于用户定义类型吗?如何定义它?
#include <unordered_set>
...
class A{
...
private:
std::unordered_set< std::pair<int, int> > u_edge_;
};
编译错误:
错误:调用std::unordered_set>::unordered_set()'没有匹配的函数
没有计算一对哈希的标准方法。将这个定义添加到您的文件中:
struct pair_hash {
inline std::size_t operator()(const std::pair<int,int> & v) const {
return v.first*31+v.second;
}
};
现在你可以这样使用:
std::unordered_set< std::pair<int, int>, pair_hash> u_edge_;
这行得通,因为pair<T1,T2>
定义了相等。对于不提供测试相等性方法的自定义类,您可能需要提供一个单独的函数来测试两个实例是否彼此相等。
当然这个解仅限于一对两个整数。下面是一个答案的链接,它可以帮助您定义为多个对象创建散列的更通用的方法。
您的代码可以在VS2010 SP1 (VC10)上编译,但无法在GCC g++ 4.7.2上编译。
但是,您可能想要考虑Boost中的boost::hash
。用于散列std::pair
的函数(添加此功能后,您的代码也可以使用g++编译)。
#include <unordered_set>
#include <boost/functional/hash.hpp>
class A
{
private:
std::unordered_set<
std::pair<int, int>,
boost::hash< std::pair<int, int> >
> u_edge_;
};
问题是std::unordered_set
使用std::hash
模板为其条目计算哈希值,并且没有std::hash
专门化对。所以你需要做两件事:
- 决定你想使用什么哈希函数
- 使用该函数为您的密钥类型(
std::pair<int, int>
)专门化std::hash
。
下面是一个简单的例子:
#include <unordered_set>
namespace std {
template <> struct hash<std::pair<int, int>> {
inline size_t operator()(const std::pair<int, int> &v) const {
std::hash<int> int_hasher;
return int_hasher(v.first) ^ int_hasher(v.second);
}
};
}
int main()
{
std::unordered_set< std::pair<int, int> > edge;
}
正如关于这个问题的大多数其他答案中已经提到的,您需要为std::pair<int, int>
提供一个散列函数。但是,从c++ 11开始,您也可以使用lambda表达式来代替定义散列函数。下面的代码以Sergey给出的解为基础:
auto hash = [](const std::pair<int, int>& p){ return p.first * 31 + p.second; };
std::unordered_set<std::pair<int, int>, decltype(hash)> u_edge_(8, hash);
Ideone代码
我想重复Sergey的免责声明:此解决方案仅限于一对两个整数。这个答案为更通用的解决方案提供了思路。
这是一个保证不发生碰撞的简单解决方案。只需将您的问题简化为现有的解决方案,即将您的int
对转换为string
,如下所示:
auto stringify = [](const pair<int, int>& p, string sep = "-")-> string{
return to_string(p.first) + sep + to_string(p.second);
}
unordered_set<string> myset;
myset.insert(stringify(make_pair(1, 2)));
myset.insert(stringify(make_pair(3, 4)));
myset.insert(stringify(make_pair(5, 6)));
享受吧!
您需要为std::hash<>
提供与std::pair<int, int>
一起工作的专门化。下面是如何定义专门化的一个非常简单的例子:
#include <utility>
#include <unordered_set>
namespace std
{
template<>
struct hash<std::pair<int, int>>
{
size_t operator () (std::pair<int, int> const& p)
{
// A bad example of computing the hash,
// rather replace with something more clever
return (std::hash<int>()(p.first) + std::hash<int>()(p.second));
}
};
}
class A
{
private:
// This won't give you problems anymore
std::unordered_set< std::pair<int, int> > u_edge_;
};
这里的其他答案都建议构建一个散列函数,以某种方式将两个整数组合在一起。
这将工作,但产生非唯一哈希。虽然这对于您使用unordered_set
来说很好,但对于某些应用程序来说,这可能是不可接受的。在你的例子中,如果你碰巧选择了一个不好的哈希函数,它可能会导致许多不必要的冲突。
但是你可以产生唯一的哈希值!
int
通常为4字节。您可以通过使用int32_t
使其显式。
哈希的数据类型是std::size_t
。在大多数机器上,这是8字节。您可以在编译时检查。
由于一对由两个int32_t
类型组成,您可以将两个数字放入std::size_t
中以生成唯一的哈希。
看起来像这样(我想不起来如何强制编译器将有符号值视为无符号值进行位操作,所以我为uint32_t
编写了以下代码):
#include <cassert>
#include <cstdint>
#include <unordered_set>
#include <utility>
struct IntPairHash {
std::size_t operator()(const std::pair<uint32_t, uint32_t> &p) const {
assert(sizeof(std::size_t)>=8); //Ensure that std::size_t, the type of the hash, is large enough
//Shift first integer over to make room for the second integer. The two are
//then packed side by side.
return (((uint64_t)p.first)<<32) | ((uint64_t)p.second);
}
};
int main(){
std::unordered_set< std::pair<uint32_t, uint32_t>, IntPairHash> uset;
uset.emplace(10,20);
uset.emplace(20,30);
uset.emplace(10,20);
assert(uset.size()==2);
}
您缺少std::pair<int, int>>
的哈希函数。例如,
struct bad_hash
{
std::size_t operator()(const std::pair<int,int>& p) const
{
return 42;
}
};
....
std::unordered_set< std::pair<int, int>, bad_hash> u_edge_;
您还可以将std::hash<T>
专门化为std::hash<std::pair<int,int>>
,在这种情况下,您可以省略第二个模板参数。
要创建一个unordered_set对,您可以创建一个自定义散列函数,也可以创建一个unordered_set字符串。
-
创建自定义哈希函数:根据数据创建自定义哈希。所以没有放之四海而皆准的哈希函数。一个好的哈希函数必须有更少的冲突,所以在创建哈希函数时需要考虑冲突计数。
-
使用字符串:使用字符串非常简单,花费的时间更少。它还保证很少或没有碰撞。而不是使用unordered_set
<int,>>我们使用了一个unordered_set。我们可以通过用分隔符(字符或字符串)分隔数字来表示这对。下面给出的示例显示了如何使用分隔符(";")插入一对整数。 auto StringPair = [](const pair<int,>&X){返回to_string(x.first) + ";"+ to_string (x.second);};unordered_set;
vector<pair<>比;Nums = {{1,2}, {2,3}, {4,5}, {1,2}};
(汽车,对:num){Set.insert (StringPair(一对);}
只是在这里添加我的2美分,奇怪的是,要使用unordered_set,您需要指定一个外部哈希函数。封装原则更倾向于在类中有一个返回哈希值的'hash()'函数,而unordered_set将调用该函数。您应该有一个Hashable接口,并且您的类(在本例中是std::pair)将实现该接口。我认为这是Java等语言所遵循的方法。不幸的是,c++不遵循这种逻辑。你能模仿的最接近的是:
- 从std::pair派生一个类(无论如何,这允许你有更可读的代码)
- 将哈希函数传递给unordered_set模板
代码示例
class Point : public pair<int, int> {
public:
Point() {};
Point(int first, int second) : pair{first, second}{};
class Hash {
public:
auto operator()(const Point &p) const -> size_t {
return ((size_t)p.first) << 32 | ((size_t)p.second);
}
};
};
int main()
{
unordered_set< Point, Point::Hash > us;
Point mypoint(1000000000,1);
size_t res = Point::Hash()(mypoint);
cout<<"Hello World " << res << " " << mypoint.first;
return 0;
}
如果size_t为64位,int为32位,则使用的简单哈希函数有效,在这种情况下,该哈希函数保证没有冲突,这是理想的
- c++找不到具有相同哈希的无序集合元素
- 如何写向量的无序向量集,即unordered_set<向量<向量<int>>集合?
- 从C++无序集合中高效提取元素
- 仅从无序集合中删除一个项目
- 如何从一个无序集合中获取一个元素
- C++中的无序集合交集
- 设置要与无序集合一起使用的自定义类 - 在集合中找不到元素
- 使用shared_ptr<字符串>转换为一个无序集合<字符串>
- 如何将数组插入无序集合
- 将向量的元素添加到无序集合中
- 为什么这些C++ STL 无序集合不被视为相等?
- 打印无序集合的元素
- 无序集合中的哈希函数
- std::插入无序集合(或映射)的迭代器
- 比较两个无序集合的相等性有多昂贵
- 相当于 python 的 set.pop() 用于C++的无序集合
- C++:将元素从无序集合复制到向量
- 无序集合哈希表中元素的排序
- 使用自定义哈希函数插入到一个无序集合中
- 如何在c++中生成整数对的无序集合?