8

Here is the original code:

public static String reverseString(String s){       
    if(s == null) return "";        
    char[] rev = s.toCharArray();
    int i = 0, j = s.length() - 1;
    while(i < j) {
        rev[i] ^= rev[j];
        rev[j] ^= rev[i];
        rev[i++] ^= rev[j--];           
    }       
    return String.valueOf(rev); 
}

My question is how does Xor work in swapping character values here, and why is rev[i++]^=rev[j--] needed here?

scionx
  • 167
  • 1
  • 6
  • 1
    XOR is the exclusive-OR. It means "x or y, but not x and y." – Zarwan Oct 27 '16 at 04:00
  • `rev[i++]^=rev[j--]` same with: `rev[i]^=rev[j];i++;j++;` it swaps one more time then increment `i` and `j` – malioboro Oct 27 '16 at 04:06
  • In a high level language it is much clearer (and in this case probably even faster) to swap the "normal" way with a temporary variable. The XOR trick is mainly used in assembly language when you want to swap two registers and don't have a third one free. – Henry Oct 27 '16 at 04:18
  • Simply put , it is just a mathematical trick. Check [my answer](http://stackoverflow.com/a/40328451/5671993) which explains that – Vikhram Oct 30 '16 at 11:20

5 Answers5

15

The code is equivalent to

    rev[i] ^= rev[j];
    rev[j] ^= rev[i];
    rev[i] ^= rev[j];           
    i++;  j--;

The last part is just needed to increment i and decrement j for the next loop iteration.

As to why x ^= y; y ^= x; x ^= y works to swap the values, I don't know why, but you can see that it works on 1-bit values by look at all four possibilities:

 start   after x^=y  after y^=x   after x^=y
x    y     x   y       x   y        x   y
0    0     0   0       0   0        0   0
0    1     1   1       1   0        1   0
1    0     1   0       1   1        0   1
1    1     0   1       0   1        1   1

So you can see that in all cases, the x and y bits are swapped. When the statements are applied to larger integers, the ^ operator works on all bits in parallel, so the end result is that every pair of bits is swapped, i.e. the entire values are swapped.

ajb
  • 31,309
  • 3
  • 58
  • 84
2

XOR operator has this very unique operator that it acts as inequality detector meaning only when the two bits differ result will be 1 else result is 0.

Now take this for example say in A^B, ith bit 1, this means that ith bit of A and B differ. Meaning one of them is 1 and the other is 0.

Now when we do this (A^B)^B , if the ith bit in B was 0, what we will get is 1 since 1^0 = 1, which is equal to ith bit in A and (A^B)^A = 0, which is ith bit in B.

Similarly,When ith bit is B is 1 and in A is 0, again swapping occurs.

Same logic applies to when ith bit in A^B is 0. You can veryify it very easily.

Its easy to see how the swapping is occurring, When you xor the original number with A^B, you get the other number, because swapping happens for each of the respective bits

Sumeet
  • 8,086
  • 3
  • 25
  • 45
0

The below routine is expected to swap the values of a and b

a = a ^ b
b = b ^ a
a = a ^ b

Let's analyze to see how and why the swap works.

For that, let's not swap the values, but store them in separate variables, so we can see what exactly goes on.

a0 = a ^ b
b1 = b ^ a0
a1 = a0 ^ b1

Simplifying the equations using below properties of XOR. Check XOR Properties@Wikipedia for reference

  1. commutative (a ^ b == b ^ a)
  2. associative (a ^ (b ^ c) == (a ^ b) ^ c)
  3. a ^ a == 0
  4. 0 ^ a == a

a0 = a ^ b                      // Equation #1
b1 = b ^ a0
a1 = a0 ^ b1

b1 = b ^ a0                     // Equation #2
   = b ^ (a ^ b)                // Using Equation #1
   = b ^ (b ^ a)                // Property #1
   = (b ^ b) ^ a                // Property #2
   = 0 ^ a                      // Property #3
   = a                          // Property #4

a1 = a0 ^ b1
   = a0 ^ (b ^ a0)              // Using Equation #2
   = (a ^ b) ^ (b ^ (a ^ b))    // Using Equation #1
   = (b ^ a) ^ (b ^ (b ^ a))    // Using Property #1
   = (b ^ a) ^ ((b ^ b) ^ a)    // Using Property #2
   = (b ^ a) ^ (0 ^ a)          // Using Property #3
   = (b ^ a) ^ a                // Using Property #4
   = b ^ (a ^ a)                // Using Property #2
   = b ^ 0                      // Using Property #3
   = b                          // Using Property #4

As you can see, b1 now contains the original value of a and a1 contains the original value of b, i.e. the values of b and a are swapped

In summary, a^=b;b^=a;a^=b is just an idiomatic expression, nothing magical in it :)

Plain English explanation for the same

XOR sets the bits when the operand bits are dissimilar and resets the bits otherwise

Let's walk through the transformations that take place with an example. For that let's say, we have the following numbers (in binary) assigned to the variables.

a = 1 1 0 0
b = 1 0 1 0

Step #1: a = a ^ b // CREATE A MASK

a = 0 1 1 0
b = 1 0 1 0

Imagine the new value of a is a mask for generating the old value of a given b or generating the old value of b given a.

Step #2: b = b ^ a // Recover original value of a using mask and original value of b

a = 0 1 1 0
b = 1 1 0 0

Since b is still preserved/untouched, we can recover original value of a with the mask - which is what we did in this step

Step #3: a = a ^ b // Recover original value of b using mask and original value of a

a = 1 0 1 0
b = 1 1 0 0

Now we have the original value of a in variable b, so we can use our same mask to recover the original value of b. We can overwrite the mask now, since we don't need the mask after this step.

Vikhram
  • 4,294
  • 1
  • 20
  • 32
0

If you will agree that y == (x^y)^x == x^(y^x), then you have the answer.

Consider an abstract version of the loop body in the code you gave:

a = a ^ b
b = b ^ a
a = a ^ b

Now rename one value to clarify what's happening:

a_xor_b = a ^ b
b = b ^ a_xor_b // Now b is the original a because b^(a^b) == a!
a = a_xor_b ^ b // Now a is the original b because (a^b)^a == b!

Now note the code works fine if a_xor_b is the same variable is a.

Gene
  • 46,253
  • 4
  • 58
  • 96
0

It may be easier to first consider a different (but closely related) way of swapping two number values a and b:

a =  a + b;
b =  a - b;
a = -b + a;

This works both with pure arbitrary-precision numbers, and with integers modulo N (integers that wrap around when they get too big or small, like they do in Java).

To analyze this mathematically, we should assign a new symbol each time a value would change so that = can represent mathematical equality instead of assignment. Then, it is just a matter of basic algebra.

a1 = a0 + b0
b2 = a1 - b0 = (a0 + b0) - b0 = a0
a2 = -b2 + a1 = -(a0) + (a0 + b0) = b0

What about XOR? One way to explain XOR is to think of it as binary addition without carrying. Performing a swap with XOR is then equivalent to performing the "addition swap" described above on each bit modulo 2. The expression can be simplified, though, since in addition modulo 2, each number is its own inverse (equivalently, addition and subtraction are the same). This (with commutativity) gives us the familiar:

a = a ^ b;
b = b ^ a;
a = a ^ b;

In general, the "addition swap" above can be performed in any mathematical group (even if it is non-commutative -- basically just associativity and inverses are needed). Another way of thinking of XOR is just that it induces a group structure on the n-bit integers, and so swapping works just as it does in any group.

tehtmi
  • 676
  • 4
  • 7