- 伽利略:“ 素数是上帝用来描写宇宙的文字 。”
素数,又称为质数,是不能被1与其本身以外的其他整数整除的整数。如2、3、5、7、11、13、17是前几个素数,其中 2为唯一的偶素数;
与此相对应,一个整数如果能被除1与其本身以外的整数整除,该整数称为合数或复合数。例如,15能被除1与15以外的整数3、5整除,15是一个合数;
特别地,数 1既不是素数,也不是合数;
作为一类特殊的整数,素数是数论中探讨最多的也是难度最大的一类整数,其中有些问题是许多著名数学家提出并研究过的经典问题;
试商判别法
试应用试商判别法求出指定区间上的所有素数,并统计该区间上素数的个数;
1.说明:
试商判别法是依据素数的定义来实施的;
应用试商,法来探求奇数i(只有唯一的偶素数2,不作试商判别)是不是素数,用奇数j(取3,5,…,sqrt(i))去试商,若存在某个j能整除i,说明i能被1与本身以外的整数j整除,i不是素数,若上述范围内的所有奇数j都不能整除i,则i为素数;
有些程序把试商奇数j的取值上限定为 i/2 或 i-1 也是可行的,但并不是可取的,这样无疑会增加了试商的无效循环,理论上说,如果i存在一个大于sqrt(i)且小于i的因数,则必存在一个与之对应的小于sqrt(i)且大于1的因数,因而从判别功能来说,取到sqrt(i)已足够;
判别j整除i常用表达式 i%j=0 实现;
2.程序设计:
#include<stdio.h>
#include<math.h>
int main()
{
long c,d,i,j,n,t;
printf("请输入区间上下限c,d(c>2):");
scanf("%ld,%ld",&c,&d);
printf("求区间[%ld,%ld]上的素数:\n",c,d);
if(c%2==0)
c=c+1;
n=0;
for(i=c;i<=d;i+=2)
{
for(t=0,j=3;j<=sqrt(i);j+=2)
if(i%j==0) /*实施试商*/
{
t=1;
break;
}
if(t==0) /*标志量t=0时i为素数*/
{
printf("%ld ",i);
n++; /*统计素数的个数*/
if(n%5==0)
printf("\n");
}
}
printf("\n共%ld个素数。",n);
}
3.程序运行示例及其注意事项:
请输入区间上下限c,d(c>2):2000,2100
求区间[2000,2100]上的素数:
2003 2011 2017 2027 2029
2039 2053 2063 2069 2081
2083 2087 2089 2099
共14个素数。
厄拉多塞 筛法
求素数的筛法 是公元前三世纪的 厄拉多塞(Eratosthenes)提出来的:
- 对于一个大整数x,只要知道不超过 sqrt(x) 的所有素数p,划去所有p的倍数2p,3p,…,剩下的整数就是不超过x的全部素数;
应用筛法求素数,为了方便实施“划去”操作,应设置数组。每一数组元素对应一个待判别的奇数,并赋初值0,如果该奇数为p的倍数则应划去,对应元素加一个划去标记,通常给该元素赋值-1,最后,打印元素值不是-1(即没有划去)的元素对应的奇数即所求的素数;
在实际应用筛法的过程中,p通常不限于取不超过 sqrt(x) 的素数,而是适当放宽取不超过 sqrt(x) 的奇数(从3开始)。这样做尽管多了一些重复划去操作,但程序实现要简便些;
1.说明:
设置a数组存储区间中的所有奇数,凡“划去”的数组元素赋值“-1”;
在指定区间 [c,d] (约定c为奇数)上所有的奇数表示为 j=c+2*k(k=0,1,…,e,这里 e=(d-c)/2 )。于是k=(j-c)/2是奇数j在数组中的序号(下标)。如果j为奇数的倍数时,对应数组元素作“划去”标记,即 a[(j-c)/2]=-1 ;
根据c与奇数i确定g=2 * int(c/(2*i))+1,使得gi接近区间下限c,从而使划去的gi,(g+2)i,…,在 [c,d] 中,减少无效操作,以提高对广大区间的筛选效率;
最后,凡数组元素 a[k]!=-1,对应的奇数j=c+2*k就为素数;
2.程序设计:
#include<stdio.h>
#include<math.h>
int main()
{
long c,d,e,g,i,j,k,n;
static long int a[80000];
printf("请输入区间上下限c,d(c>2):");
scanf("%ld,%ld",&c,&d);
printf("求区间[%ld,%ld]上的素数:\n",c,d);
if(c%2==0)
c=c+1;
e=(d-c)/2;
i=1;
while(i<=sqrt(d)) /*在[c,d]中筛选素数*/
{
i=i+2;
g=2*int(c/(2*i))+1;
if(g*i>d)
continue;
if(g==1)
g=3;
j=j*g;
while(j<=d)
{
if(j>=c)
a[(j-c)/2]=-1;
j=j+2*i;
}
}
for(n=0,k=0;k<=e;k++)
if(a[k]!=-1) /*输出所得素数*/
{
n++;
printf("%ld ",c+2*k);
if(n%5==0)
printf("\n");
}
printf("\n共%ld个素数。\n",n);
}
3.程序运行示例及其注意事项:
请输入区间上下限c,d(c>2):1671800,1672000
求区间[1671800,1672000]上的素数:
1671801 1671807 1671813 1671825 1671835
1671853 1671855 1671867 1671883 1671891
1671903 1671909 1671925 1671933 1671939
1671945 1671967
共17个素数。
注意:
试商法较为直观,设计容易实现,因此常为程序设计爱好者所采用;
筛法在较大区间的搜索与较大整数的判别上效率更高一些,但设计上较难把握;