Xor of all pairwise sums of integers in an array

妖精的绣舞 提交于 2020-12-03 07:37:09

问题


We have an array A for example [1, 2, 3]. I want to find the XOR of the SUM of all pairs of integers in the array.
Though this can easily be done in O(n^2) (where n is the size of the array) by passing over all of the pairs, I want to improve the time complexity of the solution? Any answer that improves the time complexity would be great.
E.g. for the above example array, A, the answer would be (1+2)^(1+3)^(2+3) = 2. Since the pairwise elements are (1,2), (1,3), (2,3), and 3 ^ 4 ^ 5 = 2.


回答1:


Here's an idea for a solution in O(nw) time, where w is the size of a machine word (generally 64 or some other constant). The most important thing is counting how many of the pairs will have a particular bit set, and the parity of this number determines whether that bit will be set in the result. The goal is to count that in O(n) time instead of O(n2).

Finding the right-most bit of the result is easiest. Count how many of the input numbers have a 0 in the right-most place (i.e. how many are even), and how many have a 1 there (i.e. how many are odd). The number of pairs whose sum has a 1 in the rightmost place equals the product of those two counts, since a pair must have one odd and one even number for its sum to be odd. The result has a 1 in the rightmost position if and only if this product is odd.

Finding the second-right-most bit of the result is a bit harder. We can do the same trick of counting how many elements do and don't have a 1 there, then taking the product of those counts; but we also need to count how many 1 bits are carried into the second place from sums where both numbers had a 1 in the first place. Fortunately, we can compute this using the count from the previous stage; it is the number of pairs given by the formula k*(k-1)/2 where k is the count of those with a 1 bit in the previous place. This can be added to the product in this stage to determine how many 1 bits there are in the second place.

Each stage takes O(n) time to count the elements with a 0 or 1 bit in the appropriate place. By repeating this process w times, we can compute all w bits of the result in O(nw) time. I will leave the actual implementation of this to you.




回答2:


Here's my understanding of at least one author's intention for an O(n * log n * w) solution, where w is the number of bits in the largest sum.

The idea is to examine the contribution of each bit one a time. Since we are only interested in whether the kth bit in the sums is set in any one iteration, we can remove all parts of the numbers that include higher bits, taking them each modulo 2^(k + 1).

Now the sums that would necessarily have the kth bit set lie in the intervals, [2^k, 2^(k + 1)) and [2^(k+1) + 2^k, 2^(k+2) − 2]. So we sort the input list (modulo 2^(k + 1)), and for each left summand, we decrement a pointer to the end of each of the two intervals, and binary search the relevant start index.

Here's JavaScript code with a random comparison to brute force to show that it works (easily translatable to C or Python):

// https://stackoverflow.com/q/64082509
// Returns the lowest index of a value
// greater than or equal to the target
function lowerIdx(a, val, left, right){
  if (left >= right)
    return left;
  mid = left + ((right - left) >> 1);
  if (a[mid] < val)
    return lowerIdx(a, val, mid+1, right);
  else
    return lowerIdx(a, val, left, mid);
}

function bruteForce(A){
  let answer = 0;
  for (let i=1; i<A.length; i++)
    for (let j=0; j<i; j++)
      answer ^= A[i] + A[j];
  return answer;
}
  
function f(A, W){
  const n = A.length;
  const _A = new Array(n);
  let result = 0;

  for (let k=0; k<W; k++){
    for (let i=0; i<n; i++)
      _A[i] = A[i] % (1 << (k + 1));
    _A.sort((a, b) => a - b);

    let pairs_with_kth_bit = 0;
    let l1 = 1 << k;
    let r1 = 1 << (k + 1);
    let l2 = (1 << (k + 1)) + (1 << k);
    let r2 = (1 << (k + 2)) - 2;
    let ptr1 = n - 1;
    let ptr2 = n - 1;

    for (let i=0; i<n-1; i++){
      // Interval [2^k, 2^(k+1))
      while (ptr1 > i+1 && _A[i] + _A[ptr1] >= r1)
        ptr1 -= 1;
      const idx1 = lowerIdx(_A, l1-_A[i], i+1, ptr1);
      let sum = _A[i] + _A[idx1];
      if (sum >= l1 && sum < r1)
        pairs_with_kth_bit += ptr1 - idx1 + 1;

      // Interval [2^(k+1)+2^k, 2^(k+2)−2]
      while (ptr2 > i+1 && _A[i] + _A[ptr2] > r2)
        ptr2 -= 1;
      const idx2 = lowerIdx(_A, l2-_A[i], i+1, ptr2);
      sum = _A[i] + _A[idx2]
      if (sum >= l2 && sum <= r2)
        pairs_with_kth_bit += ptr2 - idx2 + 1;
    }

    if (pairs_with_kth_bit & 1)
      result |= 1 << k;
  }
  return result;
} 
 
var As = [
  [1, 2, 3], // 2
  [1, 2, 10, 11, 18, 20], // 50
  [10, 26, 38, 44, 51, 70, 59, 20] // 182
];

for (let A of As){
  console.log(JSON.stringify(A));
  console.log(`DP, brute force: ${ f(A, 10) }, ${ bruteForce(A) }`);
  console.log('');
}

var numTests = 500;

for (let i=0; i<numTests; i++){
  const W = 8;
  const A = [];
  const n = 12;
  for (let j=0; j<n; j++){
    const num = Math.floor(Math.random() * (1 << (W - 1)));
    A.push(num);
  }

  const fA = f(A, W);
  const brute = bruteForce(A);
  
  if (fA != brute){
    console.log('Mismatch:');
    console.log(A);
    console.log(fA, brute);
    console.log('');
  }
}

console.log("Done testing.");


来源:https://stackoverflow.com/questions/60576922/xor-of-all-pairwise-sums-of-integers-in-an-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!