计算累计XOR小于k的子集数量

时间:2019-05-11 13:26:10

标签: algorithm set dynamic-programming counting

给定一个大小为N的数字S。对S的所有子集进行计数,这些子集对子集元素的累积XOR小于K。

我可以想到蛮力方法来生成S的所有子集,并对累积XOR元素小于k的子集进行计数。我正在寻找不生成S的所有子集的优化解决方案,我可以找到所有这样的子集

Example: 
S = {1,2}
K = 4
U = {{},{1},{2},{1,2}}
Answer is 4 As 
cumulative XOR values are 0 for {}, 1 for {1}, 2 for {2}, 3 for {1,2}.

2 个答案:

答案 0 :(得分:3)

根据问题的约束,有几种可行的解决方法:

  • 正如您所指出的那样,如果N足够小,则尝试O(2^N)中的所有可能子集都会产生预期的结果。
  • 如果S中的值被某个较小的值所限制,则可以使用Master Chief帖子中概述的动态编程解决方案。
  • 如果N都很高,并且S中的值都很大(十亿及以上),则可以采用更复杂的多项式时间方法。大纲如下:

让我们看一下S中数字的二进制表示形式,例如对于数字17、5、20、14,它将是:

10001 17
00101  5
10100 20
01110 14

与K相同,例如,让K = 11: 01011 11

如果我们要计算多少个XOR精确地等于 K,我们可以将问题表示为模2的线性方程组,其变量与S中的数字一样多,并且许多方程式,因为我们的数字中包含有意义的位。更具体地,让第i个方程式表示约束“ S的子集中的数字的第i个比特的XOR应等于K中的第i个比特”。 (请注意,“异或”运算等效于求和模2)。例如,对于最小(最右边)位,我们具有以下内容:x1 * 1 + x2 * 1 + x3 * 0 + x4 * 0 = 1 (mod 2),其中x_j为0或1,具体取决于我们是否在子集中包含第j个数字。

请注意,此方程组可能具有0、1或许多解。对于许多解,每个自变量可以取0或1,因此解的数量为2 ^(独立变量)。

我们可以使用高斯消除来检测线性方程组的独立变量和可解性,该高斯消除在O(n^3)中针对大小为n的方阵运行-在您的情况下,矩阵为' t平方,因此我们可以使用(|S|, log(max(S))中的较大者来估算复杂度。

太好了,现在我们可以遍历所有K'从0到K-1,分别解决问题,并对结果求和。但是,这没有比动态编程解决方案更好的地方,并且在运行时只是伪多项式。让我们再做一个观察,得出多项式解:我们只对O(logK)个不同的方程组感兴趣,以计算多少个XOR子集小于K

让我们将K中最高的非零位表示为B。如果在我们采用的子集的XOR中,高于B的所有位以及位B都等于0,那么显然小于K。因此,我们的第一个方程组只能写为上述位,而忽略B以下的所有内容。

现在让我们看看如果我们允许第B位等于1,会发生什么情况。如果在数字K中,在第B位之后有一个或多个零位,它们都必须为0在产生的XOR中也是如此。如果在我们的XOR中将第一个后续的非零位B2设置为0,那么它将小于K。我们可以通过说“ B之上的所有位均为0,B为1,B和B2之间的所有位均为0,B2为0“,并计算解决方案的数量。

如果我们继续这样操作,直到K中最小的二进制位置,我们将最多需要建立logK方程组,并获得所需的结果。

这种方法的复杂性类似于O(logK * max(n, logK)^3),尽管根据实现的不同,高斯消去法对于非平方矩阵的工作要快得多。

答案 1 :(得分:0)

问题与count of subsets having sum equal to k非常相似。 我们可以以类似的方式进行,求和的总和等于0到k。

count

下面是我的python实现。

它使用动态编程将一些中间结果存储在DP表的每个单元格中。单元格dp [i] [j]包含等于j的子集数量,可以使用排序数组中的前ith个数字形成子集。

时间复杂度O(n * maxXor),其中maxXormaximum value which can be achieved by xoring any of the numbers in the arraymaxXor的最大值等于maxValue present in arrayK

的2的最小幂
from math import floor, log


arr = [1, 2]
K = 4


def getCoundDp(arr, k):
    arr.sort()
    maxVal = arr[-1]
    maxXor = 2**(floor(log(max(maxVal, k), 2)) + 1)
    dp = [[0 for i in range(maxXor)] for a in arr]
    dp[0][0] = 1
    # in the 1st row, mark the arr[0] to have count 1
    dp[0][arr[0]] = 1
    for row in range(1, len(arr)):
        for col in range(maxXor):
            dp[row][col] += dp[row-1][col]
            neededXor = col ^ arr[row]
            dp[row][col] += dp[row-1][neededXor]
    return sum(dp[-1][:k])


print(getCoundDp(arr, K))

您关于生成和检查所有子集的建议将非常缓慢O(2^n)。但是至少应该对验证更快的实现有价值。以下是使用itertools.combination的python中的蛮力方法示例,您可以详细了解here

from itertools import combinations


def getXor(arr):
    xor = 0
    for i in arr:
        xor ^= i
    return xor


def getCountBruteForce(arr, k):
    arr.sort()
    countLessThanK = 0
    for r in range(0, len(arr)+1):
        for comb in combinations(arr, r):
            xor = getXor(comb)
            if xor < k:
                countLessThanK += 1
    return(countLessThanK)