【问题标题】:How to hash an unordered_map?如何散列 unordered_map?
【发布时间】:2014-10-04 10:04:50
【问题描述】:

boost::hash 具有适用于大多数内置类型(包括容器)的散列函数。

但正如boost::hash_range function description 中所述,范围的哈希算法

对元素的顺序很敏感,因此不适合将其与无序容器一起使用

因此对于std::unordered_mapboost::unordered_map 没有boost::hash 特化。


问题是:

是否有一种“简单而有效”的方法来散列 unordered_map 而无需从头开始重新实现散列算法?

【问题讨论】:

  • 问这个是用来做什么的是不是太过分了?使用树作为键是相当奇怪的。
  • 我回答这个问题。我不清楚哈希值对于无序数据结构的用途是什么。
  • 用例基本上是一个类似 json 的对象,用作键,因此需要是可散列的,因为该对象是递归的(可以是树)可能的“形式”之一(该对象的变体实现)是一个 unordered_map 本身

标签: c++ boost hash unordered-map


【解决方案1】:

这里的问题是,不能保证项目甚至在它们之间有一个顺序。
因此,对项目进行排序可能不适用于任意无序的容器。你有两个选择:

  1. 只需对所有单个元素的哈希值进行异或。这是最快的。
  2. 首先对容器的哈希进行排序,然后然后对它们进行哈希。这可能会产生更好的哈希值。

【讨论】:

  • 使用 XOR 的有趣想法 - 它自然是独立于顺序的。不分发位,但在此应用程序中可能无关紧要。
  • XOR 操作很聪明,但它可能会降低源哈希函数完成的工作的质量。事实需要进行准确的分析,但可以大大减少生成的哈希“相当”唯一的“保证”。
  • @StefanoBuora:因此我说第二个可能会产生更好的哈希值。
【解决方案2】:

您当然可以将unordered_map 转换为具有保证顺序的其他数据结构,并使用它来生成哈希。

一个更好的想法可能是散列地图的每个单独元素,将这些散列放入vector,然后对散列进行排序和组合。参见例如How do I combine hash values in C++0x? 来组合散列。

template<typename Hash, typename Iterator>
size_t order_independent_hash(Iterator begin, Iterator end, Hash hasher)
{
    std::vector<size_t> hashes;
    for (Iterator it = begin; it != end; ++it)
        hashes.push_back(hasher(*it));
    std::sort(hashes.begin(), hashes.end());
    size_t result = 0;
    for (auto it2 = hashes.begin(); it2 != hashes.end(); ++it2)
        result ^= *it2 + 0x9e3779b9 + (result<<6) + (result>>2);
    return result;
}

在 shuffled 向量上进行测试表明它总是返回相同的哈希值。

现在调整该基本概念以专门用于unordered_map。由于unordered_map 的迭代器返回pair,因此我们也需要一个哈希函数。

namespace std
{
    template<typename T1, typename T2>
    struct hash<std::pair<T1,T2> >
    {
        typedef std::pair<T1,T2> argument_type;
        typedef std::size_t result_type;

        result_type operator()(argument_type const& s) const
        {
            result_type const h1 ( std::hash<T1>()(s.first) );
            result_type const h2 ( std::hash<T2>()(s.second) );
            return h1 ^ (h2 + 0x9e3779b9 + (h1<<6) + (h1>>2));
        }
    };

    template<typename Key, typename T>
    struct hash<std::unordered_map<Key,T> >
    {
        typedef std::unordered_map<Key,T> argument_type;
        typedef std::size_t result_type;

        result_type operator()(argument_type const& s) const
        {
            return order_independent_hash(s.begin(), s.end(), std::hash<std::pair<Key,T> >());
        }
    };
}

查看实际操作:http://ideone.com/WOLFbc

【讨论】:

    【解决方案3】:

    我认为您可能会混淆哈希的用途。它用于用于标识元素的键,以确定将它们存储在哪里。两个等效元素应该具有相同的值。

    您是否尝试查看两个无序映射是否等效并将它们存储在某种容器中?

    无序映射的键 - 那些是散列的。事实上,这个容器应该被称为 hash_map,只不过这样的容器已经存在。

    但是好的,假设您确实想要存储无序映射并比较两者是否相等。那么你必须想出一个散列算法,无论它包含的元素的位置如何,它都会返回相同的值。其所有元素(键和值)的校验和将是一种可能的方法。

    还要注意,仅仅因为两个元素具有相同的哈希值,并不意味着它们是等价的。这只是意味着如果哈希值不同,它们肯定是不等价的。事实上,正是出于这个原因,校验和经常被用来验证数据。错误的校验和是数据无效的证明,并且给定一个好的公式,一个正确的公式使它很有可能虽然不确定它是。

    【讨论】:

    • 别担心,我了解散列的一般概念及其作为快速访问容器中键的作用。但是是的,我确实在尝试散列 unordered_map 以将它们用作键(实际上实际用例稍微复杂一些)但我想看看是否存在某些东西,然后再去制作一个需要时间并且可能会赢的自制算法不要超级高效。
    • 键也是不可变的。这意味着一旦您将 unordered_map 设置为键,您就无法更改它。
    • 是的,否则它是另一个键。
    【解决方案4】:

    我很好奇您正在尝试散列 unordered_map 以将其用作密钥,并且一旦您散列了 unordered_map 您将不会更改它(除非您使用它来创建一个新密钥),将unordered_map 转换为有序map 的性能影响是否可以接受(然后,当然,对有序map 进行散列并将其用作密钥)?或者这种方法的问题是您需要unordered_map 提供的更快的查找时间?

    对于它的价值可能使用有序的map 有空间优势(根据以下帖子中接受的答案,unordered_map 通常使用更多内存):

    Is there any advantage of using map over unordered_map in case of trivial keys?

    【讨论】:

      【解决方案5】:

      您没有指定任何性能要求,但如果您只是想要一个“快速而肮脏”的解决方案,不需要代表您进行太多编码并且可以利用 boost::hash,您可以复制项目范围从unordered_mapvectorstd::sort 向量,然后将其传递给boost::hash_range

      不过,这几乎不是最有效的解决方案,也不是您希望经常使用或与许多元素一起使用的解决方案。

      我首选的方法是 unordered_map 的特殊化,它可以保持内容的运行、最新散列 - 您不必传递所有元素并执行计算来获取当前值。相反,数据结构的成员应该反映散列,并在插入或删除元素时实时修改,并在需要时读取。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-11-23
        • 2020-10-19
        • 2017-07-18
        • 2020-10-22
        • 2020-06-15
        相关资源
        最近更新 更多