C++ 哈希表 - 如何解决 unordered_map 与自定义数据类型作为键的冲突?
Posted
技术标签:
【中文标题】C++ 哈希表 - 如何解决 unordered_map 与自定义数据类型作为键的冲突?【英文标题】:C++ Hash Table - How is collision for unordered_map with custom data type as keys resolved? 【发布时间】:2018-08-28 01:01:09 【问题描述】:我定义了一个名为Point
的类,它将用作unordered_map
中的键。所以,我在类中提供了一个operator==
函数,我还为std::hash
提供了一个template specialization
。根据我的研究,这是我认为必要的两件事。相关代码如图:
class Point
int x_cord = 0;
int y_cord = 0;
public:
Point()
Point(int x, int y):x_cordx, y_cordy
int x() const
return x_cord;
int y() const
return y_cord;
bool operator==(const Point& pt) const
return (x_cord == pt.x() && y_cord == pt.y());
;
namespace std
template<>
class hash<Point>
public:
size_t operator()(const Point& pt) const
return (std::hash<int>(pt.x()) ^ std::hash<int>(pt.y()));
;
// Inside some function
std::unordered_map<Point, bool> visited;
程序编译并在我测试的情况下给出了正确的结果。但是,当使用用户定义的类作为键时,我不相信这是否足够。在这种情况下,unordered_map
如何知道如何解决冲突?我需要添加任何东西来解决碰撞吗?
【问题讨论】:
【参考方案1】:这是一个糟糕的哈希函数。但这是合法的,因此您的实施将起作用。
Hash 和 Equals 的规则(也是唯一的规则)是:
如果a == b
,那么std::hash<value_type>(a) == std::hash<value_type>(b)
。
(同样重要的是,Hash 和 Equals 总是为相同的参数产生相同的值。我曾经认为这是不言而喻的,但我见过几个 SO 问题,其中 unordered_map 产生了意外的结果,正是因为其中一个或两个这些函数依赖于一些外部值。)
这将通过一个总是返回 42 的哈希函数来满足,在这种情况下,地图会在填满时变得非常慢。但除了速度问题之外,代码也可以工作。
std::unordered_map
使用chained hash,而不是开放地址哈希。所有具有相同哈希值的条目都放在同一个桶中,这是一个链表。所以低质量的哈希不能很好地在桶之间分配条目。
很明显,您的哈希值给x, y
和y, x
提供了相同的哈希值。更严重的是,一个小矩形中的任何点集合都将共享相同的少量不同哈希值,因为哈希值的高位都是相同的。
【讨论】:
如何选择更好的哈希函数?像x_cord * 10 + y_cord
这样的东西有用吗?只是为了找到size_t
类型的唯一值吗?
我会乘以一个更大的数字,可能是一个素数(或者至少不是一个偶数)。您不需要找到唯一的号码;只要确保值分布均匀。 (请记住将x_cord
和y_cord
都转换为unsigned
以避免潜在的整数溢出UB。)
使用质数或奇数的原因是什么?
如果使用偶数,x_cord
不会影响哈希的低位,因为乘积总是偶数。
谢谢。我已将return语句更改为std::hash<int>(pt.x()) * 17 + std::hash<int>(pt.y())
【参考方案2】:
Knowing that Point
is intended to store coordinates within an image,这里最好的哈希函数是:
pt.x() + pt.y() * width
其中width
是图像的宽度。
考虑到x
是[0, width-1]
范围内的值,上述哈希函数为pt
的任何有效值生成一个唯一编号。不可能发生冲突。
请注意,如果您将图像存储为单个内存块,则此哈希值对应于点 pt
的线性索引。即假设y
也在有限范围内([0, height-1]
),则生成的所有哈希值都在[0, width* height-1]
范围内,并且可以生成该范围内的所有整数。因此,考虑用一个简单的数组(即图像)替换您的哈希表。图像是将像素位置映射到值的最佳数据结构。
【讨论】:
以上是关于C++ 哈希表 - 如何解决 unordered_map 与自定义数据类型作为键的冲突?的主要内容,如果未能解决你的问题,请参考以下文章