# Maximization with xor operator

Few days ago i found task : with given N numbers only one of those numbers doesn't have pair, which one is it? After hours of surfing the net i found that XOR operator is good for that, because

X xor X=0
X xor 0=X


and

A xor B xor C = B xor A xor C


so we can do xor in any order. But yesterday i've found next nice problem. With given N numbers i have to pick subset of them which XOR is as maximum as possible. I've figured out that if we want to have maximum result we need to xor numbers with totally different(or at least similiar) binary representation, but i think it's too slow approach. How to deal with this one? Cheers

P.S.: SPOJ Problem: http://www.spoj.pl/problems/XMAX/

The Gaussian elimination in this case will work as follows.

First sort the numbers in a non-ascending order. Now visualize the binary representations of all the numbers in a matrix. Consider them to be non-negative 64 bit integers. (Note: you don't have to actually convert them to binary or make the matrix, use bit operations on an array of numbers)

Then carry out the "Forward Elimination" phase of Gaussian Elimination. First find the MSB position (i.e. the first 1) of the very first number. Make sure that all the numbers below it don't have have a '1' at this position. If they do then just XOR them with the number at top and their bits at this position will become zero. After that move on to the next row and next column, and repeat the process. If at anytime you find that the row you are working on does not have a 1 at the desired position, then carry out "Pivoting". Look for any number below that may have a 1 at this position and swap the rows i.e. the numbers. If all numbers below also have 0's then stay at the same row and move on to the next column, and keep repeating the process if all you find are 0's. (For this problem you don't have to be at the last row and last column at the same time.)

Once elimination is done simply traverse each row of the matrix. Have a variable called result which is initially 0. You have to take the first row since it has the highest MSB. By taking I mean carrying out XOR operation with your current result variable. Then move on to the next row and column. If you see that your result has a 0 at this column then XOR with the number at this row since it can only increase your sum or keep it the same. In this way check all the rows, and the result you finally have is the maximum xor sum. (An easier way to think of this would be that you only XOR a row with your current result if it increases the current result.)

What we are doing here is trying to make each number 1 bit shorter than the previous number. This makes it easier for us to think of the XOR operations without thinking of how it may effect the bits to the left. You may have to think a little to understand why this solution is valid.

Let the "length" of a number be the number of digits needed to write it out in binary, excluding any leading zeros. Equivalently, the length of a number is the position of the leading $1$ bit in its binary representation.

Clearly, if all the input numbers had a different length, the problem would have a trivial solution: just iterate over the input numbers in decreasing order by length, choosing each number if and only if XORing it with the maximum so far increases the maximum, i.e. if and only if its leading bit is not set in the current maximum.

Since this is an algorithmic question, let me provide some pseudo-code to illustrate the algorithm I just described:

# Find maximum XOR of input, assuming that all input numbers have
# different length:

let max = 0
for each number n in input (sorted in descending order by length):
if max < (max XOR n): let max = (max XOR n)


(To see why this algorithm works, first observe that each bit in a binary number is "worth" more than all the lower bits combined, so we clearly want to work from top down, first optimizing the highest bits, no matter what this does to the lower bits.

Now assume that we've already considered all the input number longer than $k$ bits, and determined the maximum value the bits higher than $k$ can have in their XOR, and that we're now considering whether to XOR a $k$-bit number — which, by assumption above, is the only $k$-bit number in the input — into the maximum or not. We cannot change the bits above the $k$-th bit by doing so, but we can always ensure that the $k$-th bit in the result is set, by XORing the current input number into the result if and only if the $k$-th bit of the result so far is not yet set. Further, ensuring that the $k$-th bit is set is clearly more important that anything that may happen to the lower bits as a side effect, so we may ignore those bits for now.)

The tricky part is when the input may contain multiple numbers with the same length, since then it's not obvious which of them we should choose to include in the XOR. What we'd like to do is reduce the input list into an equivalent form that doesn't contain more than one number of the same length.

Conveniently, this is exactly what Gaussian elimination does: it transforms a list of vectors into another list of vectors which have strictly decreasing length, as defined above (that is, into a list which is in echelon form), but which still spans the same linear subspace.

