哈希32位int到16bit int?

时间:2010-06-17 00:32:18

标签: javascript hash integer

将32位整数(例如IP地址,例如Unix time_t等)散列为16位整数的简单方法是什么?

E.g。 hash_32b_to_16b(0x12345678)可能会返回0xABCD

让我们从这开始作为一个可怕但功能性的示例解决方案:

function hash_32b_to_16b(val32b) {
    return val32b % 0xffff;
}

问题是关于JavaScript的,但可以随意添加任何与语言无关的解决方案,最好不使用库函数。

此问题的上下文是生成唯一ID(例如,64位ID可能由多个32位值的多个16位哈希组成)。避免碰撞很重要。

简单=好。古怪+混淆=有趣。

6 个答案:

答案 0 :(得分:5)

我认为这是你最好的。你可以将代码压缩成一行,但var现在作为文档存在:

function hash_32b_to_16b(val32b) {
    var rightBits = val32b & 0xffff; // Left-most 16 bits
    var leftBits = val32b & 0xffff0000; // Right-most 16 bits

    leftBits = leftBits >>> 16; // Shift the left-most 16 bits to a 16-bit value

    return rightBits ^ leftBits; // XOR the left-most and right-most bits
}

考虑到问题的参数, best 解决方案将使每个16位散列恰好对应于2 ^ 16个32位数。它也会以不同方式对IMO散列顺序32位数字。除非我遗漏了什么,否则我相信这个解决方案可以做到这两点。

我认为安全性不能成为这个问题的考虑因素,因为散列值太少了。我相信我提供的解决方案可以将32位数字均匀分布到16位哈希

答案 1 :(得分:3)

这取决于整数的性质。 如果它们可以包含一些位掩码,或者可以通过2的幂来区分,那么简单的XOR将具有很高的冲突概率。 您可以尝试类似(i>>16) ^ ((i&0xffff) * p)的内容,其中p是素数。

像MD5这样的安全哈希都很好,但在这里显然是一种矫枉过正。任何比CRC16更复杂的东西都是过度的。

答案 2 :(得分:2)

我想说只需应用sha1或md5之类的标准哈希值,然后获取最后16位。

答案 3 :(得分:2)

假设您期望最低有效位“变化”最多,我认为您可能只需使用该值的低16位作为哈希就可以获得足够好的分布。

如果您要哈希的数字不具有这种分布,那么在高16位中进行xor-ing的附加步骤可能会有所帮助。

当然,这个建议是,如果您打算仅将哈希用于某种查找/存储方案,并且不寻找与非可猜测性和不可逆性相关的加密相关属性(其中xor-建议并没有真正买你。)

答案 4 :(得分:2)

最大程度地保留某些原始32位“信号”的熵的关键是确保32个输入位中的每一个都具有 独立且相等的能力 更改16位输出字的值。

由于OP要求的比特大小恰好是原始大小的一半,因此满足此条件的最简单方法是对上半部分和下半部分进行 XOR ,如其他人所述。使用 XOR 是最佳选择,因为(如 XOR 的定义obvious一样)-保证独立翻转32个输入位中的任何一个都可以更改16位输出。

当您需要进一步缩小尺寸而不仅仅是一半大小时,例如从 32位输入 > 2位输出。请记住,目标是尽可能地保留源头的熵,因此,用(i & 3)天真掩盖两个最低位的解决方案通常会朝错误的方向前进;这样做保证,除了未屏蔽的位 之外,没有任何其他位会影响结果,这通常意味着运行时信号中有一个任意的,可能有价值的部分,即被无原则地立即丢弃。

从上一段落开始,您当然可以再进行 XOR 三次,以产生2位输出,并希望每个输出均受到平等影响 /任何输入位。当然,该解决方案仍然是最佳的正确解决方案,但是涉及到循环或多个展开的操作,事实证明,这些操作或不必要的操作!

幸运的是,有一种很好的技术,只需 两次操作 即可提供 可证明的最佳结果 这个情况。与 XOR 一样,它不仅可以确保对于任何给定的32位值,将任意一个输入位旋转一下,都可以更改(例如)2位输出值,而且2位输出值的分布是完全均匀的。换句话说,在4,294,967,296个可能的输入值上,四个可能的2位散列结果1,073,741,824中的每一个都将精确地有{ 0, 1, 2, 3 }

我在这里提到的方法使用了我通过详尽搜索发现的特定魔术值,并且似乎在互联网上的其他地方并未对此进行过多讨论,至少对于此处正在讨论的特定用途(即,确保统一最大保留熵的哈希分布)。奇怪的是,根据同样的详尽搜索,魔术值实际上是唯一的,这意味着对于每个目标位宽{ 16, 8, 4, 2 },我下面显示的魔术值都是 only 值,当我在这里使用时,它满足上面概述的完美哈希标准。

事不宜迟,将32位哈希到n = { 16, 8, 4, 2 }的唯一且数学上最优的过程是乘以与n对应的魔术值(无符号,丢弃溢出) ),然后获取结果的n 最高位。要将这些结果位隔离为[0 ... (2ⁿ - 1)]范围内的哈希值,只需将乘法结果右移(无符号!)32 - n位即可。

“魔术”值和类似于C的表达式语法如下:

  

最大程度保留熵的哈希值,用于从32位减少到......

