本节探讨连续合数问题,实际上还是与素数密切相关;
最多连续合数区间中不能存在素数,最小连续n个合数中也不能存在素数;
最多连续合数区间
试在指定区间 [c,d] 内探求最多连续合数的个数及最多连续合数的起始与终止数;
例如输入c,d:10,100,在区间[10,100]内最多连续合数的个数为7个,所求得的连续合数区间为:[90,96];
下面应用试商法与筛法两种设计分别求解;应用试商设计:
1.说明:
对指定区间[c,d]内的每一个奇数i应用试商法判别是否为素数,若i(t=0)为素数,则i-f(f为i前面的素数)与max比较以求取最多并用变量f1和i1记录;
随后,f=i即把素数i赋值给f,为下一个素数进行比较做准备;
最多连续合数的起始数f+1有可能与区间起始数c重合,因而当c为偶数或非素数时有必要作赋值:f=c-1;
注意:以上比较所得max不是f与i之间的合数个数,max比f与i之间的合数个数多了1;
2.程序设计:#include
#include
int main()
{
long c,d,i,j,f,t,f1,i1,max;
printf("请输入c,d(2
scanf("%ld,%ld",&c,&d);
f=c;
max=0;
if(c%2!=0)
{
for(t=0,j=3;j<=sqrt(c);j+=2) /*试商判别下限c是否为素数*/
if(c%j==0)
{
t=1;
break;
}
}
if(c%2==0||t==1) /*令c-1为素数并赋值给f*/
f=c-1;
if(c%2==0)
c++;
for(i=c;i<=d;i+=2)
{
t=0;
for(j=3;j<=sqrt(i);j+=2) /*试商判别素数*/
if(i%j==0)
{
t=1;
break;
}
if(d-i<2 && t==1)
{
t=0;
i=d+1; /*令d+1为素数并赋值给i*/
}
if(t==0) /*t为0表明i为素数*/
{
if(i-f>max)
{
max=i-f;
f1=f+1;
i1=i-1;
}
f=i; /*f为i的前一个素数*/
}
}
printf("最多连续合数的个数为:%ld\n",max-1);
printf("连续合数区间为:[%ld,%ld]\n",f1,i1);
}
3.程序运行示例:请输入c,d(2
最多连续合数的个数为:113
连续合数区间为:[492114,492226]应用筛法:
求出区间 [c,d] 内的所有相邻素数对f,m,求取m-f的最大值max;
注意到本题的搜索范围较大,采用效率较高的筛法求素数是适宜的;
1.说明:
应用筛法求素数,为了方便实施“划去”操作,设置数组;
考虑到有时待测区间[c,d]比较大,为不使数组下标超维,把[c,d]分割为若干个子区间[cs,ds],确保在子区间中操作不超维;
每一数组元素对应一个待判断的奇数,并赋初值0,如果该奇数为p的倍数则应划去,对应元素加一个划去标记,通常给该元素赋值-1,最后,打印元素值不是-1(即没有划去)的元素对应的奇数即所求素数;
在实际应用筛法的过程中,p通常不限于取不超过sqrt(x)的素数,而是适当放宽取不超过sqrt(x)的奇数(从3开始),这样做尽管多了一些重复划去操作,但程序实现要简便些;
在指定区间[cs,ds](约定cs为奇数)上所有奇数表示为j=cs+2k(k=0,1,……,e,这里e=(ds-cs)/2),于是k=(j-cs)/2是奇数j在数组中的序号(下标),如果j为奇数的倍数时,对应数组元素作划去标记,即a[(j-cs)/2]=-1;
根据cs与奇数i确定g=2*int(cs/(2 * i))+1,使得gi接近区间下限cs,从而使划去的gi,(g+2) * i,……在[cs,ds]中,减少无效操作,以提高对大区间的筛选效率;
最后,凡数组元素a[k]!=-1,对应的奇数j=cs+2k就为素数;
2.程序设计:#include
#include
int main()
{
long c,d,cs,ds,ct,dt,f,g,i,j,k,m,max,a[11000];
int e,u,x,y;
printf("请输入c,d(2
scanf("%ld,%ld",&c,&d);
if(d-c<=20000)
{
cs=c;
ds=d;
x=0;
}
else
{
x=(d-c)/20000;
cs=c;
ds=d-20000*x;
}
f=cs;
max=0;
for(y=1;y<=x+1;y++) /*把[c,d]分x+1个子区间筛选素数*/
{
if(cs%2==0)
cs++;
for(i=0;i<=10999;i++)
a[i]=0;
e=(ds-cs)/2;
i=1;
while(i<=sqrt(ds))
{
i=i+2;
g=2*(cs/(2*i))+1;
if(g*i>ds)
continue;
if(g==1)
g=3;
j=i*g;
while(j<=ds)
{
if(j>=cs) /*赋值筛去标记-1*/
a[(j-cs)/2]=-1;
j=j+2*i;
}
}
for(u=1,k=0;k<=e;k++)
{
if(a[k]!=-1)
{
m=cs+2*k; /*m即筛选所得素数*/
if(m-f>max) /*寻求两相邻素数间距的最大值*/
{
max=m-f;
ct=f+1;
dt=cs+2*k-1;
}
f=m;
}
}
cs=ds+1;
ds=ds+20000; /*cs与ds增长后继续探求*/
}
printf("最多连续合数的个数为:%ld\n",max-1);
printf("连续合数区间为:[%ld,%ld]\n",ct,dt);
}
3.程序运行示例及其注意事项:请输入c,d(2
最多连续合数的个数为:153
连续合数区间为:[4652354,4652506]
注意:如果测试的区间比较大,应用筛法所需时间比试商判别要短,有兴趣的读者不妨用实例进行比较;
最小连续n个合数
试求出最小的连续n个合数(其中n是键盘输入的任意正整数);
例如输入n=7,最小的连续7个合数为 [90,96];
1.说明:
求出区间 [c,d] 内的所有素数(区间起始数c可由小到大递增),检验其中每相邻两素数之差,若某相邻的两素数m和f之差大于n,即m-f>n,则区间[f+1,f+n]中的n个数为最小的连续n个合数;
应用试商法求指定区间[c,d](约定起始数 c=3,d=c+10000)上的所有素数,求出该区间内的一个素数m,设前一个素数为f,判别:若m-f>n,则输出结果[f+1,f+n]后结束;否则,作赋值f=m,为求下一个素数做准备;
如果在区间[c,d]中没有满足条件的解,则作赋值:c=d+2,d=c+10000,继续试商下去,直到找出所要求的解;
2.程序设计:#include
#include
int main()
{
long c,d,f,m,j;
int t,n;
printf("求最小的n个连续合数。\n");
printf("请输入n(n<200):");
scanf("%d",&n);
c=3;
d=c+10000;
f=3;
while(1)
{
for(m=c;m<=d;m+=2)
{
for(t=0,j=3;j<=sqrt(m);j+=2)
if(m%j==0) /*实施试商*/
{
t=1;
break;
}
if(t==0 && m-f>n) /*满足条件进行输出*/
{
printf("最小的%d个连续合数区间为:",n);
printf("[%ld,%ld] \n",f+1,f+n);
return;
}
if(t==0) /*每求出一个素数m后赋值给f*/
f=m;
}
if(m>d)
{
c=d+2; /*每一轮试商后改变c和d转下一轮*/
d=c+10000;
}
}
}
3.程序运行示例及其注意事项:求最小的n个连续合数。
请输入n(n<200):100
最小的100个连续合数区间为:[370262,370361]。
注意:随着n的增加,最小的n个连续合数也随之迅速变大,搜索也随之变得困难,例如输入n=200,搜索最小的200个连续合数区间[20831324,20831523]所有的时间就比较长;