Maximize XOR Equation

Viewed 452

Problem statement:

Given an array of n elements and an integer k, find an integer x in the range [0,k] such that Xor-sum(x) is maximized. Print the maximum value of the equation.

Xor-sum(x)=(x XOR A1)+(x XOR A[2])+(x XOR A[3])+…………..+(x XOR A[N])

Input Format

The first line contains integer N denoting the number of elements in A. The next line contains an integer, k, denoting the maximum value of x. Each line i of the N subsequent lines(where 0<=i<=N) contains an integer describing Ai.

Constraints

1<=n<=10^5
0<=k<=10^9
0<=A[i]<=10^9

Sample Input

3
7
1
6
3

Sample Output

14

Explanation

Xor_sum(4)=(4^1)+(4^6)+(4^3)=14.

This problem was asked in Infosys requirement test. I was going through previous year papers & I came across this problem. I was only able to come up with a brute-force solution which is just to calculate the equation for every x in the range [0,k] and print the maximum. But, the solution won't work for the given constraints.

My solution

#include <bits/stdc++.h>
using namespace std;
int main()
{
  int n, k, ans = 0;
  cin >> n >> k;
  vector<int> a(n);
  for (int i = 0; i < n; i++) cin >> a[i];
  for (int i = 0; i <= k; i++) {
    int temp = 0;
    for (int j = 0; j < n; j++) {
      temp += (i ^ a[j]);
    }
    ans = max(temp, ans);
  }
  cout << ans;
  return 0;
}

I found the solution on a website. I was unable to understand what the code does but, this solution gives incorrect answer for some test cases.

Scroll down to question 3

5 Answers

The trick here is that XOR works on bits in parallel, independently. You can optimize each bit of x. Brute-forcing this takes 2*32 tries, given the constraints.

As said in other comments each bit of x will give an independent contribution to the sum, so the first step is to calculate the added value for each possible bit.

To do this for the i-th bit of x count the number of 0s and 1s in the same position of each number in the array, if the difference N0 - N1 is positive then the added value is also positive and equal to (N0-N1) * 2^i, let's call such bits "useful".

The number x will be a combination of useful bits only.

Since k is not in the form 2^n - 1, we need a strategy to find the best combination (if you don't want to use brute force on the k possible values).

Consider then the binary representation of k and loop over its bits starting from the MSB, initializing two variables: CAV (current added value) = 0, BAV (best added value) = 0.

If the current bit is 0 loop over.

If the current bit is 1:

a) calculate the AV sum of all useful bits with lower index plus the CAV, if the result is greater then the BAV then replace BAV

b) if the current bit is not useful quit loop

c) add the current bit added value to CAV

When the loop is over, if CAV is greater than BAV replace BAV

EDIT: A sample implementation (in Java, sorry :) )

public class XorSum {
    public static void main(String[] args) {
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int k=sc.nextInt();
        int[] a=new int[n];
        for (int i=0;i<n;i++) {
            a[i]=sc.nextInt();
        }

        //Determine the number of bits to represent k (position of most significant 1 + 1)
        int msb=0;
        for (int kcopy=k; kcopy!=0; kcopy=kcopy>>>1) {
            msb++;
        }
        
        
        //Compute the added value of each possible bit in x
        int[] av=new int[msb];
        int bmask=1;
        for (int bit=0;bit<msb;bit++) {
            int count0=0;
            for (int i=0;i<n;i++) {
                if ((a[i]&bmask)==0) {
                    count0++;
                }
            }
            av[bit]=(count0*2-n)*bmask;
            bmask = bmask << 1;
        }
        
        //Accumulated added value, the value of all positive av bits up to the index
        int[] aav=new int[msb];
        for (int bit=0;bit<msb;bit++) {
            if (av[bit]>0) {
                aav[bit]=av[bit];
            }
            if (bit>0) {
                aav[bit]+=aav[bit-1];
            }
        }


        //Explore the space of possible combinations moving on the k boundary
        int cval=0;
        int bval=0;

        bmask = bmask >>> 1;
        //Start from the msb
        for (int bit=msb-1;bit>=0;bit--) {
            //Exploring the space of bit combination we have 3 possible cases:
            //bit of k is 0, then we must choose 0 as well, setting it to 1 will get x to be greater than k, so in this case just loop over
            if ((k&bmask)==0) {
                continue;
            }
            //bit of k is 1, we can choose between 0 and 1:
            //- choosing 0, we can immediately explore the complete branch considering that all following bits can be set to 1, so just set to 1 all bits with positive av
            //  and get the meximum possible value for this branch
            int val=cval+(bit>0?aav[bit]:0);
            if (val>bval) {
                bval=val;
            }
            //- choosing 1, if the bit has no positive av, then it's forced to 0 and the solution is found on the other branch, so we can stop here
            if (av[bit]<=0) break;
            //- choosing 1, with a positive av, then store the value and go on with this branch
            cval+=av[bit];
        }
        if (cval>bval) {
            bval=cval;
        }
        
        //Final sum
        for (int i=0;i<n;i++) {
            bval+=a[i];
        }
        
        System.out.println(bval);
        
    }
}

