求出3到50w范围内所有的素数。
这类问题在C语言题目中经常会遇见。同样,大素数的研究对于密码学也起到了重要的作用。那么对于C语言的初学者,该如何编写程序计算素数呢?
1.
首先从素数的定义来看,“一个大于1的自然数,如果除了1和它自身外,不能被其他自然数整除的数”。除了一和他自身外,没有其他因数,那么最粗暴的算法就出来了:
遍历所有所有大于一,小于他自身的整数,如果没有数能整除他,他就是素数。
#include
#include
int
main(){
int i,j,a,b;
a=clock();
for(i=3;i<500000;i++){
for(j=2;j
if(i%j==0){
goto table;
}
}
table:;
}
b=clock();
printf("%d\n",b-a);
return 0;
}
上述代码就是实现了这个算法,当出现因数,直接跳到下一个数字。
经过测试,执行该代码的时间是33449ms。
明显对于一个求素数的算法来说,时间太长了。如果范围扩大到5000000,甚至50000000,耗费时间更是无法想象。那么如何去改进呢?
2.
让我们再从定义去看。“因数”,我们要寻找的是除了自身与1没有因数的数字。而除了自身的平方根,因数都是成对存在的。而且这些因数对,一定是一大一小(除平方根)。那么我们只要保留上面的遍历,但将遍历的最大值设为所有因数对较小值中的最大值就可以了。只要这个值不存在,就说明该数字不存在因数对。那么自然也就是素数了。
#include
#include
#include
int main(){
int i,j,a,b;
a=clock();
for(i=3;i<500000;i++){
for(j=2;j<=sqrt(i);j++){
if(i%j==0){
goto table;
}
}
table:;
}
b=clock();
printf("%d\n",b-a);
return 0;
}
因数对中的较小值的最大值,自然就是其自身的平方根了。在这之后的因数都能对应一个在这之前的因数。所以如果在这之前没有因数,那么在这之后也同样没有因数。
这可以说是个相当大的改进了,大大减少了遍历次数,程序执行时间为234ms。
3.
除了2以外,所有的素数都是奇数。那么我们就可以先将偶数排除,再来判定质数
#include
#include
#include
int main(){
int i,j,a,b;
a=clock();
for(i=3;i<500000;i++){
if(i%2==0) goto table;
for(j=3;j<=sqrt(i);j+=2){
if(i%j==0){
goto table;
}
}
table:;
}
b=clock();
printf("%d\n",b-a);
return 0;
}
因为先一步排除了偶数, 所以寻找因数便不必再遍历偶数因数。因为奇数的因数一定是奇数。
进一步的优化,现在程序执行时间仅有109ms。
4.
我们做数学题时,都知道平方根并不好算。同样对于计算机来说,平方根计算也会消耗大量时间。所以我们可以先一步计算平方根,以使其不用在每次比较时都去计算。
#include
#include
#include
int main(){
int i,j,a,b;
double sq;
a=clock();
for(i=3;i<500000;i++){
if(i%2==0) goto table;
sq=sqrt(i);
for(j=3;j<=sq;j+=2){
if(i%j==0){
goto table;
}
}
table:;
}
b=clock();
printf("%d\n",b-a);
return 0;
}
这样程序的执行时间又缩短了一倍,现在只有46ms了。
5.
不要以为数论就是一堆没用的理论知识。算术基本定理,现在是用上它的时候了。
任何一个大于1的自然数
N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积。
那么我们只需要遍历素数因数就好了嘛。
至于能成为因数的0素数有哪些呢?
不要忘了我们是从小到大寻找的素数,现在可以用到前面的结果了。#include
#include
#include
int ar[300000];
int main(){
int i,j,k=0,a,b;
double sq;
a=clock();
for(i=3;i<500000;i++){
if(~i&1) goto table;
sq=sqrt(i);
for(j=0;ar[j]<=sq&&j
if(i%ar[j]==0){
goto table;
}
}
ar[k]=i;
k++;
table:;
}
b=clock();
printf("%d\n",b-a);
return 0;
}
把前面每一个素数放入一个数组,以此来寻找素数因数。
现在整个程序的执行时间只需要15ms了!
从最开始的3万3千毫秒,到现在的15毫秒,整整缩短了2200多倍。可见算法对于一个程序是多么重要。
最后再贴上一段程序,思考一下这一段与上一段有什么区别,以及为什么要这么做
#include
#include
#include
int
ar[300000];
int
main(){
int i,j,k=0,a,b;
double sq;
ar[0]=2;
a=clock();
for(i=3;i<5000000;i++){
if(~i&1) goto table;
sq=sqrt(i);
for(j=0;ar[j]<=sq;j++){
if(i%ar[j]==0){
goto table;
}
}
ar[k]=i;
k++;
table:;
}
b=clock();
printf("%d\n",b-a);
return 0;
}