传说数学家 卡普雷卡 (Kaprekar)偶然发现铁路旁的里程碑“3025”被雷击得一分为二:30与25.他敏锐地注意到:30+25=55,55^2=3025。
现称这样具有分段和平特性的整数为卡普雷卡数。
先探讨搜索4位卡普雷卡数的基础上引申至一般偶数位卡普雷卡数,并进一步拓广到2段和平方数。
搜索4位卡普卡雷卡数
一个4位整数分为前后两个2位数,若该数等于所分两个2位数和的平方,则称该数为4位卡普雷卡数。
试求出所有的4位卡普雷卡数。
1.说明:
设4位整数a=b*b,存在以下两个枚举方案:
(1)、循环枚举所有4位整数a,应用取整函数(int)和求余(%)把a分段为前后两个2位整数x,y,通过条件判别:若满足a=(x+y)^2且y>=10,即找到卡普雷卡数a,进行打印输出;
(2)、循环枚举2位整数b,求出a=b*b,对平方数a同上分段为前后两个2位整数x,y,进行判别:若满足b=x+y且y>=10,即找到卡普雷卡数a,进行打印输出。
显然方案二的枚举次数较少,较为简单。
2.程序设计:
#include<stdio.h>
#include<math.h>
int main()
{
int a,b,c,x,y;
printf("4位卡普雷卡数:");
c=(int)sqrt(1000);
for(b=c+1;b<=99;b++) /*枚举2位整数b*/
{
a=b*b;
x=a/100;
y=a%100; /*a分为前后两个整数*/
if(y>=10&&b==(x+y))
printf("%d,",a);
}
printf("\n");
}
3.程序运行示例及其注意事项:
4位卡普雷卡数有:2025,3025,
注意:分段和条件检验中的y>=10是确保y为2位数。若去除y>=10的限制,则还有9801,该数分解的两段,前段为一个2位数98,后段是一个1位数1,不符合“分段为前后两个2位整数”的要求。
探求偶数n位卡普雷卡数
偶数n位卡普雷卡数分为前后两个n/2位整数,该数等于所分两个数和的平方;
输入偶数n(4<=n<=14),输出所有的n位卡普雷卡数。
1.说明:
注意到n超过10位,相关变量设置为双精度实型。
1)、设置枚举循环;
设n位平方数a=b*b,求出b的最小值c与最大值d,
设置b(c~d)循环,循环中a=b*b即为n位平方数。
2)、实施分段;
同时设置分段特征量w=10^(n/2),对平方数a应用取整x=floor(a/w)和求余y=fmod(a,w)计算a分段前后的两个n/2位整数x,y。
3)、分段和判别;
如果后一段首位为0,则导致整数y不足n/2位,为此需要加上条件y>=w/10,
若满足条件b=x+y且y>=w/10,即找到n位卡普雷卡数a,进行打印输出。
2.程序设计:
#include<stdio.h>
#include<math.h>
int main()
{
double a,b,m,w,x,y;
int k,n;
long c,d;
while(1)
{
printf("请输入偶数n位(n<=14):");
scanf("%d",&n);
if(n%2!=0)
printf("对不起!你输入的不是偶数,请你重新输入!\n");
else
break;
}
printf("%d位卡普雷卡数有: \n",n);
for(m=1,k=2;k<=n;k++)
m*=10;
for(w=1,k=1;k<=n/2;k++)
w*=10;
c=(long)pow(m,0.5); /*求出枚举b循环的起点*/
d=(long)pow(10*m-1,0.5); /*求出枚举b循环的终点*/
for(b=c+1;b<=d;b++)
{
a=b*b;
x=floor(a/w);
y=fmod(a,w);
if(b==x+y&&y>=w/10) /*分段和条件检验*/
printf(" %.0f=(%.0f+%.0f)^2 \n",a,x,y);
}
}
3.程序运行示例及其注意事项:
请输入偶数n位(n<=14): 14
14位卡普雷卡数有:
19753082469136=(1975308+2469136)^2
24284602499481=(2428460+2499481)^2
25725782499481=(2572578+2499481)^2
30864202469136=(3086420+2469136)^2
注意:以上所得4个14位卡普雷卡数,所分的前后两段都是7位整数。
2段和平方数
作为卡普雷卡数的进一步推广,推出2段和平方数。
定义:把一个n位正整数a分为前后两段(两段的位数不要求相等,两段所生成的两个正整数的位数之和也不要求等于n),若分段的两个正整数之和的平方等于a,则称a为2段和平方数。
例如:88209=(88+209)^2,88209就是一个把自身分为两段“88”与“209”的和的平方数,即2段和平方数。
显然,前面的偶数位卡普雷卡数是偶数位2段和平方数的特例,
输入位数n(2<=n<=16),搜索并输出所有的n位2段和平方数。
1.说明:
注意到n位数比较大,n位数及其相关数据设置为双精度实型。
1)、设置枚举循环;
设a=b * b,a为n位整数,求出b的取值范围[c,d],设置枚举b(c~d)循环,循环中计算的a=b * b确保为n位平方数,
2)、实施分2段;
把一个n位数分为前后两段有n-1种分法:设置分段操作的k(1~n-1)循环,循环中模拟分段的变量w从1开始,通过自乘10可分别得w=10,100,…,10^(n-1)。应用取整x=floor(a/w)和取余y=fmod(a,w)等操作把整数a分为前后两个整数x和y。
3)、分段和条件检验;
在分段操作的k(1~n-1)循环中,每分段得两个整数x和y,检验若b=x+y,则满足分段和平方数条件,
注意到如果后段的首位可能为0,则两个正整数x,y的位数之和可能小于n,这是允许的;如果后段全部为0,则整数y为0而非正整数,这是不允许的,
因而在分段和条件检验中,除了检验b=x+y之外,需要加上条件y>0 。
2.程序设计:
#include<stdio.h>
#include<math.h>
int main()
{
double a,b,m,w,x,y;
long c,d;
int k,n,s=0;
printf("请输入正整数n(2<=n<=16):");
scanf("%d",&n);
for(m=1,k=2;k<=n;k++)
m*=10;
c=(long)pow(m,0.5);
d=(long)pow(10*m-1,0.5);
for(b=c+1;b<=d;b++)
{
a=b*b; /*a位n位平方数*/
w=1;
for(k=1;k<=n-1;k++)
{
w*=10;
x=floor(a/w);
y=fmod(a,w); /*n位平方数a分为前后两段x,y*/
if(b==x+y&&y>0) /*分段和检验*/
{
s++;
printf(" %.0f=(%0.f+%0.f)^2 \n",a,x,y);
}
}
}
if(s>0)
printf("共%d个%d位2段和平方数。\n",s,n);
else printf("没有%d位2段和平方数。\n",n);
}
3.程序运行示例及其注意事项:
请输入正整数n(3<=n<=15):11
20408122449=(20408+122449)^2
21948126201=(21948+126201)^2
33058148761=(33058+148761)^2
35010152100=(35010+152100)^2
43470165025=(43470+165025)^2
共5个11位2段和平方数。
注意:运行程序可知,若位数n为奇数时,所分两段位数一般只相差1(如n=11时,一段为5位,另一段为6位);若位数n为偶数时,所分两段位数相等(如n=14时,两段都为7位)。
因此,对以上程序前后两端实施n-1种分发应该可以简化:
#include<stdio.h>
#include<math.h>
int main()
{
double a,b,m,w,x,y;
long c,d;
int k,n,s=0;
printf("请输入正整数n(3<=n<=15):");
scanf("%d",&n);
for(m=1,k=2;k<=n;k++)
m*=10;
c=(long)pow(m,0.5);
d=(long)pow(10*m-1,0.5);
for(b=c+1;b<=d;b++)
{
a=b*b; /*a位n位平方数*/
for(w=1,k=1;k<=n/2;k++)
w*=10;
for(k=0;k<=n%2;k++) /*由n的奇偶控制循环次数*/
{
x=floor(a/w);
y=fmod(a,w); /*n位平方数a分为前后两段x,y*/
if(b==x+y&&y>0) /*分段和检验*/
{
s++;
printf(" %.0f=(%0.f+%0.f)^2 \n",a,x,y);
}
w*=10;
}
}
if(s>0)
printf("共%d个%d位2段和平方数。\n",s,n);
else printf("没有%d位2段和平方数。\n",n);
}