我正在尝试理解C#中的二元运算符,尤其是^ - exclusive or。
例如:
给出一组正整数。除了一个出现奇数次数的数字之外,所有数字都出现偶数次。在O(n)时间和常数空间中找到数字。
这可以通过^完成,如下所示:对所有元素进行按位异或。最后我们得到奇数出现的数字。
它是如何工作的?
当我这样做时:
int res = 2 ^ 3;
res = 1;
int res = 2 ^ 5;
res = 7;
int res = 2 ^ 10;
res = 8;
实际发生了什么?还有什么其他的魔法?我可以查阅任何参考资料并了解更多信息吗?
答案 0 :(得分:96)
我知道这是一个相当古老的帖子,但我想简化答案,因为我在寻找其他东西时偶然发现了它。
XOR(eXclusive OR /或者),可以简单地翻译为开关。
哪个将排除或包含指定的位。
使用4位(1111),我们从0-15得到16个可能的结果:
decimal | binary | expanded
0 | 0000 |
1 | 0001 |
2 | 0010 |
3 | 0011 | (1+2)
4 | 0100 |
5 | 0101 | (1+4)
6 | 0110 | (2+4)
7 | 0111 | (1+2+4)
8 | 1000 |
9 | 1001 | (1+8)
10 | 1010 | (2+8)
11 | 1011 | (1+2+8)
12 | 1100 | (4+8)
13 | 1101 | (1+4+8)
14 | 1110 | (2+4+8)
15 | 1111 | (1+2+4+8)
二进制值左侧的十进制值是XOR和其他按位操作中使用的数值。
例如:0011
是第1位和第2位,第4位和第8位是关闭的。它表示为3
的十进制值,用于表示打开的位,并以展开的形式显示为1+2
。
至于XOR背后的逻辑是什么,这里有一些例子 来自原帖
2 ^ 3 = 1
- 2是 1 + 2 的成员(3)删除2 = 1
2 ^ 5 = 7
- 2不是 1 + 4 的成员(5)添加2 = 1 + 2 + 4 (7)
2 ^ 10 = 8
- 2是 2 + 8 的成员(10)删除2 = 8
更多例子
1 ^ 3 = 2
- 1是 1 + 2 的成员(3)删除1 = 2
4 ^ 5 = 1
- 4是 1 + 4 的成员(5)删除4 = 1
4 ^ 4 = 0
- 4本身的成员删除4 = 0
1 ^ 2 ^ 3 = 0
逻辑:((1 ^ 2)^(1 + 2))
- (1 ^ 2)1不是2的成员2 = 1 + 2 (3)
- (3 ^ 3)1和2是 1 + 2 的成员(3)删除 1 + 2 (3 ) = 0
1 ^ 1 ^ 0 ^ 1 = 1
逻辑:(((1 ^ 1)^ 0)^ 1)
- (1 ^ 1)1是1删除1 = 0
的成员- (0 ^ 0)0是0删除0 = 0
的成员- (0 ^ 1)0不是1 add 1 = 1
的成员1 ^ 8 ^ 4 = 13
逻辑:((1 ^ 8)^ 4)
- (1 ^ 8)1不是8的成员1 = 1 + 8 (9)
- (9 ^ 4)1和8不是4的成员添加 1 + 8 = 1 + 4 + 8 (13)
4 ^ 13 ^ 10 = 3
逻辑:((4 ^(1 + 4 + 8))^(2 + 8))
- (4 ^ 13)4是 1 + 4 + 8 的成员(13)删除4 = 1 + 8 (9)
- (9 ^ 10)8是 2 + 8 的成员(10)删除8 = 2
- 1不是 2
的成员+8(10) add 1 = 1 + 2 < EM>(3)4 ^ 10 ^ 13 = 3
逻辑:((4 ^(2 + 8))^(1 + 4 + 8))
- (4 ^ 10)4不是 2 + 8 的成员(10)添加4 = 2 + 4 + 8 < EM>(14)
- (14 ^ 13)4和8是 1 + 4 + 8 的成员(13)删除 4 + 8 = 1 强>
- 2不是 1的成员
+ 4 + 8(13) add 2 = 1 + 2 (3)
答案 1 :(得分:53)
要了解它是如何工作的,首先需要以二进制形式编写两个操作数,因为按位运算适用于各个位。
然后,您可以为您的特定运营商应用truth table。它作用于两个操作数中具有相同位置的每对位(相同的位置值)。因此A
的最左边的位(MSB)与B
的MSB组合以产生结果的MSB。
示例:2^10
:
0010 2
XOR 1010 8 + 2
----
1 xor(0, 1)
0 xor(0, 0)
0 xor(1, 1)
0 xor(0, 0)
----
= 1000 8
结果是8。
答案 2 :(得分:31)
另一种表明这一点的方法是使用XOR的代数;你不需要知道任何关于个别位的信息。
对于任何数字x,y,z:
XOR是可交换的:x ^ y == y ^ x
XOR是关联的:x ^ (y ^ z) == (x ^ y) ^ z
标识为0:x ^ 0 == x
每个元素都有自己的反转:x ^ x == 0
鉴于此,很容易证明结果。考虑一个序列:
a ^ b ^ c ^ d ...
由于XOR是可交换和关联的,因此顺序无关紧要。所以对元素进行排序。
现在任何相邻的相同元素x ^ x
都可以替换为0
(自反属性)。并且可以删除任何0
(因为它是身份)。
尽可能重复。任何出现偶数次的数字都有一对整数,所以它们都变为0并消失。
最终你只剩下一个元素,就是出现奇数次的元素。每当它出现两次,那两个就会消失。最终你只剩下一次了。
[更新]
请注意,此证明仅需要对操作进行某些假设。具体来说,假设具有运算符.
的集合S具有以下属性:
x . (y . z) = (x . y) . z
,x
和y
,代理人:z
。
身份:对于S中的所有e
,存在单个元素e . x = x . e = x
,x
。
关闭:对于S中的任何x
和y
,x . y
也在S中。
自反:对于S中的任何x
,x . x = e
事实证明,我们不需要承担交换;我们可以证明:
(x . y) . (x . y) = e (by self-inverse)
x . (y . x) . y = e (by associativity)
x . x . (y . x) . y . y = x . e . y (multiply both sides by x on the left and y on the right)
y . x = x . y (because x . x = y . y = e and the e's go away)
现在,我说“你不需要知道任何关于个别位的信息”。我认为任何满足这些属性的组都足够了,并且这样的组不一定与XOR下的整数同构。
但@Steve Jessup在评论中证明我错了。如果您将标量乘法定义为{0,1}:
0 * x = 0
1 * x = x
...然后这个结构满足整数mod 2的所有axioms of a vector space。
因此,任何此类结构都与分量XOR下的一组位向量同构。
答案 3 :(得分:5)
按位运算符将整数值内的位视为微小的位数。这些位中的每一位都像微小bool
值。当您使用按位异或运算符时,对运算符执行操作的一种解释是:
净效应是单个位开始false
,如果“切换”的总数是偶数,则最后它仍为false
。如果“切换”的总数为奇数,则最后为true
。
只要想想“微小的布尔值数组”,它就会开始变得有意义。
答案 4 :(得分:2)
这是基于一个简单的事实,即一个数字的XOR本身会产生零。
和0的数字的XOR结果表示数字本身。
所以,如果我们有一个数组= {5,8,12,5,12}。
5发生2次。 8发生1次。 12发生了2次。
我们必须找到发生奇数次数。显然,8是数字。
我们从res = 0和XOR开始,包含数组的所有元素。
int res=0;
for(int i:array)
res = res ^ i;
1st Iteration: res = 0^5 = 5
2nd Iteration: res = 5^8
3rd Iteration: res = 5^8^12
4th Iteration: res = 5^8^12^5 = 0^8^12 = 8^12
5th Iteration: res = 8^12^12 = 8^0 = 8
答案 5 :(得分:1)
XOR(异或)运算符在位上的定义是:
0 XOR 0 = 0
0 XOR 1 = 1
1 XOR 0 = 1
1 XOR 1 = 0
想象它的一种方法是,右侧的“1”从左侧改变位,而右侧的0不改变左侧的位。但是,XOR是可交换的,所以如果两侧相反则也是如此。 由于任何数字都可以用二进制形式表示,任何两个数字都可以进行异或。
为了证明它是可交换的,你可以简单地看一下它的定义,并且看到对于每一侧的每个比特组合,如果改变边,结果是相同的。为了证明它是关联的,你可以简单地完成所有可能的组合,即将3个位相互异或,无论顺序如何,结果都将保持不变。
现在,正如我们上面所证明的那样,让我们看看如果我们对自己的相同数字进行异或,会发生什么。由于该操作适用于各个位,因此我们只需要测试两个数字:0和1。
0 XOR 0 = 0
1 XOR 1 = 0
所以,如果你对一个数字进行异或,你总是得到0(信不信由你,但是当需要将0加载到CPU寄存器中时,编译器会使用XOR的属性。执行速度更快有点操作,而不是明确地将0推入寄存器。编译器只会产生汇编代码,以便将寄存器XOR转换为自身。)
现在,如果X XOR X为0,并且XOR是关联的,您需要找出在一系列数字中没有重复的数字,其中所有其他数字已重复两次(或任何其他奇数次) )。如果我们将重复数字放在一起,它们将异或为0.任何与0异或的东西都将保留。因此,在对这样的序列进行异或运算之后,最终会留下一个不重复的数字(或重复偶数次)。
答案 6 :(得分:1)
This有很多不同功能的样本都是通过比特摆弄来完成的。有些可能非常复杂,所以要小心。
理解位操作需要做的是至少:
对于XOR,真值表很简单:
1^1 = 0
1^0 = 1
0^1 = 1
0^0 = 0
要在结果中获取位n
,请将规则应用于第一个和第二个输入中的位n
。
如果您尝试计算1^1^0^1
或任何其他组合,您会发现如果奇数为1则结果为1,否则为0。您还会发现任何与自身进行异或的数字都是0,这与您进行计算的顺序无关,例如1^1^(0^1) = 1^(1^0)^1
。
这意味着当您对列表中的所有数字进行异或时,重复的数字(或呈现偶数次数)将异或为0,而您将只留下出现奇数的数字次。
答案 7 :(得分:0)
从名称(按位)可以明显看出,它在位之间进行操作。 让我们看看它是如何工作的, 例如,我们有两个数字a = 3和b = 4, 3的二进制表示形式是011,4的二进制表示形式是100,因此基本上相同位的异或是0,而相反位则是1。 在给定的示例3 ^ 4中,其中“ ^”是异或符号,将为我们提供111,其十进制值为7。 再举一个例子,如果给定一个数组,其中每个元素除一个元素外都出现两次,并且必须找到该元素。 你该怎么做?相同数字的简单xor始终为0,而恰好出现一次的数字将作为输出。因为任何一个数字为0的输出都将是相同的名称数字,因为该数字将具有置零的设置位。