一个好的向量散列函数

Posted

技术标签:

【中文标题】一个好的向量散列函数【英文标题】:A good hash function for a vector 【发布时间】:2013-12-28 23:11:18 【问题描述】:

我有一些整数向量,我想在 c++11 的 unordered_map 中有效地存储我的问题是:

如何最好地存储这些并针对.find 查询进行优化?

我想出了以下哈希:

class uint32_vector_hasher 
public:
  std::size_t operator()(std::vector<uint32_t> const& vec) const 
    std::size_t ret = 0;
    for(auto& i : vec) 
      ret ^= std::hash<uint32_t>()(i);
    
    return ret;
  
;

然后将对象存储在 unordered_map 中,不过我有几个问题

    哈希的计算频率是多少,只有一个,一些随机数或次数? 使用== 和散列函数创建一个包装器对象以记住散列并避免重复计算是否有意义?

在进行分析时,我注意到我的大量 cpu 时间花在查找无序地图上,这并不是最佳的 :(

【问题讨论】:

每次插入和每次查找都会进行一次哈希处理,并且每次调整基础表的大小时都可能为每个对象再次处理一次。 澄清一下,系统不会散列表中已经存在的东西,只是因为您正在查找某些内容,而只是您用于查找的键。 顺便说一句,xor 是一个可怕的哈希组合器 一些示例扩展@RichardPlunkett 关于 xor 是一个糟糕的组合器的评论:如果两个向量具有相同的数据但顺序不同,它们将具有相同的哈希值。如果一个向量具有多个相同的值,则这些值中的大多数都不会影响组合散列(它们相互抵消)。如果值通常很小(或者更确切地说,不使用uint32_t 中的全部位),那么最高有效位将不会用于组合散列中。 @Martin,sort 不太可能有帮助,除非它意味着一个特别可怕的例子不会出现,还有很多其他例子。 【参考方案1】:

所以,当不想使用 boost 时,Michael Blurr 的评论导致了以下哈希函数实现:

std::size_t operator()(std::vector<uint32_t> const& vec) const 
  std::size_t seed = vec.size();
  for(auto& i : vec) 
    seed ^= i + 0x9e3779b9 + (seed << 6) + (seed >> 2);
  
  return seed;

似乎有效。

【讨论】:

这是一个起始种子。它会很快被异或操作(^=)变异。 vec.size()(第 2 行)可能会更好,因为它考虑了更多的矢量信息。我已经调整了回复。【参考方案2】:

如果可能,请始终与专家合作: http://www.boost.org/doc/libs/release/doc/html/hash/reference.html#boost.hash_combine

【讨论】:

你知道最近版本的链接失效了吗? 尽管 boost 是一个不错的库,但我还是有点不愿意使用它,因为我的软件需要在多种平台上编译。 @MartinKristiansen:boost 也适用于各种平台。即使你不想或不能使用 boost,@RichardPlunkett 指出的 hash_combine() 函数也只是一个单行:seed ^= hash_value(v) + 0x9e3779b9 + (seed &lt;&lt; 6) + (seed &gt;&gt; 2); @MartinKristiansen 公式中的神奇数字在理论上是否得到证实? @Orient 见***.com/questions/4948780/…【参考方案3】:

boost::hash_combine is good enough but not particularly good

接受的答案已经足够好,但我建议为每个条目使用一个好的散列,然后将它们组合起来。问题是 std::hash 不是一个好的哈希值,boost::hash_combine 不够强大,无法纠正它。

template<typename T>
T xorshift(const T& n,int i)
  return n^(n>>i);


uint32_t hash(const uint32_t& v) 
  uint32_t p = 0x55555555ul; // pattern of alternating 0 and 1
  uint32_t c = 3423571495ul; // random uneven integer constant; 
  return c*xorshift(p*xorshift(n,16),16);


// if c++20 rotl is not available:
template <typename T,typename S>
typename std::enable_if<std::is_unsigned<T>::value,T>::type
constexpr rotl(const T n, const S i)
  const T m = (std::numeric_limits<T>::digits-1);
  const T c = i&m;
  return (n<<c)|(n>>((T(0)-c)&m)); // this is usually recognized by the compiler to mean rotation, also c++20 now gives us rotl directly


class uint32_vector_hasher 
public:
  std::size_t operator()(std::vector<uint32_t> const& vec) const 
    std::size_t ret = 0;
    for(auto& i : vec) 
      ret = rotl(ret,11)^hash(i);
    
    return ret;
  
;

【讨论】:

以上是关于一个好的向量散列函数的主要内容,如果未能解决你的问题,请参考以下文章

设计一个重新散列函数......如何避免相同的散列?

对于 unordered_map,对于具有 3 个无符号字符和一个 int 的结构,啥是好的散列函数?

什么是英语单词的好散列函数?

算法散列表

7.springsecurity密码加密问题

什么是好的哈希函数?