📌  相关文章
📜  给定数组中所有对的按位或的总和(1)

📅  最后修改于: 2023-12-03 14:56:54.288000             🧑  作者: Mango

给定数组中所有对的按位或的总和

这是一道算法题,需要计算一个整数数组中所有元素的按位或的结果总和。具体来说,对于数组中的任意两个元素,计算它们的按位或运算结果,然后将所有结果加起来,得到最终答案。

解法分析
暴力枚举

最直观的解法是使用两层循环枚举数组中的所有元素对,并计算它们的按位或运算结果。这种解法的时间复杂度是 $O(n^2)$。

def bitwise_or_sum(nums: List[int]) -> int:
    n = len(nums)
    res = 0
    for i in range(n):
        for j in range(i + 1, n):
            res += nums[i] | nums[j]
    return res
位运算优化

观察按位或的定义,发现它的结果比较难直接计算。但是,我们可以将按位或转换为按位与和按位异或的组合,具体如下:

$$a \texttt{ | } b = (a \texttt{ ^ } b) \texttt{ | } (a \texttt{ & } b)$$

根据这个等式,我们可以使用一层循环遍历数组中的所有元素,依次计算它与所有前面的元素的按位异或和按位与,将结果相加,得到最终答案。

def bitwise_or_sum(nums: List[int]) -> int:
    n = len(nums)
    res = 0
    cur_or = 0  # 当前元素与之前所有元素的按位或结果
    for i in range(n):
        cur_or |= nums[i]
        cur_xor = cur_or  # 当前元素与之前所有元素的按位异或结果
        for j in range(i):
            cur_xor ^= nums[j]
            res += cur_xor
    return res

这种解法的时间复杂度是 $O(n^2)$,空间复杂度是 $O(1)$。

位运算进一步优化

如果我们仔细观察按位或运算的定义,发现它具有一个重要的性质:如果 $a$ 或 $b$ 的某个二进制位为 $1$,则它们的按位或结果的该二进制位也为 $1$,否则为 $0$。也就是说,在计算按位或结果时,如果我们将每个元素的所有二进制位分开考虑,就可以在不使用按位或运算的情况下计算每一位的结果。

具体来说,我们可以从低位到高位依次计算所有元素的二进制位,统计出每个二进制位上的 $1$ 的个数。假设当前计算到第 $k$ 位,对于所有元素的第 $k$ 位,如果有 $m$ 个元素的该位为 $1$,则该位对结果的贡献是 $2^{k-1} \times m \times (n-m)$,其中 $n$ 是元素的总数。可以发现,这个式子中 $m$ 和 $n-m$ 是可以提前预处理出来的,因此我们只需要在一次遍历中统计所有二进制位的 $1$ 的个数即可。

def bitwise_or_sum(nums: List[int]) -> int:
    n = len(nums)
    res = 0
    for k in range(32):  # 计算每一位的结果
        cnt = 0
        for i in range(n):
            if nums[i] & (1 << k):
                cnt += 1
        res += (1 << k) * cnt * (n - cnt)
    return res

这种解法的时间复杂度是 $O(32n) = O(n)$,空间复杂度是 $O(1)$。

总结

本题的解法涉及到按位或、按位与、按位异或等位运算的使用,同时也需要一些位运算的技巧。相比于暴力枚举,二进制拆分的方法能够大幅优化时间复杂度。