【问题标题】:Commutative hash function for uint32_t value pairsuint32_t 值对的交换散列函数
【发布时间】:2013-07-19 21:13:47
【问题描述】:

我需要一个快速、简单的哈希函数,它为一对 uint32_t 值创建一个唯一标识符 - 所以 (2,7)(7,2) 的哈希值相同。

有什么想法吗?

【问题讨论】:

  • 通过位移两个数字中较小的(或较大的)并将另一个相加来创建一个 uint64。然后你只需要散列 64bit int。 (或者,让哈希函数复制该对,然后交换元素以保证顺序并在该对上应用真正的哈希)
  • @DavidRodríguez-dribeas:是的,同时我想出了解决方案,谢谢。我已经有了它的 bitshift 的东西,但你记得我比较是魔法。

标签: c++ algorithm hash uint32


【解决方案1】:
constexpr uint32_t hash_max = ...;    

constexpr uint32_t commutative_hash(uint32_t i, uint32_t j) {
   return (i*j + (i*i)*(j*j) + (i*i*i)*(j*j*j)) % hash_max;
};

额外的括号用于编译器 - 优化此表达式会更容易。

不要使用任何条件指令(或std::max/std::min) 如果你想制作一个快速的功能,它会破坏 CPU 管道(并且很慢)。

【讨论】:

  • 谢谢,但是由于很多乘法,这非常慢。看我的回答。
  • 我敢打赌,我的功能更快。您是否将您的功能与我的功能进行了比较?我在答案中添加了解释为什么它更快。
  • @LeonidVolnitsky +1 以获得正确的解释。在某些情况下,条件语句可能会混淆分支预测。
  • @LeonidVolnitsky:确实你是对的,你的更快。我很惊讶条件语句会减慢速度。你能详细解释一下吗?
  • 在带有管道的现代 CPU 上 - 是的。另见sites.google.com/site/dataanxiety/blog/…
【解决方案2】:

回答我自己的问题,解决办法是:

uint64_t hash(uint32_t x, uint32_t y)
{
    const uint64_t a = static_cast<uint64_t>(x);
    const uint64_t b = static_cast<uint64_t>(y);

    if (x < y) return (b << 32) | a;
    else return (a << 32) | b;
}

可以改进为无分支版本

uint64_t hash(uint32_t x, uint32_t y)
{
    const uint64_t a = static_cast<uint64_t>(x);
    const uint64_t b = static_cast<uint64_t>(y);

    const uint64_t h0 = (b << 32) | a;
    const uint64_t h1 = (a << 32) | b;

    return (x < y) ? h0 : h1; // conditional move (CMOV) instruction
}

这些方法是完美的哈希函数 - 它们保证零冲突。但是,它们的缺点是您无法对高于 2^32 - 1 的值进行哈希处理。

【讨论】:

  • 我喜欢转换的想法,它很自然,不需要证明唯一性。如果您想处理大于 2^32 的值,您可以返回一个字符串作为唯一标识符,在其中保留一个特殊符号来分隔哈希的两个部分(将表示基数更改为大于 10 也是一个好主意)跨度>
  • “将表示基数更改为大于 10 也是一个好主意” - 你是什么意思?
  • 如果您将数字表示为字符串,那么您可以使用比 {0,1, ..., 9} 更大的字母,例如十六进制甚至更大。基数越大,表示越短。
  • 当我和你有完全相同的需求时,我使用了if (x&gt;y),但现在我想起来,hash(x) ^ hash(y) 也会这样做(问题是所有对相同的元素(x, x) 具有相同的哈希值。这对某些应用程序来说可能是个问题)
  • @plasmacel signed int 右移是实现定义的,并且您的 max &lt;&lt; 32 调用未定义的行为(因为您正在移动数量 >= 数据类型大小)。只有在| 之后,才能保证将结果转换为uint64_t。实际上,您的编译器无论如何都在进行 64 位计算,但这不能确定。
猜你喜欢
  • 1970-01-01
  • 2021-08-06
  • 2012-06-14
  • 1970-01-01
  • 2017-02-08
  • 1970-01-01
  • 2014-01-08
  • 1970-01-01
  • 2014-01-02
相关资源
最近更新 更多