水仙花数的求解
1. 水仙花数的定义
“水仙花数”是指一个 n 位数,其各位数字的 n 次方之和确好等于该数本身,如:153= 1 ^ 3 + 5 ^ 3 + 3 ^ 3,则153是一个“水仙花数”。
2. 问题描述
求出 0 ~ 100000 之间的所有“水仙花数”并输出。
3. 求解思路
根据水仙花数的定义,要验证一个数是否是水仙花数首先要知道这个数是一个几位数,这是验证的前提,其次还要将每一位的数字提取出来,最后将这些数字分别乘以位数的次方进行验证。有了这个思路之后,就可以将验证的步骤分为以下三步:
· 待验证数是一个几位数
· 提取每一位的数字
· 每一个数字分别乘以位数的次方,然后相加,与原数相比较
只要解决了上面的三个问题,本题也就能够得到解决。
3.1 第一步
如何验证一个数是几位数?
C语言程序里面并没有相关的函数可以使用。
但是,我们不难想到可以使用求商的方法,用待验证数去除以 10 ,没除一次,就计数一次,直到待验证数变为,这样计数计了几次,那待验证数就是几位数。
如下面这段程序:
while (n!= 0)
{
n /= 10;
count++;
}
以 153 为例:
n 为待验证数字,count 初始值为 0.
第一次出循环:n = 15 ,count = 1
第二次出循环:n = 1, count = 2
第三次出循环:n = 0, count = 3
当 n = 0 之后,就不会再进入循环,此时, count 的数字就是 n 的位数。
3.2 第二步
如何提取一个数的每一位的数字?
再C语言中可以使用取余数的方法,将待验证的数%10,这样每次得到的结果就是每一位的具体数字了。
如下面的代码:
while (count--)
{
m = n % 10;
n = n / 10;
}
通过第一步我们已经知道待验证数的位数为 count ,因而我们只需要进行 count 次数的 %10就可以将每一位数字提取出来。
以 153为例:
初始值:m = 0 , n = 153 ,count = 3
第一次出循环:m = 3 , n = 15 , count = 2
第二次出循环:m = 5 , n = 1 , count = 1
第三次出循环:m = 1 , n = 0 , count = 0
当 count = 0 之后,就不会再进入循环.
此时, 待验证数就被分解为了 “1” “3” “5”
3.3 第三步
如何将提取出来的每一个数字分别乘以位数的次方,再相加?
C语言提供了一个数学函数库,里面就有一个幂函数的 pow 函数,我们只需要应用即可。
头文件:#include <math.h>
函数类型:double pow (double x, double y)
解释:x 的 y 次方
下面是结合第二步的代码:
//m 和 i 为待验证的数字
for(int j = 0; j < count; j++)
{
sum += pow(m % 10, count);
m /= 10;
}
if (sum == i)
{
printf("%d ", i);
}
当 sum 的值与待验证的数字相等的时候,这个数字就是水仙花数。
4. 代码实现
4.1 方法一:循环法
代码如下:
int main()
{
int i = 0;
for (i = 1; i <= 100000; i++)
{
int sum = 0;
int count = 0;
int m = i;
int n = i;
while (n)
{
n /= 10;
count++;
}
for(int j = 0; j < count; j++)
{
sum += pow(m % 10, count);
m /= 10;
}
if (sum == i)
{
printf("%d ", i);
}
}
return 0;
}
结果如下图:
这个方法需要注意以下几点:
1、在进行第一步和第二步的时候,会改变待验证数的值,因而在循环刚开始的时候,要复制两个待验证数的值,也就是 n 与 m 的初始化:
int m = i; int n = i;
以此来避免后面无法比较。
2、每进行一次循环,都是对一个新的待验证数的判断,因而判断需要用到的临时变量每一次都要重置,最好的方法就是直接将它们的初始化放到循环体里面,如本代码里面一样。
4.2 方法二:函数法
代码如下:
int Count(int x)
{
int count = 0;
while (x)
{
x /= 10;
count++;
}
return count;
}
void Judge(int x)
{
int n = x;
int sum = 0;
int count = Count(n);
for (int j = 0; j < count; j++)
{
sum += pow(x % 10, count);
n /= 10;
}
if (sum == x)
{
printf("%d ", n);
}
}
int main()
{
int i = 0;
for (i = 1; i <= 100000; i++)
{
Judge (i);
}
return 0;
}
此方法将步骤一变为一个函数块,将步骤二、三合并为一个函数块。
在判断一个函数是否为水仙花数的时候,只需要调用 Judge 函数即可。如果是水仙花数,则直接在频幕上显示,如果不是,则直接跳过本次的数字,直接开始判断下一个数字。
4.3 方法三:递归法
代码如下:
int Count(int x)
{
int count = 0;
while (x)
{
x /= 10;
count++;
}
return count;
}
int Sum(int x, int n)
{
if (x == 0)
{
return 0;
}
else
{
return pow(x % 10, n) + Sum(x / 10, n);
}
}
void Judge2(int x)
{
int n = Count(x);
int m = Sum(x, n);
if (m == x)
{
printf("%d ", x);
}
}
int main()
{
int i = 0;
int sum = 0;
for (i = 1; i <= 100000; i++)
{
Judge2(i);
}
return 0;
}
此方法将三个步骤分为三部分:判断位数、各个位上的数字幂次求和、判断是否为水仙花数。
其余函数与方法二几乎相同,需要强调的是“各个位上的数字幂次求和”这个函数块:
这个函数块采用递归的方法对各个位上的数字幂次求和
它的思想在于每次调用自己来求解,以 153 的求解为例:
求解过程图如上
· 求 153 的各个位上的数字幂次求和的时候,先将 153 的个位数字 3 提取出,3 的 3 次方加上剩下数字 “15” 的各个位数次方和就是 153 各个位上的数字幂次求和的值。
· 将“15 ”的个位数字 5 提取出来,5 的 3 次方加上 “1” 的各个位数次方和就是“ 15 ” 的结果
· 1 的提取就是本身,1 除以 10 之后为 0 ,此时进入函数迭代的结束条件,返回0
得到结果 0 之后,依次返回 1 的结果, 15 的结果和 153 的结果。
5. 总结
解决此类问题的关键在于将问题分块化,一步一步的解决当前的问题,最后再从总体的角度解决问题。
好了,今天的分享就到这里,欢迎大家留言讨论。