I think you can consider solving for each bit. The number X should be the one that can turn on many high-order bits in the array. So you can count the number of bits 1 for 2^0, 2^1, ... And for each position in the 32 bits consider turning on the ones that many number has that position to be bit 0.

Combining this with the limit K should give you an answer that runs in O(log K) time.

Assuming k is unbounded, this problem is trivial.

For each bit (assuming 64-bit words there would be 64 for example) accumulate the total count of 1's and 0's in all values in the array (for that bit), with c1_i and c0_i representing the former and latter respectively for bit i.

Then define each bit b_i in x as

x_i = 1 if c0_i > c1_i else 0

Constructing x as described above is guaranteed to give you the value of x that maximizes the sum of interest.


When k is specific number, this can be solved using a dynamic programming solution. To understand how, first derive a recurrence.

Let z_0,z_1,...,z_n be the positions of ones occuring in k's binary representation with z_0 being the most significant position.

Let M[t] represent the maximum sum possible given the problem's array and defining any x such that x < t.

Important note: the optimal value of M[t] for t a power of 2 is obtained by following the procedure described above for an unbounded k, but limiting the largest bit used.

To solve this problem, we want to find

M[k] = max(M[2^z_0],M[k - 2^z_0] + C_0)

where C_i is defined to be the contribution to the final sum by setting the position z_i to one.

This of course continues as a recursion, with the next step being:

M[k - 2^z_0] = max(M[2^z_1],M[k - 2^z_0 - 2^z_1] + C_1)

and so on and so forth. The dynamic programming solution arises by converting this recursion to the appropriate DP algorithm.

Note, that due to the definition of M[k], it is still necessary to check if the sum of x=k is greater than M[k], as it may still be so, but this requires one pass.

At bit level it is simple 0 XOR 0, 1 XOR 1 = 0 and last one 0 XOR 1 = 1, but when these bit belongs to a number XOR operations have addition and subtraction effect. For example if third bit of a number is set and num XOR with 4 (0100) which also have third bit set then result would be subtraction from number by 2^(3-1), for example num = 5 then 0101 XOR 0100 = 0001, 4 subtracted in 5 , Similarly if third bit of a number is not set and num XOR with 4 then result would be addition for example num = 2 then 0010 XOR 0100 = 0101, 4 will be added in 2. Now let’s see this problem,

This problem can’t be solved by applying XOR on each number individually, rather the approach to solve this problem is Perform XOR on particular bit of all numbers, in one go! . Let’s see how it can be done?

Fact 1: Let’s consider we have X and we want to perform XOR on all numbers with X and if we know second bit of X is set, now suppose somehow we also know that how many numbers in all numbers have second bit set then we know answer 1 XOR 1 = 0 and we don’t have to perform XOR on each number individually.

Fact 2: From fact 1, we know how many numbers have a particular bit set, let’s call it M and if X also have that particular bit set then M * 2^(pos -1) will be subtracted from sum of all numbers. If N is total element in array than N - M numbers don’t have that particular bit set and due to it (N – M) * 2^(pos-1) will be added in sum of all numbers.

From Fact 1 and Fact 2 we can calculate overall XOR effect on a particular bit on all Numbers by effect = (N – M)* 2^(pos -1) – (M * 2^(pos -1)) and can perform the same for all bits. Now it’s time to see above theory in action, if we have array = {1, 6, 3}, k = 7 then,

1 = 0001 (There are total 32 bits but I am showing only relevant bits other bits are zero) 
6 = 0110
3 = 0011

