【问题标题】:C++ Hash Table - How is collision for unordered_map with custom data type as keys resolved?C++ 哈希表 - 如何解决 unordered_map 与自定义数据类型作为键的冲突?
【发布时间】: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_cord{x}, y_cord{y}
    {

    }
    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 如何知道如何解决冲突?我需要添加任何东西来解决碰撞吗?

【问题讨论】:

    标签: c++ hashmap unordered-map user-defined-types hash-collision


    【解决方案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, y}{y, 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]范围内,并且可以生成该范围内的所有整数。因此,考虑用一个简单的数组(即图像)替换您的哈希表。图像是将像素位置映射到值的最佳数据结构。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-02-26
      • 1970-01-01
      • 2013-06-05
      相关资源
      最近更新 更多