最大化保留某些原始 32 位“信号”的熵的关键是确保 32 个输入位中的每一个都具有独立且相等的能力改变 16 位输出字的值。
由于 OP 要求的位大小恰好是原始的一半,因此满足此标准的最简单方法是 XOR 上半部分和下半部分,正如其他人所提到的。使用 XOR 是最优的,因为正如 XOR 的定义中的 obvious 一样,独立翻转 32 个输入位中的任何一个都可以保证改变 16-位输出。
当您需要进一步减小 一半大小(例如从 32 位输入 到 >2 位输出。请记住,目标是尽可能多地从源中保留熵,因此涉及用(i & 3) 天真地掩盖两个最低位的解决方案通常会朝着错误的方向前进;这样做保证没有任何位除了未屏蔽位影响结果,这通常意味着运行时信号中有一个任意的、可能有价值的部分是无原则地被直接抛弃。
从前面的段落开始,您当然可以再用 XOR 迭代 3 次,以产生一个 2 位输出,该输出具有所需的属性,即被每个 同等影响 /任何输入位。当然,该解决方案仍然是最佳正确的,但涉及循环或多个展开操作,事实证明,这不是必需的!
幸运的是,有一种很好的技术,只有 两个操作 可以提供 可证明的最佳结果这个情况。与 XOR 一样,它不仅确保对于任何给定的 32 位值,旋转任何单个输入位都会导致(例如)2 位输出值发生变化,而且也就是说,给定输入值的均匀分布,2 位输出值的分布也将完全均匀。例如,在4,294,967,296 可能的输入值上,该方法准确地给出四个可能的2 位哈希结果{ 0, 1, 2, 3 } 中的每一个的1,073,741,824。
我在这里提到的方法使用了我通过详尽搜索发现的特定魔法值,并且在互联网上的其他地方似乎没有进行太多讨论,至少对于这里讨论的特定用途(即确保统一最大程度地保持熵的散列分布)。奇怪的是,根据同样的详尽搜索,魔法值实际上是唯一的,这意味着对于每个目标位宽 { 16, 8, 4, 2 },我在下面显示的魔法值是 only 值,当我在这里展示时,它满足上述完美的散列标准。
事不宜迟,将 32 位散列到 n = { 16, 8, 4, 2 } 的唯一且数学上最优的过程是 乘 与对应于 n 的魔法值(无符号,丢弃溢出),然后取结果的n 最高位。要将这些结果位隔离为[0 ... (2ⁿ - 1)] 范围内的哈希值,只需将乘法结果右移(无符号!)32 - n 位即可。
“神奇”值和类C表达式语法如下:
最大限度地保留熵的哈希,用于从 32 位减少到...
目标位乘数右移表达式
----------- ------------ ----------- ---------------- --------
16 0x80008001 16 (i * 0x80008001) >> 16
8 0x80808081 24 (i * 0x80808081) >> 24
4 0x88888889 28 (i * 0x88888889) >> 28
2 0xAAAAAAAB 30 (i * 0xAAAAAAAB) >> 30
注意事项:
- 使用无符号 32 位乘法并丢弃任何溢出(不需要 64 位乘法)。
- 如果使用右移(如图所示)隔离结果,请务必使用 unsigned 移位操作。
[编辑:为 64 位输入值添加了表]
最大熵保留哈希,用于将 64 位值减少到...
目标位乘数右移表达式
------------ ------ ----------- ---------- ---------------------
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 0xAAAAAAAAAAAAAAAAB 62 (i * 0xAAAAAAAAAAAAAAAAB) >> 62
进一步讨论
我发现这一切都很酷。实际上,关键信息理论要求是保证,对于任何m-bit 输入值及其对应的n-bit 哈希值结果,翻转m 源位中的任何一个总是会导致n-bit 结果值发生了一些变化。现在虽然总共有2ⁿ 个可能的结果值,其中一个已经“使用”(由结果本身),因为“切换”到那个从任何其他结果将根本没有变化。这使得2ⁿ - 1 结果值可以被整个m 输入值集使用一个位翻转。
让我们考虑一个例子;事实上,为了展示这种技术看起来是如何接近于怪异或彻头彻尾的魔法,我们将考虑更极端的情况,m = 64 和n = 2。对于 2 个输出位,有四个可能的结果值,{ 0, 1, 2, 3 }。假设一个任意的64位输入值0x7521d9318fbdf523,我们得到它的2位哈希值1:
(0x7521d9318fbdf523 * 0xAAAAAAAAAAAAAAAB) >> 62 // result --> '1'
所以结果是 1 并且声称 没有值在 64 个值的集合 其中0x7521d9318fbdf523 的一位被切换可能具有相同的结果值。也就是说,这 64 个其他结果中没有一个可以使用值1,而所有结果都必须使用0、2 或3。所以在这个例子中,除了 64 个其他输入值之外,似乎 2⁶⁴ 输入值中的每一个都会自私地为自己占用四分之一的输出空间。当您考虑到这些相互作用的约束的绝对规模时,是否存在同时满足总体要求的解决方案?
当然,为了显示(确切地说?)一个确实,这里是哈希结果值,按顺序列出,用于翻转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 // continued…
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 // notice: no '1' values
如您所见,没有 1 值,这意味着源中的每一位“原样”都必须对结果产生影响 (或者,如果您愿意,0x7521d9318fbdf523 中每一位的 de facto 状态对于防止整个整体结果为“not-@”是必要的 987654356@")。因为无论您对 64 位输入进行什么单位更改,2 位结果值都将不再是 1。
请记住,上面显示的“缺失值”表是从仅对一个随机选择的示例值0x7521d9318fbdf523 的分析中得出的; 所有其他可能的输入值都有自己的类似表,每一个都奇怪地缺少其所有者的实际结果值,但在其集合成员中却以某种方式保持全局一致。此属性本质上对应于在(固有有损)位宽缩减任务期间最大程度地保留可用熵。
所以我们看到2⁶⁴ 中的每一个可能的源值都独立地对恰好 64 个其他源值施加了排除一个可能的结果值的约束。与我的直觉相反的是,这 64 个成员的集合中有数万亿个,每个成员也属于 63 个 other,看似无关的位旋转集合。然而不知何故,尽管存在这个最令人困惑的交织约束之谜,但利用一个(我猜想)同时完全满足它们的解决方案仍然是微不足道的。
所有这些似乎都与您可能在上表中注意到的一些事情有关:也就是说,我没有看到任何明显的方法可以将该技术扩展到压缩到 1 位的情况结果。在这种情况下,只有两个可能的结果值{ 0, 1 },因此,如果任何/每个给定(例如)64 位输入值仍然将其自己的结果排除在其所有 64 个单位翻转邻居的结果之外,那么现在基本上 强加 other,只剩下那些 64 的价值。我们在表格中看到的数学分解似乎表明在这种情况下同时出现的结果是一座桥太远了。
换句话说,XOR 的特殊'information-preserving' characteristic(也就是说,它非常可靠地保证,与 AND 不同,OR 等等,它 c̲a̲n̲ 和 w̲i̲l̲l̲ 总是会发生一点变化)毫不奇怪会产生一定的成本,即对一定数量的肘部空间的强烈不可协商的需求— 至少 2 位 — 可以使用。