在不超出时间限制的情况下查找素数

时间:2013-10-03 13:19:31

标签: c++ primes

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

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

如果是,那么我怎样才能让它更节省时间? [另一位编制者说它超出了时间限制]


  

问题如果一个数字大于1,则称为素数   除了1和它本身之外没有除数。前几个素数   是2,3,5,7,11,13 ......等等。给定一个整数X,找到   最小素数,不小于X

     

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

     

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

     

约束:1 <= T <= 10 1 <= X <= 1,000,000

     

样本输入: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;
}

4 个答案:

答案 0 :(得分:3)

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

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;
}

关于Eratosthenes筛选的一个注释,其他一些回答者推荐:它很好,但考虑到测试用例的数量很小(10),也许你真的不需要它。

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

筛选方法需要至少1000000次迭代才能构建素数列表。

试用方法每个数字需要少于500次迭代,尝试的数字少于114,直到找到素数,然后执行10次,因此迭代次数小于500 * 114 * 10 = 570000。

答案 1 :(得分:2)

我不打算为你解决,但给你一些提示。

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

答案 2 :(得分:2)

解决这个问题而不耗尽时间需要两件事:

  • 预先计算素数,
  • 使用二进制搜索

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

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

答案 3 :(得分:2)

你的大数字失败 - 例如,不小于1,000,000的最小素数是1,000,003。
测试边缘情况非常重要。

用Eratosthenes的筛子预先计算质数以加速它。