很好的哈希函数对基本类型

Good hash function for pair of primitive types

本文关键字:类型 函数 哈希 很好      更新时间:2023-10-16

我试图找出一个好的哈希函数为std::对两个基本类型。这是我现在实现它的方式:

template<typename T, typename U>
std::size_t operator()(const std::pair<T,U> &rhs) const
{
    return stdext::hash_value<T>(rhs.first) ^ stdext::hash_value<U>(rhs.second);
}

它似乎工作,即使我有两对,如(1,2)和(2,1)(数字翻转)。它们生成相同的哈希值,但这些值仍然成功地插入到哈希映射中。任何想法吗?

一般来说,散列容器总是必须处理这种情况(散列冲突)。他们可以使用一些方法,如链接和探测,其中任何一种都可能损害性能。

相反,我建议使用boost::hash_combine来组合哈希,这样交换firstsecond不会生成相同的哈希。

下面是基于当前版本boost文档的hash_combine实现:(http://www.boost.org/doc/libs/1_53_0/doc/html/hash/reference.html boost.hash_combine)

template<typename T> void hash_combine(size_t & seed, T const& v) {
  seed ^= stdext::hash_value(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}

你可以这样使用:

template<typename T, typename U>
std::size_t operator()(const std::pair<T,U> &rhs) const   {
  size_t retval = stdext::hash_value<T>(rhs.first);
  hash_combine(retval, rhs.second);
  return retval;
}

我不能保证这个函数背后的逻辑,但它看起来比这里提到的大多数选项更可靠。

假设stdext::hash_value为first和second提供了一个很好的散列值分布,如果您不期望出现不成比例的高镜像对(例如(1,2)和(2,1)),那么您所做的是可以的。如果您的数据集确实期望有很多这样的数据集,那么您可以考虑调整散列函数,使它们不同。例如,反转第一个哈希值:

return ~stdext::hash_value<T>(rhs.first) ^ stdext::hash_value<T>(rhs.second);

我提到这个只是因为你表达了对镜像对的关注。如果你的输入在这方面接近随机,那么^就可以了。请记住,目标是尽量减少碰撞,而不是完全避免碰撞。

正如其他人所指出的,哈希函数只影响哈希表的效率,而不影响正确性。只有当镜像对频繁出现时,函数才会出现问题。由于在某些应用程序中这可能是一个问题,因此交换一个哈希值的上下半字,然后进行异或是合理的。许多其他方案都是可行的,但这个方案非常快速和简单。

template<typename T, typename U>
std::size_t operator()(const std::pair<T,U> &rhs) const
{
    const int h = sizeof(size_t) * 8 / 2;
    size_t a = stdext::hash_value<T>(rhs.first);
    return ((a << h) | (a >> h)) ^ stdext::hash_value<U>(rhs.second);
}

只是为了好玩,这里有另一种简单且直接解决问题的方法,特别是:

  • 如果firstsecond是相同的,它返回它们的公共哈希值
  • 否则,它对值进行xor,但是:
    • 为了防止h(a, b)与h(b, a)发生碰撞,使用了<b在h(a)^h(b)和h(a)^h(~b)之间进行选择>
实现:

1 template<typename T, typename U>
2 std::size_t operator()(const std::pair<T,U> &rhs) const
3 {
4     std::size_t a = stdext::hash_value<T>(rhs.first);
5     return rhs.first == rhs.second ? a :
6            a ^ (rhs.first < rhs.second ? stdext::hash_value<U>(rhs.second)
7                                        : stdext::hash_value<U>(~rhs.second));
8 }

说真的,我建议遵循Mark B的建议,使用boost::hash_combine——更少的分支可能会提高速度。