【问题标题】:Hash function for 3 shorts3条短裤的哈希函数
【发布时间】:2012-04-06 16:58:26
【问题描述】:

我必须创建一个基于 3 个短裤的哈希函数。这样做的最佳方法是什么?

编辑 我有一个名为 Point 的对象。它由三个短裤(x,y,z)组成。为了在 QSet 中使用这个对象,我必须填写以下函数的主体

uint qHash(const Point &point) {
    // return something here that is a unique combination of x, y, z so that
    // it is very quick to calculate and has minimal (if any) hash collisions
}

【问题讨论】:

  • “基于 3 条短裤”是什么意思?
  • 我已经相应地更新了我的问题。
  • 由于您尝试将 48 位压缩为 32 位,因此必然可能会发生冲突。

标签: hash hashtable hashcode


【解决方案1】:

如果三个短裤分布比较均匀,你可以使用类似的东西:

hashVal = (short1 xor short2 xor short3) modulo numBuckets

这会给你一个简短的,减少到从0numBuckets - 1的特定范围。

这是否合适在很大程度上取决于您的输入值将如何分布以及您对散列函数的期望。

根据您的问题编辑说明哈希必须进入无符号整数,并假设 16 位短整数和 32 位无符号整数,没有办法完全避免冲突(您需要 48 位) .一种可能性是使用:

hashVal = (x leftshift 16) logical-or (y leftshift 8) logical-or (z)

这将结合(与逻辑或)您的值:

xxxxxxxxxxxxxxxx0000000000000000
        yyyyyyyyyyyyyyyy00000000
                zzzzzzzzzzzzzzzz

并且至少将模拟x/y/z 值相互影响的可能性降到最低。

并且,根据您的评论:

我希望我的输入值在 0 到 512 的范围内。这将如何影响我的决定?

如果您的输入值限制在 0 到 512(含)的范围内,则每个值只需要 10 位(这将为您提供 0 到 1023 的值)。在这种情况下,它们中的三个将很容易放入 32 位无符号整数中,因此您可以使用:

hashVal = (x leftshift 20) logical-or (y leftshift 10) logical-or (z)

这提供了一个完美的散列,绝对没有冲突的机会。

【讨论】:

  • 什么,SO 上的发布时间没有亚秒级分辨率? :-) 第一个与我去过的第二个完全相关。
  • 实际上,@Eric,当我将鼠标悬停在“X 分钟前”文本上时,我会在 5:49:39 收到你的,在 5:49:40 收到我的,所以我想你可能已经点了我在帖子里:-)
  • 我希望我的输入值在 0 到 512 的范围内。这将如何影响我的决定?
  • @paxdiablo:有趣的是,他们必须分别调用 DateTime.Now 来渲染每个“X 秒前已回答”的文本,因为它们在发布后都显示为“17 秒前”。
  • @Jon:Pax 的解决方案假定输入分布良好,如果您期望 0 到 512,情况并非如此。我的解决方案乘以 31 的幂,以便在与 XOR 结合之前更好地分配位,从而减轻那个问题。
【解决方案2】:

这在很大程度上取决于您对哈希函数的需求。

速度很重要吗?

近乎完美的哈希分布是否重要?

您的哈希键必须有多大? 32位? 64位?更大?

在不了解任何其他细节的情况下,您可能需要考虑以下方面的内容:

uint hash = (31 * 31 * 31 * (uint)short1) ^ (31 * 31 * (uint)short2) ^ (31 * short3);

这将很快并且应该有合理的比特分布,即使短裤的输入值分布不均匀

更新:

将代码示例修改为键入uint。如果输入在 0 到 512 范围内,我的变体应该可以正常工作。

如果您有兴趣了解为什么我将每个输入乘以 31 的幂,请参阅

Why does Java's hashCode() in String use 31 as a multiplier?

【讨论】:

  • 我在我的问题中添加了更多信息。
  • 虽然 31 在要处理的值数量不限的情况下是一个不错的乘数,但我认为如果知道其中正好三个值,一些更大的值会更好.
  • @supercat:这是一个有效的考虑,尽管我缺乏密码学理论背景来证明或反驳该陈述。根据我所阅读的内容,立方数约为 2^24 的素数(取决于可能的输入范围)可能是更好的选择。针对 31 个基数与更大的素数基数运行一个给定的样本,比如 1,000,000 个有效 (x,y,z),可以合理地了解这两个选项的性能。每次运行使用相同的输入,并计算哈希冲突的数量。使用Dict<int,int>(键是散列,值是带有该散列的输入#)
  • @EricJ.:我认为不需要密码学理论。如果相乘的数字永远不会大到足以用完所有 2^32 个可能的Integer 值,但某些哈希值是通过多个输入组合获得的,则哈希函数肯定会产生次优分布,并且在不影响性能的情况下,分布可能会得到改善。使用 31 作为乘数,散列函数不会生成超出大约 +/- 32,540,000 范围的值,即使散列值的范围约为 +/- 2,147,000,000。
  • I don't think one needs cryptographic theory... 你总是需要密码理论背景 :-) 至少有很多微妙的事情可能会与非专业人士的假设和不提供免责声明让我感到不舒服的实现。如果n > 40,n^3 将不适合 16 位 ushort,因此 (n^3) * z 将包装 32 位 uint。
猜你喜欢
  • 1970-01-01
  • 2023-02-01
  • 2012-02-12
  • 2016-12-22
  • 1970-01-01
  • 2012-08-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多