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&lt;value_type&gt;(a) == std::hash&lt;value_type&gt;(b)

(同样重要的是,Hash 和 Equals 总是为相同的参数产生相同的值。我曾经认为这是不言而喻的,但我见过几个 SO 问题,其中 unordered_map 产生了意外的结果,正是因为其中一个或两个这些函数依赖于一些外部值。)

这将通过一个总是返回 42 的哈希函数来满足,在这种情况下,地图会在填满时变得非常慢。但除了速度问题之外,代码也可以工作。

std::unordered_map 使用chained hash,而不是开放地址哈希。所有具有相同哈希值的条目都放在同一个桶中,这是一个链表。所以低质量的哈希不能很好地在桶之间分配条目。

很明显,您的哈希值给x, yy, x 提供了相同的哈希值。更严重的是,一个小矩形中的任何点集合都将共享相同的少量不同哈希值,因为哈希值的高位都是相同的。

【讨论】:

如何选择更好的哈希函数?像x_cord * 10 + y_cord 这样的东西有用吗?只是为了找到size_t 类型的唯一值吗? 我会乘以一个更大的数字,可能是一个素数(或者至少不是一个偶数)。您不需要找到唯一的号码;只要确保值分布均匀。 (请记住将x_cordy_cord 都转换为unsigned 以避免潜在的整数溢出UB。) 使用质数或奇数的原因是什么? 如果使用偶数,x_cord 不会影响哈希的低位,因为乘积总是偶数。 谢谢。我已将return语句更改为std::hash&lt;int&gt;(pt.x()) * 17 + std::hash&lt;int&gt;(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 与自定义数据类型作为键的冲突?的主要内容,如果未能解决你的问题,请参考以下文章

C++之unordered_map和unordered_set以及哈希详解

C++数据结构——哈希表

C++数据结构——哈希表

C++数据结构——哈希表

C++数据结构——哈希表

std::unordered_map 如何表现? [C++]