定义m(m>1)位超级素数:
- m位超级素数本身为素数;
- 从高位开始,去掉1位后为m-1位素数、去掉2位后为m-2位素数、…… 、去掉m-1位后为1位素数;
例如137是一个3位数的超级素数,因为137是一个3位素数、去掉高1位后得37是一个2位素数、去掉高2位得7是一个1位素数,而素数107不是超级素数,因为去掉高1位得7不是一个2位素数;
输入整数m(1< m<=10),统计m位超级素数的个数,并输出其中最大的m位超级素数;
试应用枚举与递推两种算法设计求解;
枚举设计
1.说明:
(1)、为了方便判别素数,应用试商法设计素数判别函数p(k):若k为素数,p(k)返回1;否则,p(k)返回0;
(2)、为枚举m位数需要,通过自乘10(即c=c*10)计算m位数的起始数c;
(3)、设置枚举m位奇数的f循环:
①若f不是素数或f的个位数字不是3或7(超级素数的个位数字必然是3或7),则返回;
②若f的其他各位数字出现“0”,显然应予排除;
③除m位数f本身及其个位数已检验外,从高位开始去掉1位,2位,……,m-2位可得m-2个数(f%k,k=100,1000,……,10*m-1),这m-2个数的p函数值相乘为t:若t=0,说明m-2个数中至少有一个非素数,则返回;若t=1,说明m-2个数全为素数,应用变量作统计个数;
④为输出最大的m位超级素数,在统计的同时进行赋值:e=f,最后输出的e则为最大的m位超级素数;
2.程序设计:
#include<stdio.h>
#include<math.h>
int main()
{
int i,m;
long c,d,e,f,k,s,t;
int p(long f);
printf("请确定m(m>1):");
scanf("%d",&m);
for(c=1,i=1;i<=m-1;i++) /*确定最小的m位数c*/
c=c*10;
s=0;
for(f=c+1;f<=10*c-1;f=f+2) /*设置枚举循环,f为m位奇数*/
{
if(!(f%10==3||f%10==7)||p(f)==0)
continue;
for(t=1,d=f/10,i=1;i<=m-2;i++)
{
if(d%10==0) /*如果包含0数字,标记t=0*/
{
t=0;
break;
}
d=d/10;
}
if(t==0)
continue;
for(k=10,i=1;i<=m-2;i++) /*枚举m-2次去位操作*/
{
k=k*10;
if(p(f%k)==0) /*若其中任一数不是素数则退出循环*/
break;
}
if(i>m-2)
{
s++; /*统计*/
e=f; /*赋值*/
}
}
printf("共%ld个%d位超级素数。\n",s,m);
printf("其中最大数为%ld。\n",e);
}
int p(long k) /*设计素数检测函数*/
{
int j,h,z=0;
if(k==2)
z=1;
if(k>=3&&k%2==1)
{
for(h=0,j=3;j<=sqrt(k);j+=2)
if(k%j==0)
{
h=1;
break;
}
if(h==0) /*k为素数返回1,否则返回0*/
z=1;
}
return z;
}
3.程序运行示例及其注意事项:
请确定m(m>1):5
共192个5位超级素数。
其中最大数为99643。
注意:所得5位超级素数99643从高位“去位”后所得9643、643、43、3分别为4、3、2、1位素数,所以超级素数各位数上不可能存在0;
递推设计
1.说明:
根据超级素数的定义,m位超级素数去掉高位数字后是m-1位超级素数。一般地,k(k=2,3,……,m)位超级素数去掉高位数字后是k-1位超级素数;
那么,在已求得g个k-1位超级素数 a[i](i=1,2,……,g)时,在 a[i] 的高位加上一个数字j(j=1,2,……,9),得到9*g个k位候选数 f=j *e[k]+a[i](e[k]=10^(k-1)),只要对这个 9 *g个k位候选数检测即可,这就是从k-1递推到k的递推关系;
注意到超级m(m>1)位素数的个位数字必然是3或7,则得递推的初始(边界)条件:
- a[1]=3,a[2]=7,g=2;
2.程序设计:
#include<stdio.h>
#include<math.h>
int main()
{
int g,i,j,k,m,t,s;
double d,f,a[20000],b[20000],e[20];
int p(double f);
printf("请确定m(m>1):");
scanf("%d",&m);
g=2;
s=0;
a[1]=3; /*递推的初始条件*/
a[2]=7;
e[1]=1;
for(k=2;k<=m;k++)
{
e[k]=e[k-1]*10;
t=0;
for(j=1;j<=9;j++)
for(i=1;i<=g;i++)
{
f=j*e[k]+a[i]; /*产生9*g个候选数f*/
if(p(f)==1)
{
t++;
b[t]=f;
if(k==m) /*统计并记录最大的超级素数*/
{
s++;
d=f;
}
}
}
g=t;
for(i=1;i<=g;i++) /*g个k位b[i]赋值给a[i]*/
a[i]=b[i];
}
printf("共%d个%d位超级素数。\n",s,m);
printf("其中最大数为%.0f。\n",d);
}
int p(double k)
{
int h,z=0;
double j;
long t;
t=(int)pow(k,0.5);
for(h=0,j=3;j<=t;j+=2)
if(fmod(k,j)==0)
{
h=1;
break;
}
if(h==0) /*k为素数返回1,否则返回0*/
z=1;
return z;
}
3.程序运行示例及其注意事项:
请确定m(m>1):9
共545个9位超级素数。
其中最大数为999962683。
两个算法时间复杂度比较
应用枚举设计:需要对m位奇数进行检测,枚举数量级为10^m;
应用递推设计:只需要检测9*g(k-1)个(g(k-1)为k-1位超级素数的个数),k=2,3,……,m,因而求m位超级素数共检测的次数为:
s(m)=9*(m-1)∑(k=1)g(k)
这里s(m)是对递推设计的时间复杂度的定量估算,其数量级远低于10^m,即递推设计的时间复杂度要低于枚举设计;
例如,当m=5时,应用枚举设计,需要调用检测函数p(k)次数为23697;而应用递推设计调用p(k)函数次数仅为9*(2+11+39+99)=1359;