为什么XOR是组合哈希的默认方式?

Nat*_*ray 135 hash cryptography bit-manipulation probability xor

假设您有两个哈希H(A)并且H(B)您想要将它们组合在一起.我已经读到了将两个哈希值组合在一起的好方法XOR,例如XOR( H(A), H(B) ).

我发现的最佳解释在这里简要介绍了这些哈希函数指南:

对具有大致随机分布的两个数字进行异或,导致另一个数字仍具有大致随机分布*,但现在取决于这两个值.
...
*在两个数字相结合的每个比特,一个输出0,如果两个比特相等,否则为1.换句话说,在组合的50%,1将输出.因此,如果两个输入位各有大约50-50的机会为0或1,那么输出位也是如此.

你能解释为什么XOR应该是组合散列函数(而不是OR或AND等)的默认操作的直觉和/或数学吗?

Yak*_*ont 155

xor是散列时使用的危险默认函数.它比和和更好,但是并没有多说.

xor是对称的,因此元素的顺序会丢失.所以xor哈希结合起来就像and.

xor将相同的值映射为零,并且应避免将"common"值映射为零:

因此,它or被映射到0,并且xor也被映射到0.因为这样的对比随机性更常见,所以你最终会得到比你想象的更多的零碰撞.

有了这两个问题,xor最终成为一个散列组合器,在表面看起来不太合适,但在进一步检查后却没有.

在现代硬件上,通常以与xor一样快的速度添加(它可能会使用更多的功率来实现这一点).添加的真值表与所讨论的位上的xor类似,但当两个值均为1时,它也会向下一位发送一个位.这会擦除较少的信息.

所以"bad"更好的是,如果"dab",结果是而xor不是0.

这仍然是对称的.我们可以以适度的成本打破这种对称性:

hash(a)<<1 + hash(a) + hash(b)
Run Code Online (Sandbox Code Playgroud)

又名(a,a).((b,b)如果您使用班次解决方案,建议计算一次并建议存储).任何奇数常数而不是xor将一个xor(或k位无符号常量)双射映射到自身,因为无符号常量上的映射xor对于某些是数学模数hash(a) + hash(b),并且任何奇数常数都是相对的素数hash(a) xor hash(b).

对于一个更加漂亮的版本,我们可以检查a==b,这是有效的:

size_t hash_combine( size_t lhs, size_t rhs ) {
  lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2);
  return lhs;
}
Run Code Online (Sandbox Code Playgroud)

这里我们将一些hash(a)<<1带有常量的移位版本(基本上是随机"bad"s和"dab"s - 特别是它是黄金比率的倒数作为32位定点分数)加在一起,加上一些加法和xor.这打破了对称性,并且如果输入的散列值很差(例如,假设每个分量哈希值为0),则会引入一些"噪声" - 上面处理得很好,在每个组合后生成拖拽hash(a)*3 + hash(b)hash(a)s.我只需输出a 3).

对于那些不熟悉C/C++的人来说,a k是一个无符号整数值,足以描述内存中任何对象的大小.在64位系统上,它通常是64位无符号整数.在32位系统上,32位无符号整数.

  • 好的,完成......这里是全精度64位常数(用长双精度和无符号长long计算):0x9e3779b97f4a7c16.有趣的是它仍然是均匀的.使用PI而不是黄金比率重新进行相同的计算会产生:0x517cc1b727220a95这是奇数,而不是偶数,因此可能比其他常数"更多素数".我用过:std :: cout << std :: hex <<(unsigned long long)((1.0L/3.14159265358979323846264338327950288419716939937510L)*(powl(2.0L,64.0L)))<< std :: endl; 使用cout.precision(numeric_limits <long double> :: max_digits10); 再次感谢Yakk. (10认同)
  • @Dave 对于这些情况,逆黄金比例规则是第一个 _odd_ 数等于或大于您正在执行的计算。所以只需加 1。这是一个重要的数字,因为 N * 比率的序列,mod 最大大小(此处为 2^64)将序列中的下一个值精确地放置在该比率的最大“间隙”中间数字。在网络上搜索“斐波那契散列”以获取更多信息。 (2认同)

Gre*_*ill 112

假设均匀随机(1位)输入,AND函数输出概率分布为75%0和25%1.相反,OR为25%0和75%1.

XOR函数为50%0和50%1,因此有利于组合均匀概率分布.

通过写出真值表可以看出这一点:

 a | b | a AND b
---+---+--------
 0 | 0 |    0
 0 | 1 |    0
 1 | 0 |    0
 1 | 1 |    1

 a | b | a OR b