So our bit count list = [0, 1, 2, 2] as you can see 1 and 3 have first bit set, 6 and 3 have second bit set and only 6 have third bit set.

X = 0, …, 7 but X = 0 have effect = 0 on sum because if bit is not set then it doesn’t not affect other bit in XOR operation, so let’s star from X = 1 which is 0001,

[0, 1, 2, 2] = count list,
[0, 0, 0, 1] = X

As it is visible in count list two numbers have first bit set and X also have first bit set, it means 2 * 2^(1 – 1) will be subtract in sum and total numbers in array are three, so (3 – 2) * 2^(1-1) will be added in sum. Conclusion is XOR of first bit is, effect = (3 – 2) * 2^(1-1) - 2 * 2^(1 – 1) = 1 – 2 = -1. It is also overall effect by X = 1 because it only has first bit set and rest of bits are zero. At this point we compare effect produced by X = 1 with X = 0 and -1 < 0 which means X = 1 will reduce sum of all numbers by -1 but X = 0 will not deduce sum of all numbers. So until now X = 0 will produce max sum.

The way XOR is performed for X = 1 can be performed for all other values and I would like to jump directly to X = 4 which is 0100

[0, 1, 2, 2] = count list,
[0, 1, 0, 0] = X

As it is visible X have only third bit set and only one number in array have first bit set, it means 1 * 2^(3 – 1 ) will be subtracted and (3 – 1) * 2^(3-1) will be added and overall effect = (3 – 1) * 2^(3-1) - 1 * 2^(3 – 1 ) = 8 – 4 = 4. At this point we compare effect of X = 4 with known max effect which is effect = 0 so 4 > 0 and due to this X = 4 will produce max sum and we considered it. When you perform this for all X = 0,…,7, you will find X = 4 will produce max effect on sum, so the answer is X = 4.

So

(x XOR arr[0]) + ( x XOR arr[1]) +….. + (x XOR arr[n]) = effect + sum(arr[0] + sum[1]+ …. + arr[n])

Complexity is, O(32 n) to find for all 32 bits, how many number have a particular bit set, plus,
O(32 k) to find effect of all X in [0, k],
Complexity = O(32 n) + O(32 k) = O(c n) + O(c k), here c is constant, finally
Complexity = O(n)

#include <iostream>

#include <cmath>
#include <bitset>

#include <vector>
#include <numeric>

std::vector<std::uint32_t> bitCount(const std::vector<std::uint32_t>& numList){

    std::vector<std::uint32_t> countList(32, 0);

    for(std::uint32_t num : numList){

        std::bitset<32> bitList(num);

        for(unsigned i = 0; i< 32; ++i){

            if(bitList[i]){

                countList[i] += 1;
            }
        }
    }

    return countList;
}

std::pair<std::uint32_t, std::int64_t> prefXAndMaxEffect(std::uint32_t n, std::uint32_t k,
                                                 const std::vector<std::uint32_t>& bitCountList){

    std::uint32_t prefX = 0;
    std::int64_t xorMaxEffect = 0;

    std::vector<std::int64_t> xorBitEffect(32, 0);

    for(std::uint32_t x = 1; x<=k; ++x){

        std::bitset<32> xBitList(x);

        std::int64_t xorEffect = 0;

        for(unsigned i = 0; i< 32; ++i){

            if(xBitList[i]){

                if(0 != xorBitEffect[i]){

                    xorEffect += xorBitEffect[i];
                }
                else{

                    std::int64_t num = std::exp2(i);

                    xorBitEffect[i] = (n - bitCountList[i])* num - (bitCountList[i] * num);

                    xorEffect += xorBitEffect[i];
                }
            }
        }

        if(xorEffect > xorMaxEffect){

            prefX = x;
            xorMaxEffect = xorEffect;
        }
    }

    return {prefX, xorMaxEffect};
}

int main(int , char *[]){

    std::uint32_t k = 7;
    std::vector<std::uint32_t> numList{1, 6, 3};

    std::pair<std::uint32_t, std::int64_t> xAndEffect = prefXAndMaxEffect(numList.size(), k, bitCount(numList));

    std::int64_t sum = 0;
    sum = std::accumulate(numList.cbegin(), numList.cend(), sum) + xAndEffect.second;

    std::cout<< sum<< '\n';
}

Output :
14

Related