一、素数
素数又称质数。所谓素数是指除了1和它本身以外,不能被任何整数整除的数,例如17就是素数,因为它不能被2~16的任一整数整除。比1大但不是素数的数称为合数,1和0既非素数也非合数。
二、素数判断算法
思路1):因此判断一个整数m是否是素数,只需把m被 2 ~ m-1 之间的每一个整数去除,如果都不能被整除,那么m就是一个素数。
思路2):另外判断方法还可以简化。m不必被 2 ~ m-1 之间的每一个整数去除,只需被 2 ~ 之间的每一个整数去除就可以了。如果m不能被 2 ~ 间任一整数整除,m必定是素数。例如判别17是是否为素数,只需使17被2~4之间的每一个整数去除,由于都不能整除,可以判定17是素数。原因:因为如果m能被 2 ~ m-1 之间任一整数整除,其二个因子必定有一个小于或等于,另一个大于或等于。例如16能被2,4,8整除,16=2*8,2小于4,8大于4,16=4*4,4=√16,因此只需判定在2~4之间有无因子即可。
思路3): 素数筛法
素数筛法是这样的:
1.开一个大的bool型数组prime[],大小就是n+1就可以了.先把所有的下标为奇数的标为true,下标为偶数的标为false.
2.然后:
for( i=3; i<=sqrt(n); i+=2 )
{ if(prime[i])
for( j=i+i; j<=n; j+=i ) prime[j]=false;
}
3.最后输出bool数组中的值为true的单元的下标,就是所求的n以内的素数了。
原理很简单,就是当i是质(素)数的时候,i的所有的倍数必然是合数。如果i已经被判断不是质数了,那么再找到i后面的质数来把这个质
数的倍数筛掉。
一个简单的筛素数的过程:n=30。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
第 1 步过后2 4 ... 28 30这15个单元被标成false,其余为true。
第 2 步开始:
i=3; 由于prime[3]=true, 把prime[6], [9], [12], [15], [18], [21], [24], [27], [30]标为false.
i=4; 由于prime[4]=false,不在继续筛法步骤。
i=5; 由于prime[5]=true, 把prime[10],[15],[20],[25],[30]标为false.
i=6>sqrt(30)算法结束。
第 3 步把prime[]值为true的下标输出来:
for(i=2; i<=30; i++)
if(prime[i]) printf("%d ",i);
结果是 2 3 5 7 11 13 17 19 23 29
三、素数判断代码思路1)的代码:
#include <stdio.h> int main(){ int num=0; // 输入的整数 int i=0;
int flag=0;
printf("输入一个整数:"); scanf("%d",&num); for(i=2;i<=num;i++){ if(num%i==0){
flag=1; break; } } if((0 != num)&&(1 != num)&&(0 == flag)){ printf("%d是素数。\n", num); }else{ printf("%d不是素数。\n", num); }
return 0; }
思路2)的代码:
#include <stdio.h> #include <math.h> void main(){ int m; // 输入的整数 int i; // 循环次数 int k; // m 的平方根 printf("输入一个整数:"); scanf("%d",&m); // 求平方根,注意sqrt()的参数为 double 类型,这里要强制转换m的类型 k=(int)sqrt( (double)m ); for(i=2;i<=k;i++) if(m%i==0) break; // 如果完成所有循环,那么m为素数 // 注意最后一次循环,会执行i++,此时 i=k+1,所以有i>k if(i>k) printf("%d是素数。\n",m); else printf("%d不是素数。\n",m); return 0; }
思路3)的代码:
用了筛法的方法:
#include<stdio.h>
#include<math.h>
#define N 10000001
bool prime[N];
int main()
{
int i, j;
for(i=2; i<N; i++)
if(i%2) prime[i]=true;
else prime[i]=false;
for(i=3; i<=sqrt(N); i++)
{ if(prime[i])
for(j=i+i; j<N; j+=i) prime[i]=false;
}
for(i=2; i<100; i++)//由于输出将占用太多io时间,所以只输出2-100内的素数。可以把100改为N
if( prime[i] )printf("%d ",i);
return 0;
}
跑程序试一试。这个差别很大,前面那个程序绝对是n分钟黑屏。
另外,对于这样的筛法,还可以进一步优化,就是bool型数组里面只存奇数不存偶数。如定义prime[N],则0表示
3,1表示5,2表示7,3表示9...。如果prime[0]为true,则表示3时素数。prime[3]为false意味着9是合数。
这样的优化不是简单的减少了一半的循环时间,比如按照原始的筛法,数组的下标就对应数。则在计算30以内素
数的时候3个步骤加起来走了15个单位时间。但是用这样的优化则是这样:
则由于只存3 5 7 9 11 13 15 17 19 21 23 25 27 29,只需要14个单元
第 1 步 把14个单元赋为true (每个单元代表的数是2*i+3,如第0单元代表3,第1单元代表5...)
第 2 步开始:
i=0; 由于prime[0]=true, 把 [3], [6], [9], [12]标为false.
i=1; 由于prime[1]=true, 把 [6], [11]标为false
i=2 2*i+3>sqrt(30)算法结束。
这样优化以后总共只走6个单位时间。
当n相当大以后这样的优化效果就更加明显,效率绝对不仅仅是翻倍。
出了这样的优化以外,另外在每一次用当前已得出的素数筛选后面的数的时候可以一步跳到已经被判定不是素数的
数后面,这样就减少了大量的重复计算。(比如我们看到的,i=0与i=1时都标了[6],这个就是重复的计算。)
我们可以发现一个规律,那就是3(即i=0)是从下标为[3]的开始筛的,5(即i=1)是从下标为[11]开始筛的(因为[6]
已经被3筛过了)。然后如果n很大的话,继续筛。7(i=2)本来应该从下标为[9]开始筛,但是由于[9]被筛过了,而
[16]也已经被5(i=1)筛过了。于是7(i=2)从[23](就是2*23+3=49)开始筛。
于是外围循环为i时,内存循环的筛法是从 i+(2*i+3)*(i+1)即i*(2*i+6)+3开始筛的。
这个优化也对算法复杂度的降低起到了很大的作用。
相比于一般的筛法,加入这两个优化后的筛法要高效很多。