XOR如何真正起作用,它背后的魔力是什么?

时间:2017-04-28 07:55:43

标签: java algorithm binary bitwise-operators

问题可能是误导了一点,但我不知道如何以另一种方式提出问题。 hackerrank存在一个问题如下:

  

考虑一个整数数组,其中只有一个整数   成对出现。换句话说,每个元素恰好发生两次   除了一个独特的元素。

     

给定数组查找并打印唯一元素。 [2,3,1,3,2] - >结果是1

enter image description here

我解决了这个问题:

private static int lonelyInteger(int[] a) {
        if(a==null)
            return 0;

         if(a.length<2)
             return a.length;

        Set<Integer> set = new HashSet<>();

        for(int i : a){

            if(set.contains(i)){
                set.remove(i);
            }else{
                set.add(i);
            }            
        }

        return (Integer) set.toArray()[0];
    }

然而,我们认为这个问题有一个简洁的解决方案:

private static int lonelyInteger(int[] a) {
         int b = a[0];

         for(int i = 1; i < a.length; i++){
             b ^= a[i];
         }
        return b;       
    }

问题是我不知道为什么工作? 我理解它是如何工作但不明白为什么它工作? 要理解我制作了一个小程序来输出每个步骤的结果:

public class BitwiseOperator {

    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int[] a = new int[n];
        int sum = 0;
        in.nextLine();
        String line = in.nextLine();
        String[] numbers = line.split(" ");
        for (int i = 0; i < numbers.length; i++) {
            a[i] = Integer.valueOf(numbers[i]);
        }

        for (int i = 0; i < a.length; i++) {
            binary(sum, a[i]);
            sum ^= a[i];
            binary(sum);
            System.out.println();
            System.out.println();
            System.out.println();

        }
        System.out.println(sum);
    }


    private static void binary(int sum) {
        System.out.println(String.format("%16s", Integer.toBinaryString(sum)).replace(' ', '0') + " ->" + sum);
    }

    private static void binary(int sum, int i) {
        System.out.println(String.format("%16s", Integer.toBinaryString(sum)).replace(' ', '0') + " ->" + sum);
        System.out.println("XOR");
        System.out.println(String.format("%16s", Integer.toBinaryString(i)).replace(' ', '0') + " ->" + i);
        System.out.println("--------");
    }


}

如果输入以下输入:

5

2 3 2 1 3

输出结果为:

0000000000000000 ->0
XOR
0000000000000010 ->2
--------
0000000000000010 ->2



0000000000000010 ->2
XOR
0000000000000011 ->3
--------
0000000000000001 ->1



0000000000000001 ->1
XOR
0000000000000010 ->2
--------
0000000000000011 ->3



0000000000000011 ->3
XOR
0000000000000001 ->1
--------
0000000000000010 ->2



0000000000000010 ->2
XOR
0000000000000011 ->3
--------
0000000000000001 ->1



1

所以该程序实际上是可行的,但我真的需要了解为什么?

3 个答案:

答案 0 :(得分:4)

准确的证据,恕我直言,涉及群论(你可以根据xor建立阿贝尔群组):

  1. xor是一个群组操作
  2. 0是一个群组0
  3. A是一个反向元素(因此任何A都与自身相反)。
  4. 当然,我们必须证明(A xor B) xor C == A xor (B xor C)

    由于A xor B == B xor A我们有一个阿贝尔群组,这就是为什么可以重新组合 任何顺序的项目:

      A XOR B XOR C XOR A XOR B ==
     (A XOR A) XOR (B XOR B) XOR C ==
      C
    

    一般情况下:

       A xor B xor ... xor Z ==
      (A xor A) xor (B xor B) xor ... xor (distinct Item) ==
       0 xor 0 xor ... xor (distinct Item) ==
       distinct Item
    

答案 1 :(得分:1)

考虑一个整数数组,其中除了一个整数之外的所有整数都成对出现。换句话说,除了一个唯一元素之外,其中的每个元素都恰好发生两次。现在假设您添加了所有这些数字。

如果这笔钱是均匀的,这意味着什么?这意味着出现一次的数字必须是均匀的。如果总和是奇数,这意味着什么?这意味着出现一次的数字必须是奇数。想一想,直到你理解它为止。

现在想象一下,我们只是跟踪总和是偶数还是奇数,而不是总结它们。因此,如果前两个数字是3和7,那么总和将是10,但我们只记得它是偶数。这仍然有效。即使出现一次的数字是偶数也是奇数,如果出现一次的数字是奇数,最终的答案就是如此。想一想,直到你理解它为止。

这就是我们如何才能使它适用于一些数字。但我们也可以同时为所有位做到这一点,跟踪每个位的位置,无论总数是奇数还是偶数。完成后,我们会看到一次出现的数字是奇数还是偶数位,这就是我们所需要的。由于我们是以二进制方式进行的,因此该位置唯一的奇数是1,唯一的偶数是0.在你理解之前,请考虑这个。

答案 2 :(得分:0)

我将尝试使其简短而简单。

异或相同的数字将返回0

示例: 23 ^ 23 = 0 -325 ^ -325 = 0

因此,当您执行循环的XOR运算时,出现两次的数字将自行抵消,最终您将获得唯一的数字。