筛选法求素数

ε祈祈猫儿з 提交于 2020-01-08 23:34:55

  求素数的程序是笔试或面试中会经常被问到的题目,大四找工作面试时,就被一个面试官问到了,虽然写出的代码能够完成题目要求,但是面试官并不满意,原因当然在程序的效率上,面试官反复问及如何对原有程序进行优化,想了半天除了将偶数剔除,再无其他想法,这也导致最后投递的岗位从研发变成offer中的测试。

  素数的定义为,一个大于1的自然数,除了1和它本身外,不能被其他自然数整除(除0以外)的数称之为素数(质数);否则称为合数。对计算机专业的毕业生来说,这道题并不陌生,比如求N以内的所有素数,通常的解法为:

 1 void PrimeNum(int n)
 2 {
 3     bool *bIsPrime = new bool[n+1];    //申请n+1来表示从1到n
 4     for(int i=0; i!=n+1; i++)
 5         if(i%2 == 0)
 6             bIsPrime[i] = false;
 7         else
 8             bIsPrime[i] = true;
 9 
10     bIsPrime[2] = true;    //2为素数
11 
12     int j, k, nSqrt;
13     for(j=3; j<=n; j+=2)
14     {
15         nSqrt = sqrt(j);
16         for(k=3; k<=nSqrt; k++)
17             if(j%k == 0)
18             {
19                 bIsPrime[j] = false;
20                 break;
21             }
22     }
23 
24     //cout<<n<<"以内的素数有:"<<endl;
25     //for(k=2; k<=n; k++)
26     //    if(bIsPrime[k])
27     //        cout<<k<<"  ";
28     //cout<<endl;
29 
30     delete[] bIsPrime;
31 }

  代码段line15-21,是这个求素数的核心思想:如果一个数是合数,那么它的最小质因数肯定小于等于他的平方根。例如:50,最小质因数是2,2<50的开根号。所以判断一个数是否是质数,只需判断它是否能被小于它开跟后的所有数整除。面试时给出的答案与上面思想一致,但是没有满足面试官对效率的要求。之后了解到筛选法求素数,核心思想是找到一个素数,那么这个素数与任何一个数的积是合数,这样就可以把这些合数筛掉。实现代码如下:

 1 void PrimeNumFilter(int n)
 2 {
 3     bool *bIsPrime = new bool[n+1];    //申请n+1来表示从1到n
 4     for(int i=0; i!=n+1; i++)
 5         if(i%2 == 0)
 6             bIsPrime[i] = false;
 7         else
 8             bIsPrime[i] = true;
 9 
10     bIsPrime[2] = true;    //2为素数
11 
12     int j, k;
13     int sqn = sqrt(n);  //只需递增至sqrt(n)即可,因为对于一个数n来说,最大因子就是sqrt(n),这里指内层循环中j*k的值
14     for(j=3; j<=sqn; j+=2)
15     {
16         if(bIsPrime[j])
17         {
18             for(k=2; j*k<=n; k++)
19                 bIsPrime[j*k]=false;
20         }
21     }
22 
23     //cout<<n<<"以内的素数有:"<<endl;
24     //for(k=2; k<=n; k++)
25     //    if(bIsPrime[k])
26     //        cout<<k<<"  ";
27     //cout<<endl;
28 
29     delete[] bIsPrime;
30 }

  代码段line15-19是实现筛选法的关键,如果一个数是素数,那么它与另一个数的乘积为合数。这个版本的是上一个版本的优化,关于效率的提高,来看看下面的测试代码,分别用两个版本的函数来计算10000000以内的所有素数,打印cpu耗时进行对比:

 1 int _tmain(int argc, _TCHAR* argv[])
 2 {
 3     clock_t start, end;
 4     start = clock();
 5     PrimeNum(10000000);
 6     end = clock();
 7 
 8     cout<<"cost cpu time: "<<end - start<<endl;
 9 
10     start = clock();
11     PrimeNumFilter(10000000);
12     end = clock();
13     cout<<"cost cpu time: "<<end - start<<endl;
14 
15     return 0;
16 }

   在VS2012中执行结果如下图:

  可以看出使用筛选法求素数,程序的效率与普通版本相比得到了很大的提升,而这种优化非常考验程序员的观察力。写好一个程序,仍然需要仔细的分析代码,并且结合源数据的特点(有时会用到数学知识),看看有没有更好的算法,能够提高效率,优化性能。

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!