Target Bits    Multiplier    Right Shift          Expression
-----------   ------------   -----------   -----------------------
    16         0x80008001        16        (i * 0x80008001) >> 16
     8         0x80808081        24        (i * 0x80808081) >> 24
     4         0x88888889        28        (i * 0x88888889) >> 28
     2         0xAAAAAAAB        30        (i * 0xAAAAAAAB) >> 30
     


注释:

     
      
  1. 使用无符号32位乘法并丢弃任何溢出(不需要64位乘法)。
  2.   
  3. 如果使用右移隔离结果(如图所示),请确保使用 unsigned 移位操作。
  4.   


[编辑:添加了用于64位输入值的表]

  

最大程度地保持熵的散列,用于将64位值减小为...

Target Bits   Multiplier           Right Shift              Expression
-----------   ------------------   -----------   -------------------------------
    32        0x8000000080000001       32        (i * 0x8000000080000001) >> 32
    16        0x8000800080008001       48        (i * 0x8000800080008001) >> 48
     8        0x8080808080808081       56        (i * 0x8080808080808081) >> 56
     4        0x8888888888888889       60        (i * 0x8888888888888889) >> 60
     2        0xAAAAAAAAAAAAAAAB       62        (i * 0xAAAAAAAAAAAAAAAB) >> 62



进一步的讨论

我发现所有这些都很酷。实际上,关键的信息理论要求是确保对于任何m-bit输入值及其对应的n-bit哈希值结果,翻转m个源位中的任何一个 总是会导致n-bit结果值 发生一些变化。现在,尽管总共有2ⁿ个可能的结果值,但其中的 其中一个已经“在使用中” ,因为将结果切换为该值不会改变完全没有这样仅剩下2ⁿ - 1个结果值,这些结果值可供单个位翻转产生的整个m个输入值集合使用。

我们来看一个例子;实际上,为了展示这种技术在怪异或彻头彻尾的魔术上似乎是如何与边界相接的,我们将考虑一个更极端的情况,即m = 64n = 2。使用2个输出位,有四个可能的结果值{ 0, 1, 2, 3 }。假设有一个任意的64位输入值0x7521d9318fbdf523,我们将获得其2位哈希值1

 (0x7521d9318fbdf523 * 0xAAAAAAAAAAAAAAAB) >> 62   // result -->  '1'

但是,此结果需要在 64个值集 无值 ,其中一位0x7521d9318fbdf523被切换为 可能具有相同的结果值 。也就是说,这64个 其他 结果中没有一个可以使用值1,而所有结果都必须使用023。当2个输入值中的每个输入值都自私地从其64个对等方中获取输出空间的四分之一时,是否存在同时满足所有条件的解决方案?

足够肯定,可以证明(确实吗?)做一个 ,这是按顺序列出的哈希结果值,按顺序列出了用于翻转0x7521d9318fbdf523(一位一次),从MSB(位置63)下降到LSB(0)。

3 2 0 3 3 3 3 3 3 0 0 0 3 0 3 3 0 3 3 3 0 0 3 3 3 0 0 3 3 0 3 3 
0 0 3 0 0 3 0 3 0 0 0 3 0 3 3 3 0 3 0 3 3 3 3 3 3 0 0 0 3 0 0 3    // <-- no '1' values

如您所见,没有1值,这意味着 源“原样”中的每一位都必须有助于影响结果 (或者,如果您愿意,0x7521d9318fbdf523中每个比特的 de <事实状态为必不可少,以防止结果“不是-1”)。因为无论您对64位输入进行什么单位更改,所以2位结果值将不再是1

请记住,上面显示的“缺失值”表是从对一个随机选择的示例值0x7521d9318fbdf523的分析中转储而来的; 所有其他可能的输入值 都有一个相似的表,每个表都严重缺少其所有者的实际结果值,但在某种程度上在整个集合成员中都是全局一致的。此属性本质上对应于在(本质上是有损的)位宽缩减任务期间最大程度地保留可用熵。

因此,我们看到2⁶⁴个可能的源值中的每一个都在恰好64个其他源值上强加了排除可能结果值之一的约束。令我直觉的是,这64个成员集有不计其数的四分之一,每个成员也都属于63个 other ,看似无关紧要的位旋转集。尽管如此,尽管存在相互纠缠的约束的最令人困惑的困惑,但利用一种(我推测)的分辨率同时满足所有这些要求却是微不足道的。

所有这些似乎都与您在上表中可能注意到的东西有关:即,我看不出任何明显的方法可以将技术扩展到压缩为 1位的情况结果。在这种情况下,只有两个可能的结果值{ 0, 1 },因此,如果有任何/每个给定的(例如)64位输入值仍将其自己的结果总和排除为该64位输入值的全部结果,翻转邻居,那么现在基本上 施加 other ,剩下的只有这64个值。我们在表中看到的数学细分似乎是表示在这种情况下同时进行的结果是一座桥梁。

换句话说, XOR 的特殊'information-preserving' characteristic(即,其豪华可靠的保证,与 AND 相比, OR < / strong>等,它 c̲a̲n̲ w̲i̲l̲l̲ 总是会有所变化),不足为奇的是要花费一定的成本,即对一定数量的肘部空间(至少2位)可以使用。

答案 5 :(得分:0)

像这样的简单......

function hash_32b_to_16b(val32b) {    
    var h = hmac(secretKey, sha512);
    var v = val32b;
    for(var i = 0; i < 4096; ++i)
        v = h(v);
    return v % 0xffff;
}