---+---+--------
 0 | 0 |    0
 0 | 1 |    1
 1 | 0 |    1
 1 | 1 |    1

 a | b | a XOR b
---+---+--------
 0 | 0 |    0
 0 | 1 |    1
 1 | 0 |    1
 1 | 1 |    0
Run Code Online (Sandbox Code Playgroud)

练习:两个1位输入有多少逻辑函数ab具有这种统一的输出分布?为什么XOR最适合您问题中所述的目的?

  • 回答练习:从16个可能不同的XXX b操作`(0,a&b,a> b,a,a <b,b,a%b,a | b,!a&!b,a = = b,!b,a> = b,!a,a <= b,!a |!b,1)`,以下有0和1的50%-50%分布,假设a和b有50% -50%的0s和1s分布:`a,b,!a,!b,a%b,a == b`,即与XOR(EQUIV)相反的原因也可以使用... (24认同)
  • 格雷格,这是一个很棒的答案.在看到你的原始答案并写出我自己的真值表后,灯泡继续为我.我考虑过@Massa关于如何维护分发的6个合适操作的答案.虽然`a,b,!a,!b`将具有与其各自输入相同的分布,但您将丢失其他输入的熵.也就是说,XOR最适合于组合哈希的目的,因为我们想要从a和b捕获熵. (7认同)
  • 正如[Yakk指出](http://stackoverflow.com/a/27952689/24874),XOR可能很危险,因为它为相同的值产生零.这意味着`(a,a)`和`(b,b)`都产生零,这在许多(大多数?)情况下极大地增加了基于散列的数据结构中发生冲突的可能性. (6认同)
  • @Massa我从来没有见过%用于XOR或不相等. (3认同)
  • @2943 考虑对两个字节进行异或有 256*256 个可能的输入值,而只有 256 个输出值。假设所有三个值都具有相同的选项,则不可能在给定两个输入的情况下得出唯一的输出。 (2认同)

Mar*_*tos 29

尽管它具有方便的位混合特性,但由于其可交换性,XOR 不是结合哈希的好方法.考虑如果将{1,2,...,10}的排列存储在10元组的哈希表中会发生什么.

一个更好的选择是m * H(A) + H(B),其中m是一个很大的奇数.

图片来源:上面的合成器是Bob Jenkins的一个提示.

  • 有时交换性是一件好事,但是xor是一个糟糕的选择*即便如此*因为所有匹配项对都会被调整为零.算术和更好; 一对匹配项的散列将仅保留31位有用数据而不是32位,但这比保留零要好得多.另一种选择可以是将算术和计算为"长",然后将上部与下部重新组合. (2认同)
  • 而不是任何奇数,应该选择一个素数 (2认同)
  • @Infinum在组合哈希时不需要。 (2认同)

Leo*_*adt 17

Xor可能是组合哈希的"默认"方式,但Greg Hewgill的答案也说明了它存在缺陷的原因:两个相同哈希值的xor为零.在现实生活中,有相同的哈希比人们预期的更为常见.然后,您可能会发现在这些(并非如此罕见)的极端情况下,生成的组合哈希值始终相同(零).哈希碰撞会比你预期的要频繁得多.

在一个人为的例子中,您可能会将来自您管理的不同网站的用户的哈希密码组合在一起.不幸的是,大量用户重复使用他们的密码,并且产生的哈希值的惊人比例为零!


Cor*_*urn 8

我希望明确指出找到此页面的其他人.AND和OR限制输出,如BlueRaja - Danny Pflughoe试图指出,但可以更好地定义:

首先,我想定义两个简单的函数,我将用它来解释这个:Min()和Max().

Min(A,B)将返回A和B之间较小的值,例如:Min(1,5)返回1.

Max(A,B)将返回A和B之间较大的值,例如:Max(1,5)返回5.

如果给你: C = A AND B

然后你就会发现C <= Min(A, B)我们知道这一点,因为你无法用A或B的0位来使它们成为1.因此,每个零位保持为零位,并且每一位有机会变为零位(因此值更小).

附: C = A OR B

相反的是:C >= Max(A, B)有了这个,我们看到了AND函数的推论.任何已经是一个的位都不能被OR成为零,所以它保持为1,但每个零位有机会成为一个,因此数字更大.

这意味着输入的状态对输出施加限制.如果你和任何一个90,你知道输出将等于或小于90,无论其他值是什么.

对于XOR,根据输入没有隐含的限制.在某些特殊情况下,您可以发现,如果您使用255对一个字节进行异或,则会得到相反的但是可以从中输出任何可能的字节.每个位都有机会根据另一个操作数中的相同位改变状态.

  • 可以说"OR"是*按位max*,"AND"是*按位min*. (6认同)