使用多个 uint32_t 整数生成 uint64_t 哈希键

Posted

技术标签:

【中文标题】使用多个 uint32_t 整数生成 uint64_t 哈希键【英文标题】:Generate uint64_t hash key with several uint32_t integers 【发布时间】:2010-06-25 12:07:39 【问题描述】:

我面临与 Eduardo (Generate a hash sum for several integers) 相同的问题,但我的问题与标题中所说的略有不同。

我有四个 32 位整数,我需要生成一个 64 位唯一密钥。我现在所做的是生成由'/'分隔的四个整数的字符串连接,然后使用字符串生成一个CRC。

char id_s[64];
sprintf(id_s, "%d/%d/%d/%d", a, b, c, d);
uint64_t id = CRC(id_s);

但问题是我必须这样做几百万次,所以它似乎非常消耗 CPU。所以我在考虑直接将四个整数存储到一个整数中。

如果四个整数是16位整数,这可以很容易地完成。可以使用位移运算符来完成。

uint64_t id = a << 48 + b << 32 + c << 16 + d;

对于四个 32 位整数,我必须将 128 位放入一个 64 位整数中。

有人有什么建议吗?

【问题讨论】:

您不可能为任意 4*32 位整数创建 唯一 64 位密钥,因为它代表 128 位数据。你能明确说明你需要什么吗? 是的,确实不可能摆脱碰撞。我需要的是在不使用 sprintf 和 CRC 计算的情况下生成一个具有四个整数的整数键。还有其他方法可以快速计算该密钥吗? 【参考方案1】:

我认为你最好的选择是使用 xor:

  uint64_t makeId(uint32_t a, uint32_t b, uint32_t c, uint32_t d)
  
     uint64_t id = a;
     id <<=11;
     id ^= b;
     id <<=11;
     id ^= c;
     id <<=10;
     id ^=d;

     return id;
  

如果您的输入分布良好并使用所有 32 位,这将非常有效。就像 Mark 说的,你不能不复制就将 128 位变成 64 位。

【讨论】:

+1:比 MarkB 好得多,因为丢弃位意味着更高的哈希冲突机会。【参考方案2】:

根据您输入数据的性质,与您所建议的差不多的方法可能会正常工作:

uint64_t id = static_cast&lt;uint64_t&gt;(a &amp; 0xFFFFu) &lt;&lt; 48 + static_cast&lt;uint64_t&gt;(b &amp; 0xFFFFu) &lt;&lt; 32 + static_cast&lt;uint64_t&gt;(c &amp; 0xFFFFu) &lt;&lt; 16 + static_cast&lt;uint64_t&gt;(d &amp; 0xFFFFu);

只要值的高位相当恒定而低位相对随机,这应该会让您接近。您试图将 128 位数据塞进 64 位,因此您必须在某处丢弃数据。这只是要丢弃哪些位以及如何丢弃的问题。

【讨论】:

是的,我已经考虑过丢弃一些位,因为一些输入整数只取小值。其中两个可以存储在 16 位中,另外两个可以存储在 48 位中,但仍然是 16 位。

以上是关于使用多个 uint32_t 整数生成 uint64_t 哈希键的主要内容,如果未能解决你的问题,请参考以下文章

为啥 uint32_t 与 uint64_t 速度不同?

比较 uint64_t 和 float 的数值等价性

从uint32_t [16]数组到uint32_t变量序列的64位副本

uint8_t / uint16_t / uint32_t /uint64_t数据类型详解

uint8_t / uint16_t / uint32_t /uint64_t 是什么数据类型 - 大总结

uint8_t / uint16_t / uint32_t /uint64_t 这些数据类型是什么?