问题
水仙花数(Narcissistic number)也被称为超完全数字不变数(pluperfect digital invariant, PPDI)、自恋数、自幂数或阿姆斯壮数数(Armstrong number)。
它是指一个n位数(n≥3),它的每个位上的数字的n次幂之和等于它本身。例如:1^3 + 5^3 + 3^3 = 153,则153是一个水仙花数。对于三位数,水仙花数有153、370、371、407四个数。
这里我们扩大一点范围,假设水仙花数也可以小于三位数,只要一个n位数各位数字的n次方之和确好等于该数本身,我们就认为它是水仙花数,那么现在我们求出0~100000之间的所有“水仙花数”并输出。
整体分析
我们可以拆分一下我们到底要做哪几件事,就像你现在要煮一碗拉面,你可以将整个过程拆分为去买菜、备菜、煮面、吃、洗碗。解决这个问题,我们可以拆成生成0~100000的数组,求每个数是几位数(此时假设位数为n),求每个位上的数字的n次方之和(还可以进一步细分为求每位的n次方、求和两个动作),判断是否是水仙花数、打印这几件事。
具体分析
首先,生成0~100000的数字,这是最简单的,只需要一个循环语句:
int main()
{int i = 0;for(i=0; i<100000; i++){//具体的:计算位数n、求各位n次方和、判断是否为水仙花数和打印}return 0;
}
现在,我们就有了最外层的框架。
求几位数
怎么样才能得到这个数是几位数呢?这里我们可以用一种办法,我们知道,‘/’是整除,所以一个数/10的结果是去掉了最低位:
int a = 123;
int a = a/10;//将a整除10的结果重新赋值给a,现在a的值变为了12
那么,我们可以循环这个过程,定义一个变量count初始化为0,让我们的i每次/10后都count++,当i等于0的时候整除停止,此时我们得到的count就是位数:
看完这个图你一定就理解了吧。
得到各位上数字的count次方和
怎么做到这一点呢?我们知道一个数%10得到的就是最低位上的那个数字…等下,你不知道为什么?那我先浅浅讲解一下,已经知道的朋友可以跳过这段:
C语言中的%符号有两种主要用法:作为格式化字符串中的占位符和作为取模运算符,我们这里用到的是后一种。
%用作取模运算符时,表示整数除法的余数。例如,result = a % b; 计算的是 a 除以 b 的余数,并将结果赋值给 result。
使用%d时,确保对应的变量是整数类型(int),如果是长整型(long)则使用%ld或%Ld,短整型(short)使用%hd。
在进行取模运算时,要注意除数不能为零,否则会导致程序运行错误。
那么为什么一个数%10得到的就是最低位上的那个数字呢?我们这里说的“一个数”指的是十进制表示的一个数。%计算的是除之后的余数,%10就是除10后的余数,因为是十进制,所以剩在最低位上的就是无法达到10从而进位的数,所以也就是%10的余数。
这么说,应该能够理解了吧。
但是一次%10我们只能得到一个数字最低位的数啊,怎样才能把每一位的数字都拿到呢?其实我们的“武器”还是循环。而且我们还要借助上面刚提过的/10,因为将一个数/10就是整除10,而效果就是去掉了最低位,所以我们拿到一位、去掉一位、再拿一位、再去一位,不就能从低到高拿到每一位了吗?
既然我们要拿到每一位,并且要求它们的count次方的和,这时我们就创建一个变量sum,用来每轮循环存放这一位n次方的值,随着循环进行累加。
在C语言中我们想要求一个数的几次方,我们需要用到一个函数叫做pow,它的头文件为math.h,它所需的两个参数前者就是要求次方的数,后者就是几次方。
在了解这些后我们就能写出这个循环:
while (i)
{sum += pow(i % 10, count);i = i / 10;
}
陷阱
但是我的朋友,你先别高兴得太早,以上的代码其实有一个很难察觉的陷阱,这也是我在调试之后才找出来的:
对于后面这个求次方和的代码来说,此时的i在经过前一个循环后已经变成0而不是它原本的数字了。 那么我们根本都无法进入后面这个循环。其实,到最后判断的时候我们还要用到一次i的原始值。
那么怎么解决这个问题呢?
其实解决办法很简单,我们的目标就是不去改动i的值,那么我们只要在一开始就把i的值先赋给一个变量,并且在它需要变回i的值的时候,把i再赋一次给它就行了。所以以上两个循环应该改为:
for (i = 0; i < 100000; i++)
{int sum = 0;//这个要写在里面,否则对于每一个要检测的i来说,sum就不是从零开始了int count = 0;//这个也要写里面,理由同上。int tmp = i;//tmp接受i真正的值,替i进行变化,不用改动iwhile (tmp){count++;tmp = tmp / 10;}tmp = i;//让tmp重新变回i的值while (tmp){sum += pow(tmp % 10, count);tmp = tmp / 10;}
}
最终判断
现在,几位数(count)有了,各位上数字的count次方和有了,我们只需要拿和与我们的i进行比较判断就行了:
if (sum == i)printf("%d ", i);
完整代码参考:
在vs2022上运行效果:
那么,本篇博客内容到此就结束了,希望大家看完发现问题向我反馈,共同进步!