【问题标题】:hashing a small number to a random looking 64 bit integer将一个小数散列为一个随机的 64 位整数
【发布时间】:2011-12-14 17:45:43
【问题描述】:

我正在寻找一个散列函数,它对一个小整数(比如在 0...1000 范围内)进行运算并输出一个 64 位整数。

结果集应该看起来像 64 位整数的随机分布:均匀分布,结果之间没有线性相关性。

我希望有一个只需要几个 CPU 周期来执行的函数。 (代码将使用 C++)。

我考虑将输入乘以一个大素数并取模 2**64(类似于线性全等生成器),但输出之间存在明显的依赖关系(在低位中)。

谷歌搜索没有显示任何内容,但我可能使用了错误的搜索词。

有这样的功能吗?


一些背景信息:

我想避免在算法中使用带有伪随机数的大型持久表,并即时计算看起来随机的数字。

安全不是问题。

【问题讨论】:

  • 你见过这个吗? stackoverflow.com/questions/5085915/…这两个答案都很有趣!
  • @kol,我之前错过了这个,它看起来确实很有趣。我明天会评论。
  • 如果您的数字介于 0 和 1000 之间,则预先计算的表不会“很大” - 它会消耗大约 8k 的内存。它也是最快的算法。这真的是个问题吗?
  • 8k 可能是很多内存(至少很多缓存;如果查找未命中缓存,则 MurmurHash3 终结器在当前商品硬件上更快),但听起来好像 OP 正在做Zobrist 哈希,所以我也不买它。
  • @per 我更笼统地提出了这个问题,但我确实在尝试实现 Zobrist 散列。我认为这个问题会有更多用途,所以我很惊讶你发现了这一点。我的灵感来自 Linus Torvalds 的一篇关于查找表的帖子:lookup-tables。所以,我想试一试。

标签: algorithm random hash


【解决方案1】:

我测试了 MurmurHash3 的 64 位终结器(由 @aix 和 this SO post 建议)。如果输入为零,这将给出零,所以我首先将输入参数增加了 1:

typedef unsigned long long uint64;

inline uint64 fasthash(uint64 i)
{
  i += 1ULL;
  i ^= i >> 33ULL;
  i *= 0xff51afd7ed558ccdULL;
  i ^= i >> 33ULL;
  i *= 0xc4ceb9fe1a85ec53ULL;
  i ^= i >> 33ULL;
  return i;
}

这里的输入参数i是一个小整数,例如{0, 1, ..., 1000}的一个元素。输出看起来很随机:

i       fasthash(i) decimal:    fasthash(i) hex:
0       12994781566227106604    0xB456BCFC34C2CB2C
1       4233148493373801447     0x3ABF2A20650683E7
2       815575690806614222      0x0B5181C509F8D8CE
3       5156626420896634997     0x47900468A8F01875
...     ...                     ...

系列的后续元素之间没有线性相关性:

两个轴的范围是0..2^64-1

【讨论】:

    【解决方案2】:

    为什么不使用现有的哈希函数,例如带有 64 位终结器的 MurmurHash3?据作者介绍,在当前的 Intel 硬件上,该函数每个键占用数十个 CPU 周期。

    【讨论】:

    • 我的输入很短,所以可能不需要混合步骤。我需要一些时间来弄清楚终结器是否可以独立使用。
    • +1 推荐 MurmurHash3。我在答案中测试了它的终结器。
    【解决方案3】:

    给定:输入 i 在 0 到 1,000 的范围内。

    const MaxInt 是 64 位 int 中包含的最大值。 (你没有说它是签名还是未签名;2^64 = 18446744073709551616)

    还有一个函数 rand(),它返回一个介于 0 和 1 之间的值(大多数语言都有这样的函数)

    计算哈希值 = i * rand() * ( MaxInt / 1000 )

    【讨论】:

    • 我想要无符号的 64 位整数。我应该提到我希望得到可重现的结果,所以 rand() 不能使用。
    • 如果您使用具有相同值的 srand() 播种 rand(),它将产生相同的数字,因此 @JonnyBoats 是正确的。见cplusplus.com/reference/clibrary/cstdlib/srand
    • 通过可重现的结果,您的意思是对于 1,000 个可能的输入值中的每一个,您都会获得 1,000 个可能的哈希值之一?该要求与“结果集应该看起来像 64 位整数的随机分布”相冲突
    • 我认为 OP 的意思是他希望 1000 个输入值的散列分布在 64 位整数空间上。他还期望 f(x) 是确定性的。所以,正如我所说,只要他为 rand() 设定一个常数值,这符合他的标准。
    【解决方案4】:

    1,000 * 1,000 = 1,000,000。这非常适合 Int32。

    从数字中减去范围的下限。 将其平方,并将其用作某种位图的直接下标。

    【讨论】:

    • 这太容易了 :) 他说:“我想避免使用带有伪随机数的大持久表”
    • 我不认为 125k 很大。我的坏:)
    猜你喜欢
    • 2018-04-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-20
    • 2012-10-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多