C ++关于boost :: unordered_map&的一些问题提高::哈希

时间:2011-07-14 00:19:40

标签: c++ hash unordered-map boost-unordered

我最近才开始关注boost和它的容器,我在网上和stackoverflow上阅读了一些文章,boost :: unordered_map是大型集合中表现最快的容器。 所以,我有这个类State,它必须在容器中是唯一的(没有重复),并且容器中将有数百万甚至数十亿个状态。 因此,我一直在尝试优化它以实现小尺寸和尽可能少的计算。之前我正在使用boost :: ptr_vector,但是当我在stackoverflow上读到时,只要没有那么多对象,向量就是好的。 在我的情况下,状态描述来自机器人的感觉运动信息,因此可能存在大量状态,因此快速查找是最重要的。 在boost documentation for unordered_map之后,我意识到我可以做两件事来加快速度:使用hash_function,并使用等于运算符根据hash_function比较状态。 因此,我实现了一个私有hash()函数,它接收状态信息并使用boost :: hash_combine,创建一个std :: size_t哈希值。 operator ==基本上比较状态的哈希值。 所以:

  • 是std :: size_t足以覆盖数十亿可能的hash_function 组合?为了避免我打算使用的重复状态 他们的hash_values。

  • 创建state_map时,我应该使用State *或hash作为键 价值? 即:boost::unordered_map<State*,std::size_t> state_map; 要么 boost::unordered_map<std::size_t,State*> state_map;

  • 查询时间是否为boost :: unordered_map :: iterator = state_map.find()比通过boost :: ptr_vector更快 比较每个迭代器的键值?

  • 最后,有关如何优化此类无序地图的任何提示或技巧 对于速度和快速查找将非常感激。

编辑:我已经看到了不少答案,一个不是使用boost而是使用C ++ 0X,另一个不使用unordered_set,但说实话,我还是想看看boost :: unordered_set是如何使用的哈希函数。 我已经按照boost的文档进行了实现,但我仍然无法弄清楚如何在有序集合中使用boost的哈希函数。

3 个答案:

答案 0 :(得分:4)

这有点混乱。

  • 你说的不是“你可以做的事情来加快速度”;相反,它们是您的类型的强制性要求,有资格作为无序地图的元素类型,也适用于无序集合(您可能更愿意)。

  • 您需要提供一个比较运算符来比较对象,而不是哈希值。相等的重点是区分具有相同散列的元素。

  • size_t是无符号整数类型,x86为32位,x64为64位。既然你需要“数十亿元素”,这意味着数GB的数据,我认为你还有一台坚固的x64机器。

  • 关键是你的哈希函数,即碰撞很少。

  • 你想要一套,而不是一张地图。将对象直接放在集合中:std::unordered_set<State>。如果要将映射到某些内容,请使用地图,即将状态映射到其他内容。哦,如果可以,请使用C ++ 0x,而不是提升。

  • 使用hash_combine很好。


宝贝示例:

struct State
{
  inline bool operator==(const State &) const;
  /* Stuff */
};

namespace std
{
  template <> struct hash<State>
  {
    inline std::size_t operator()(const State & s) const
    {
      /* your hash algorithm here */
    }
  };
}

std::size_t Foo(const State & s) { /* some code */ }

int main()
{
  std::unordered_set<State> states; // no extra data needed
  std::unordered_set<State, Foo> states; // another hash function
}

答案 1 :(得分:2)

unordered_map是一个哈希表。你不存储哈希;它在内部完成,作为存储和查找方法。

根据您的要求,unordered_set可能更合适,因为您的对象是唯一要存储的项目。

虽然你有点困惑 - 相等运算符和哈希函数不是真正的性能项,但是对于容器才能正常工作的非平凡对象是必需的。一个好的哈希函数会将您的节点均匀地分布在桶中,并且相等运算符将用于根据哈希函数消除关于匹配的任何歧义。

std :: size_t适用于散列函数。请记住,没有哈希是完美的;将发生碰撞,这些碰撞项目存储在该铲斗位置的链接列表中。

因此,.find()在最佳情况下为O(1),在平均情况下非常接近O(1)(在最坏的情况下为O(N),但是一个不错的散列函数将避免这种情况。)

您没有提及您的平台或架构;在数十亿条目中,你仍然可能不得不担心内存不足的情况,具体取决于那些和State对象的大小。

答案 2 :(得分:2)

忘记哈希;没有任何东西(至少从你的问题中)表明你有一把有意义的钥匙;

让我们退后一步,重新定义您的实际绩效目标:

  • 您希望快速验证任何状态对象都不存在重复项

如果我需要添加其他人,请发表评论。

从上述目标和你的评论我建议你实际使用ordered_set而不是unordered_map。是的,有序搜索使用二进制搜索O(log(n)),而无序使用查找O(1)。

然而,不同之处在于,使用此方法时,您需要ordered_set ONLY 来检查当您要创建新的时,已经存在类似的状态 >,即州创建时

所有其他查找中,您实际上不需要查看ordered_set!因为你已经有了钥匙;状态*,键可以通过魔术解引用运算符访问该值:* key

因此,使用此方法,您只使用ordered_set作为 index 来仅在创建时验证状态。在所有其他情况下,您可以使用指针值键的解引用运算符访问您的State。

如果上述所有内容都不足以说服你,那么这就是使用哈希快速确定平等的想法的最后一个钉子;哈希函数具有较小的碰撞概率,但随着状态数量的增加,该概率将变得完全确定。因此,根据您的容错程度,您将处理状态冲突(从您的问题和您希望处理的国家数量,似乎您将处理其中的很多)

为了实现这一点,你显然需要比较谓词来测试你所有状态的内部属性(giroscope,推进器,加速度计,质子射线等)。