如何有效地计算给定数字的所有不同组合的按位异或值之和

How to calculate the sum of the bitwise xor values of all the distinct combination of the given numbers efficiently?

本文关键字:组合 位异或 计算 有效地 数字      更新时间:2023-10-16

给定n(n<=1000000)个正整数(每个数小于1000000)。任务是计算给定数字的所有不同组合的按位xor (c/c++中的^)值的和。

时间限制为1秒。例如,给定3个整数为7,3,5,则答案应为7^3 + 7^5 + 3^5 = 12。

我的方法是:

#include <bits/stdc++.h>
using namespace std;
int num[1000001];
int main()
{
    int n, i, sum, j;
    scanf("%d", &n);
    sum=0;
    for(i=0;i<n;i++)
        scanf("%d", &num[i]);
    for(i=0;i<n-1;i++)
    {
        for(j=i+1;j<n;j++)
        {
            sum+=(num[i]^num[j]);
        }
    }
    printf("%dn", sum);
    return 0;
}

但是我的代码未能在1秒内运行。我如何以更快的方式编写代码,可以在1秒内运行?

编辑:实际上这是一个在线判断问题,我得到Cpu限制超出了我的上面的代码。

您需要计算大约1e12 xors,以便蛮力执行此操作。现代处理器每秒可以进行大约1e10次这样的操作。所以蛮力不起作用;因此,他们希望你能想出一个更好的算法。

所以你需要找到一种方法来确定答案,而不需要计算所有这些xor。

提示:如果所有输入的数字都是0或1(1位),你能想到一种方法吗?然后把它扩展到2位,3位,等等?

优化你的代码时,你可以走三种不同的路线:

  1. 优化算法。
  2. 优化调用语言和库函数。
  3. 针对特定架构进行优化。

很可能有一种更快的数学方法来观察每一对组合,然后把它们加起来,但我不知道。在任何情况下,在现代处理器上,你最多只能削掉几微秒;这是因为你在做基本的运算(异或和)。

对架构进行优化也没有什么意义。它通常在重复分支中变得很重要,在这里你没有这样的。

你的算法中最大的问题是从标准输入中读取。尽管"scanf"在计算机代码中只占用5个字符,但在机器语言中,这是程序的大部分。不幸的是,如果每次运行代码时数据实际上都在变化,那么就没有办法绕过从stdin读取的要求,并且无论您使用scanf还是std::cin >>,甚至尝试实现自己的方法从输入中读取字符并将其转换为int都没有区别。

所有这些都是假设你不期望一个人在不到一秒钟的时间内输入数千个数字。我猜你可以运行你的代码通过:myprogram < data .

此函数以二次增长(感谢@rici)。对于大约25,000个正整数,每个正整数为999,999(最坏情况),for循环计算可以在大约一秒内完成。试图使这个工作与输入,你已经指定和100万个正整数似乎是不可能的。

根据Alan Stokes的回答,你可能会得到线性复杂度而不是二次复杂度:

std::size_t xor_sum(const std::vector<std::uint32_t>& v)
{
    std::size_t res = 0;
    for (std::size_t b = 0; b != 32; ++b) {
        const std::size_t count_0 =
            std::count_if(v.begin(), v.end(),
                          [b](std::uint32_t n) { return (n >> b) & 0x01; });
        const std::size_t count_1 = v.size() - count_0;
        res += count_0 * count_1 << b;
    }
    return res;
}

现场演示。

解释:

  • x^y = Sum_b((x&b)^(y&b)),其中b为单位掩码(从1<<01<<32)。
  • 对于给定的位,count_0count_1分别为01位的计数数,我们有count_0 * (count_0 - 1) 0^0, count_0 * count_1 0^1count_1 * (count_1 - 1) 1^1 (0^01^10)。