【问题标题】:XOR Operator - How does it work?XOR 运算符 - 它是如何工作的?
【发布时间】:2013-08-16 21:05:58
【问题描述】:

您能否用简单的英语解释一下什么是 XOR (^) 运算符以及它在以下代码中的作用:

public int GetHashCode(Box bx)
{
    int hCode = bx.Height ^ bx.Length ^ bx.Width;
    return hCode.GetHashCode();
} 

【问题讨论】:

标签: c# binary bitwise-operators xor


【解决方案1】:

XOR 代表异或。它确保 A 或 B 中的任何一个都为真,但绝不是两者都为真。在这种情况下,我们正在执行按位运算,因此您可以制作一个漂亮的小结果图,它们如下所示;

0 ^ 1 = 1
1 ^ 1 = 0
1 ^ 0 = 1
0 ^ 0 = 0

由于您将其应用于整数,因此上述结果将应用于操作数中的每个位。因此,假设您的高度、长度和宽度分别为 1、2、3。

你会先拥有

0001 ^ 0010 得到 0011 然后它会被异或成 3 所以 0011 ^ 0011 得到 0000

编辑:提供来自 cmets 的 wiki 链接以补充我的解释; http://en.wikipedia.org/wiki/Exclusive_or#Computer_science

编辑:为什么0001 ^ 0010 会导致0011

所以最好一点一点地做到这一点。考虑一下运算符迭代两组位并比较它们的对。所以在这种情况下,让我们从右到左工作(在这种情况下对大多数人来说最不重要)。

1 ^ 0 = 1 // xxx1
0 ^ 1 = 1 // xx11
0 ^ 0 = 0 // x011
0 ^ 0 = 0 // 0011  - end of input

如此拼凑起来,你会得到0011。基本上,获取每对输入并参考真值表以获得结果。注释显示输出,x 是一个尚未计算的值。

关于碰撞,是的,在这种情况下有很多碰撞。如果我说它是独一无二的,那是一个糟糕的词选择。我真正的意思是,如果你有 2、8、4 作为你的值,按顺序对它们进行 XOR'n 将始终产生相同的值。

【讨论】:

  • @Groostav 也对此进行了进一步扩展,之所以在这里完成,是因为a ^ b ^ c 的结果对于这些值将始终相同。在获取散列之前,他们基本上是将所有值融合为一个,以便散列对于 a、b 和 c 的值是唯一的。
  • “按位运算”是什么意思?
  • @YairNevet 按位表示它正在对二进制值一一进行操作。正如您在我的示例中看到的,我选择了整数值 1、2 和 3。0001、0010 和 0011 是它们的二进制表示。位运算符对这些值而不是这些值的整数表示进行操作。因此,如果我们使用 ASCII(我不知道它是什么字符),某个字符的值是 0001,它会被视为与整数 1 完全相同,因为它们的二进制表示是相同的。
  • @YairNevet 您可以查看 wiki 文章以获取更多信息 en.wikipedia.org/wiki/Bitwise_operation 我认为我的解释不是很清楚......但基本上所有类型在某种程度上都以二进制表示,按位运算符有效在二进制值上,而不是二进制表示的值。
  • @YairNevet 整数通常为 32 位或 64 位,具体取决于您使用的操作系统和语言。您可以用 4 位表示的最大整数值为 15。我选择使用那些低数字,因为它们只需要几位。如果您要检查这些整数的二进制文件,值将是相同的,但左侧还有 28 个零。
【解决方案2】:

详细说明一下,您会在getHashCode() 方法中的字段之间看到很多XORing,因为它是获取对象签名 的一种安全方式。签名的概念是它就像一个对象的指纹,它需要适合 32 位。许多对象使用此签名作为快速比较,(但是,如果您打算为此使用它,请查看该维基百科文章,因为您需要注意相等性和哈希码),或者一些一种寻址方式(例如 .net 的 Dictionary 和 Java 的 HashMap)。

对我来说,获取 Box 指纹的明显解决方案是简单地将这些值相加,这样如果其中任何一个发生变化,您将获得不同的指纹: bx.Height + bx.Length + bx.Width

如果我们需要测试两个盒子的相等性,那么等号操作可能非常昂贵(即非常慢):

  • Box {5, 10, 15}
  • Box {30, 40, 50}

我们可以比较两个哈希码,看看它们不同,而不是进行完全相等比较,然后跳过完全相等比较。在字典中,这对于给我们一个快速的方法来找到一个 bin(一个元素)来放置对象是至关重要的。

但如果其中任何一个值太高,我们可能会得到整数溢出异常,因此我们不使用加法,而是使用 XOR。另一种解决方案,对于 C# 来说相当独特,是使用 unchecked{ ... } 块,但使用 XOR 被认为更优雅。

我们可以做一件更微妙的事情来提高性能,你会看到很多自动生成的哈希码方法(例如由 ReSharper 或 IntelliJ 生成的方法): 我们可以通过移动(乘)每个值来使顺序很重要。

    public int hashCode() {
        int result = x;
        result = 31 * result ^ y;
        result = 31 * result ^ z;
        return result;
    }

现在发生的事情是,哈希码中的每个字段实际上在生成的 32 位中都有一个位置。这意味着这两个框:

  • Box {1, 20, 30}
  • Box {1, 30, 20}

不会有相同的哈希码(它们与您当前的系统会有相同的哈希码,但它们是不同的!)

关于哈希码,你想知道的比你想知道的还要多,但我还要说一件事。

在 Java/Scala 和 .net 框架中,如果您重载 equals 或 hash-code,您必须也重载另一个。您必须还确保如果两个对象 A 和 B 具有不同的哈希码,则对 A.Equals(B) 的调用必须为假。

【讨论】:

    猜你喜欢
    • 2011-02-10
    • 1970-01-01
    • 2011-02-19
    • 2015-02-27
    • 1970-01-01
    • 2016-10-25
    • 2010-12-03
    • 1970-01-01
    • 2017-10-09
    相关资源
    最近更新 更多