简体   繁体   中英

How does XOR really works, and what is the magic behind it?

The question is maybe misleading a little bit but I didn't know how to ask it another way. There is a problem in hackerrank that goes as follow:

Consider an array of integers, where all but one of the integers occur in pairs. In other words, every element in occurs exactly twice except for one unique element.

Given the array find and print the unique element. [2,3,1,3,2] -> result is 1

在此输入图像描述

I solved the problem like this:

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];
    }

However it turned our that there is a neat solution to this problem which is:

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

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

The problem is that is I don't know WHY IT WORKS ?! I understand HOW it works but don't understand WHY IT WORKS ? To understand that I made a small program to output the results of each step:

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("--------");
    }


}

If you enter the following input:

5

2 3 2 1 3

The output is:

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

So the program actually works BUT I Really need to understand WHY ?

An accurate proof, IMHO, involves group theory (you can build abelian group based on xor ):

  1. xor is a group operation
  2. 0 is a group 0
  3. A is an inverse element (so any A is inverse one to itself).

Of course we have to prove that (A xor B) xor C == A xor (B xor C)

Since A xor B == B xor A we have an abelian group and that's why can regroup items in any order:

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

In general case:

   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

Consider an array of integers, where all but one of the integers occur in pairs. In other words, every element in it occurs exactly twice except for one unique element. Now imagine you add all those numbers.

What does it mean if that sum is even? It means that the number that appears once must be even. What does it mean if that sum is odd? It means that the number that appears once must be odd. Think about this until you understand it.

Now imagine that instead of summing them, we just kept track if the sum was even or odd. So if the first two numbers were 3 and 7, the sum would be 10, but we'd just remember that it was even. This would still work. The final answer would be even if the number that appears once is even and odd if the number that appears once is odd. Think about this until you understand it.

So that's how we could make it work for one bit of the numbers. But we could also do that for all the bits at the same time, tracking for each bit position whether the total was odd or even. When done, we'd have whether the number that appeared once was odd or even for each bit position, and that's all we need. Since we're doing it in binary, the only odd number for that position is 1 and the only even one is 0. Think about this until you understand it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM