【问题标题】:Finding prime numbers without exceeding time limits在不超过时间限制的情况下查找素数
【发布时间】:2013-10-03 13:19:31
【问题描述】:

好的,第一件事。是的,这个问题来自编程竞赛。不,我不是想作弊,因为比赛已经结束 4 小时前。我很确定我的代码是正确的,但比赛的编译器说它给出了错误的答案。我尝试了其他编译​​器,它说“超出时间限制”。

那么,首先,你能告诉我代码是否正确吗? [一位编译器说不是]

如果是,那么我怎样才能提高时间效率? [另一个编译器说它超出了时间限制]


问题 如果一个数大于 1 并且 除了 1 和它自己之外没有除数。前几个素数 是 2, 3, 5, 7, 11, 13,.. 等等。给定一个整数 X,找到 不小于X的最小素数

输入:第一行包含测试用例的数量T.T用例 跟随。每个测试用例由一个单独的整数 X 组成。

输出:输出 T 行,每个案例一个包含最小的 不小于X的素数

约束:1

样本输入:4 8 47 90 1130

样本输出:11 47 97 1151

这是我的解决方案:

int main() 
{
    int n;
    long int x, i, a;
    bool isPrime; // This flag will test if number is prime or not?
    cin>>n; // Here "n" will represent the number of test cases
    while(n)
    {
        cin>>x; // "x" is the number to be tested for the nth case

        if(x<=2)
        {
            cout<<2<<endl; // All numbers smaller than 3 will have the smallest prime number as 2.
            continue;
        }
        for(i=x;i<=1000000;i++) // Should I have checked values of "i" for odd numbers only? I forgot to try that... Would it have helped in reducing time complexity?
        {
            isPrime=true;
            for(a=2; a<i; a++) // Okay I tried making it (i/2)+1 but then the compiler said that it was a wrong answer. I am skeptical though...
            {
                if(i%a==0 and i!=2)
                    isPrime=false;
            }
            if(isPrime==true)
            {
                cout<<i<<endl;
                break;
            }
        }

        n--;
    }
    return 0;
}

【问题讨论】:

  • 你试过在你的机器上运行这个吗?也许编译器报告错误实际上是您的程序运行时间过长(至少超过了挑战施加的时间限制)。
  • 是的,我在我的计算机上运行它并尝试了各种测试用例,例如:-5、0、1、2、3、50 所有这些数字都得到了正确的输出。提供的测试用例也很有效。
  • 您应该为已经遇到的素数使用缓存。它可能加快了这个过程。此外,您不需要检查直到 i 的每个除数,只需要检查 sqrt(i)
  • 你说“超过时间限制”,所以这意味着你的程序花费了很多时间,你需要提高性能。
  • "if(isPrime==true)" == "if(isPrime)"

标签: c++ primes


【解决方案1】:

为减少混淆,请创建一个检查数字是否为素数的函数:

bool IsPrime(int x)
{
    isPrime=true;
    for(int a = 2; a < x; a++)
    {
        if (x % a == 0 && a != 2)
            return false;
    }
    return true;
}

在这里,我没有更改您的代码,只是对其进行了重组。这很好,因为这个功能很小,任何改进都很容易。

去除边缘情况

没有必要检查a == 2,因为你从不为2调用这个函数。这使得内循环更小,提供更好的性能。

bool IsPrime(int x)
{
    isPrime=true;
    for(int a = 2; a < x; a++)
    {
        if (x % a == 0)
            return false;
    }
    return true;
}

检查更少的除数

这是一个众所周知的事实,而且很容易检查,检查除数到sqrt(x) 就足够了。这提供了更好的性能!

bool IsPrime(int x)
{
    isPrime=true;
    for(int a = 2; a * a <= x; a++)
    {
        if (x % a == 0)
            return false;
    }
    return true;
}

此时您的程序可能会被时间检查器接受。如果您还想要更好的性能,您可以进一步限制除数。

只检查主要除数

嗯,不是真正的素数,但最好限制检查至少为奇数。

bool IsPrime(int x)
{
    isPrime=true;
    static const int a_few_primes[] = {2, 3, 5, 7, 11, 13};
    for (int a: a_few_primes)
    {
        if (x % a == 0)
            return false;
    }
    for(int a = 17; a * a <= x; a += 2)
    {
        if (x % a == 0)
            return false;
    }
    return true;
}

其他回答者推荐的关于埃拉托色尼筛法的注释:它很好,但也许你并不真正需要它,因为测试用例的数量非常少 (10)。

编辑:删除了一些有缺陷的性能分析。

筛法需要至少 1000000 次迭代来构建素数列表。

试验方法每个数字需要少于 500 次迭代,尝试少于 114 的数字直到找到素数,并且它做了 10 次,因此迭代次数少于 500*114*10=570000。

【讨论】:

    【解决方案2】:

    我不会为你解决它,但给你一些提示。

    1. 使用Sieve of Eratosthenes,它允许您构建一个数组,之后您可以使用该数组来了解O(1) 中的数字是否为素数。
    2. 在读取任何数字之前构建一次 Sieve 数组,然后您可以读取数字并在恒定时间内检查每个数字。对每个数字执行相同的计算是多余的。

    【讨论】:

    • 因为是编程比赛,所以这行不通。因为你需要制作一张素数地图,这需要很多时间
    • @ST3 你是什么意思?
    • @athabaska OP 说这是竞赛风格的编程,所以你的时间和记忆力有限,这行不通,我参加过几次类似的比赛,我知道规则是这样的。跨度>
    • @mfontanini:先生,这也适用于大量数字吗?制作这么大的数组?喜欢它可能会超出分配的内存?
    • @user2732146 如果你想存储整个数组(1,000,000 个元素),那么它的重量为 3.8MB。这一点也不多。如果这太多了,那么您可以尝试优化算法。使用筛子是要走的路。
    【解决方案3】:

    在不超时的情况下解决这个问题需要两件事:

    • 预计算素数,以及
    • 使用二分查找

    您需要少于 78,500 的素数进行预计算,因此您不必太花哨。你必须做的唯一一件事就是不要浪费时间检查你的候选除数和非素数:使用你迄今为止找到的素数来发现新的素数。这个page has pseudocode for this approach

    由于您发现素数的方式,素数表将按升序排列。对于每个测试用例,使用binary search 搜索素数表。尽管线性搜索可能也可以工作,但当排序免费时,浪费大量 CPU 周期是没有意义的。此外,C++ 标准库has a convenient function for finding items in sorted containers,因此您的搜索可以在一行中编码。

    【讨论】:

      【解决方案4】:

      您在大数上失败了 - 例如,不小于 1,000,000 的最小素数是 1,000,003。
      测试边缘情况很重要。

      并使用诸如 Eratosthenes 筛子之类的东西预先计算素数以加快速度。

      【讨论】:

        猜你喜欢
        • 2021-07-18
        • 1970-01-01
        • 2014-10-30
        • 1970-01-01
        • 2015-11-07
        • 1970-01-01
        相关资源
        最近更新 更多