The reason this linear algebra algorithm is relevant here is that binary numbers satisfy the axioms of a vector space over the finite field of two elements, a.k.a. GF(2), with the numbers viewed as vectors of bits, and with XOR as the vector addition operation. (We also need a scalar multiplication operation to satisfy the axioms, but that's trivial, since the only scalars in GF(2) are $1$ and $0$.)

The linear subspace spanned by a set of bit vectors (i.e. binary numbers) over GF(2) is then simply the set of vectors obtainable by XORing a subset of them. Thus, if we can use Gaussian elimination to convert our input list into another one, which spans the same subspace, we can solve the problem using this other list and know that it gives the same solution as for the original problem.

Thus, we need to implement Gaussian elimination over GF(2). Fortunately, this turns out to be very simple (in fact, much simpler than doing it for ordinary vectors of real numbers):

1. Find the maximum length $k$ of the (remaining) inputs, and at least one input having that length. Set this input aside.

2. XOR all other inputs of length $k$ with the input set aside in step 1: their length will now become less than $k$.

3. Repeat from step 1, until all remaining inputs have length $0$ (and can therefore be discarded).

4. The inputs set aside during successive iterations of step 1 above will form the new input list. (Conveniently, they will already be in decreasing order by length.)

A useful optimization is to separate the inputs into a number of "buckets" (i.e. variable-length lists) by their length: this makes finding the maximum length, and all inputs having that length, very easy and efficient.

Here's some pseudo-code again:

# Preliminary phase: split numbers into buckets by length
for each number x in input:
let k = length of x
if k > 0: add x to bucket k

# Gaussian elimination:
for each bucket (in decreasing order by k):
if this bucket is empty: skip rest of loop
let x = arbitrary (e.g. first) number in this bucket
remove x from this bucket
add x to modified input list

for each remaining number y in this bucket:
remove y from this bucket
let z = y XOR x
let k = length of z
if k > 0: add z to bucket k


Then just apply the maximum-finding algorithm given above to the modified input list.

(Ps. It's also possible to find the subset of input numbers giving the maximum XOR using this algorithm: for each modified input number, you just need to somehow keep track of which original input numbers it was XORed from. This is basically the same algorithm as using Gaussian elimination to find a matrix inverse, just adapted to bit vectors.)

• Definitely the best answer available! Thanks for explanation. – Ignited Aug 9 '16 at 14:23
• Very clear and intuitive by explaining the problem in GP(2). – river_06 Oct 15 '16 at 3:17
• I don't understand. Suppose there are only 2 binary numbers: 100 and 101. I select 100 as the representative of the 3rd bit bucket, and change the second number to 001. Then I move to the first bit bucket and add 001. So I came up with both numbers in my set. This is obviously wrong, as I should have selected only 101. – ihadanny Aug 4 '18 at 5:16
• @ihadanny: The part you're missing is "then apply the maximum-finding algorithm given above". So you've use Gaussian elimination to turn your original list (100, 101) into the modified list (101, 001), which has the same span as the original list but where each number has a distinct length. Now you apply the maximum-finding algorithm for inputs of distinct length, given at the top of the answer, where you first pick 101 and then check if 101 XOR 001 > 101. It's not, so you reject 001 and just get 101 as your final answer. – Ilmari Karonen Aug 4 '18 at 13:57

Iterate over the bits from highest to lowest, determining for each bit its value in the result. Each bit $i$ is set in the result iff there is a subset whose XOR yields the already determined higher bits and also has bit $i$ set: if there is no such subset, it's not possible for bit $i$ to be set in the result, and if there is one, then any value in which bit $i$ is not set will be lower than the value for that subset.

Thus, the problem is reduced to finding, for each bit $i$, if possible, a subset whose XOR yields the already determined higher bits and also has bit $i$ set. But this is just a system of linear equations over $\mathbb Z_2$, the field with two elements, which can be solved using Gaussian elimination. Since the matrix of this system always has the same rows, with a new row being added for each bit, it might be possible to determine more efficiently whether the system has a solution; this is enough, except for the last bit, for which the system has to actually be solved in order to determine the solution.