使用序列元素查找xor的数字以获得给定的总和

时间:2017-04-09 11:38:32

标签: algorithm sum xor

我最近遇到了以下问题:我们给出x_i (x_i < 2^60)个整数序列n (n < 10^5),整数S (S < 2^60)找到最小整数a如下所示:

formula

例如:

x = [1, 2, 5, 10, 50, 100]
S = 242

a的可能解决方案是21,23,37,39,但最小的是21。

(1^21) + (2^21) + (5^21) + (10^21) + (50^21) + (100^21)
= 20 + 23 + 16 + 31 + 39 + 113 
= 242

1 个答案:

答案 0 :(得分:4)

可以从底部逐位构建结果。从最低位开始,尝试将0和1作为a的最低位,并查看sum-xor的最低位是否与S的相应位匹配。然后尝试下一个最低位,传播任何进位来自上一步。

遵循此算法,a的每个位可能有0,1或2个选项,因此在最坏的情况下,我们可能需要探索不同的分支并选择给出最小结果的分支。为了避免指数行为,我们将先前看到的结果缓存到某个位的进位。这产生了O(kn)的最坏情况复杂度,其中k是结果中的最大位数,并且n是给定输入列表长度为n的进位的最大值。

以下是一些实现此功能的Python代码:

max_shift = 80

def xor_sum0(xs, S, shift, carry, cache, sums):
    if shift >= max_shift:
        return 1e100 if carry else 0
    key = shift, carry
    if key in cache:
        return cache[key]
    best = 1e100
    for i in xrange(2):
        ss = sums[i][shift] + carry
        if ss & 1 == (S >> shift) & 1:
            best = min(best, i + 2 * xor_sum0(xs, S, shift + 1, ss >> 1, cache, sums))
    cache[key] = best
    return cache[key]

def xor_sum(xs, S):
    sums = [
        [sum(((x >> sh) ^ i) & 1 for x in xs) for sh in xrange(max_shift)]
        for i in xrange(2)]
    return xor_sum0(xs, S, 0, 0, dict(), sums)

如果没有解决方案,代码将返回一个大的(> = 1e100)浮点数。

这是一个测试,它会在您给出的范围中选择随机值,选择一个随机a并计算S,然后求解。请注意,有时代码会找到比用于计算S的代码更小的a,因为a的值并不总是唯一的。

import random
xs = [random.randrange(0, 1 << 61) for _ in xrange(random.randrange(10 ** 5))]
a_original = random.randrange(1 << 61)
S = sum(x ^ a_original for x in xs)
print S
print xs

a = xor_sum(xs, S)
assert a < 1e100
print 'a:', a
print 'original a:', a_original

assert a <= a_original

print 'S', S
print 'SUM', sum(x^a for x in xs)

assert sum(x^a for x in xs) == S