【问题标题】:How to write qHash for a QSet<SomeClass*> container?如何为 QSet<SomeClass*> 容器编写 qHash?
【发布时间】:2012-02-25 22:36:45
【问题描述】:

我需要在我的应用程序中实现一组集合。 将 QSet 与自定义类一起使用需要提供 qHash() 函数和 operator==

代码如下:

    class Custom{
        int x;
        int y;
        //some other irrelevant here
    }
    inline uint qHash(Custom* c){
        return (qHash(c->x) ^ qHash(c->y));
    }
    bool operator==(Custom &c1, Custom &c2){
        return ((c1.x==c2.x) && (c1.y == c2.y));
    }

    //now I can use: QSet<Custom*>

如何实现qHash(QSet&lt;Custom*&gt;),才能使用QSet&lt; QSet&lt;SomeClass*&gt; &gt;

编辑:

补充问题: 在我的应用程序中,“集合集”最多可以包含 15000 个集合。每个子集最多有 25 个自定义类指针。如何保证qHash(QSet&lt;Custom*&gt;)足够独特?

【问题讨论】:

  • 您确定QSet&lt;Custom*&gt; 吗?不应该是QSet&lt;Custom,...&gt;吗? QHash 永远不会调用 operator==Custom,因为它使用内置的相等运算符作为指针。

标签: c++ qt hash containers


【解决方案1】:

你不能用boost::hash_range/boost::hash_combine 来实现qHash(这是pmr 的答案,实际上是这样做的),因为QSetstd::unordered_set 的Qt 等价物,并且正如STL 名称所暗示的那样,这些容器是无序的,而Boost Documentation 声明hash_combine 是顺序相关的,即。它会将排列散列到不同的散列值。

这是一个问题,因为如果您天真地按存储顺序对元素进行哈希组合 你不能保证两个比较相等的集合确实相等,这是散列函数的要求之一:

For all x, y:   x == y => qHash(x) == qHash(y)

因此,如果您的哈希组合函数需要为输入值的任何排列生成相同的输出,则它需要是可交换的。幸运的是,(无符号)加法和异或运算都符合要求:

template <typename T>
inline uint qHash(const QSet<T> &set, uint seed=0) {
    return std::accumulate(set.begin(), set.end(), seed,
                           [](uint seed, const T&value) {
                               return seed + qHash(value); // or ^
                           });
}

【讨论】:

    【解决方案2】:

    散列容器的常用方法是组合所有元素的散列。 Boost 为此提供了hash_combine and hash_range。这应该让您了解如何为您的qHash 的结果实现这一点。

    所以,给你qHashCustom

    uint qHash(const QSet<Custom*>& c) {
      uint seed = 0;
    
      for(auto x : c) {
        seed ^= qHash(x) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
      }
    
      return seed;
    }
    

    【讨论】:

    • 这是我的第一个想法,但我可以确定哈希是唯一的吗?当然我可以在我的代码中测试它,但也许这背后有一些理论?
    • @piotr 您可能想将此添加到您的问题中。现在你只要求一个实现。以下是有关此功能的一些提示:stackoverflow.com/questions/4948780/…
    • @Piotr:你可以永远确保哈希是唯一的。事实上,它保证 not 是唯一的,因为散列值没有足够的位来表示所有可能的集合(至少在理论上是无限的)。这就是为什么您还需要一个operator== - 来检测碰撞并正确处理它们。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-14
    • 2020-06-01
    • 1970-01-01
    • 2017-06-10
    • 2015-03-07
    相关资源
    最近更新 更多