C++ 水仙花数

在数学趣味题中,所谓的 “水仙花数”,指的是一个 100~999 之间的三位数,它的百位、十位、个位数字的立方之和恰好等于这个数本身。

比如 153 就是一个典型的水仙花数,因为 1³ + 5³ + 3³ = 1 + 125 + 27 = 153。在 C++ 中求解水仙花数,最关键的一步就是先把三位数的每一位数字单独拆出来,下面我们就从拆位方法开始讲起。

如何拆出三位数的每一位?

想要判断一个三位数是不是水仙花数,首先要解决一个问题:给定一个完整的三位数(比如 153),我们应该怎样把它的百位、十位和个位数字分别提取出来?

在 C++ 的算术运算符中,两个整数相除(/)会自动舍去小数部分,只保留整数商。而取余运算(%)则可以得到相除之后的余数。不妨拿 153 试算一下,配合这两个运算符就能把每一位数字拆出来:

153 % 10 = 3
153 / 100 = 1
153 / 10 % 10 = 5

假设三位数保存在整型变量 n 中,上面这 3 个算式的拆位思路如下:

  • 拆出个位:使用 n % 10 计算。153 除以 10 的商是 15,余数是 3,任何正整数对 10 取余都能直接拿到最右边的个位数字。
  • 拆出百位:使用 n / 100 计算。将 153 除以 100 会直接舍去末尾两位,得到的整数商就是最左边的百位数字 1。
  • 拆出十位:使用 n / 10 % 10 计算。我们先算 153 / 10 去掉个位得到 15,再算 15 % 10 取出余数,就能拿到中间的十位数字 5。当然,写成先去掉百位再除以 10 的 n % 100 / 10,算出来的结果也是一样的。

判断单个水仙花数

拆出百位、十位和个位上的数字之后,我们只需要算出这 3 个数字各自的立方和,再配合 if...else 语句判断结果是否等于原来的三位数 n 就可以了。请看下面例子。

示例 1:判断 153 是否为水仙花数

#include <iostream>

int main() {
    int n = 153;
    int a = n / 100;        // 拆出百位
    int b = n / 10 % 10;    // 拆出十位
    int c = n % 10;         // 拆出个位

    int sum = a * a * a + b * b * b + c * c * c;
    if (sum == n) {
        std::cout << n << " 是水仙花数\n";
    } else {
        std::cout << n << " 不是水仙花数\n";
    }
    return 0;
}

运行结果如下。

153 是水仙花数

分析:

上面这个例子非常简单,变量 a、b、c 依次保存从 n 中拆出来的百位 1、十位 5 和个位 3。小伙伴们要注意,n / 100 和 n % 10 只是读取 n 的值参与运算,并没有修改 n 本身,因此拆位结束后 n 的值依然是 153。

随后程序算出立方和 sum 为 153,满足条件 sum == n,于是输出 “153 是水仙花数”。如果我们把开头的 n 改成 123,算出来的 sum 就是 36(不等于 123),程序就会进入 else 分支输出 “123 不是水仙花数”。

下面代码中,变量 b 的最终值是 7,小伙伴们可以思考一下为什么。

int n = 370;
int b = n / 10 % 10;

注意 计算整数立方时,直接写成 a * a * a 比调用 <cmath> 中的 std::pow() 更稳妥。因为 std::pow() 按浮点数计算,在某些环境下可能产生类似 124.999999 的微小误差,赋给 int 时会被截断成 124 而导致判断出错。

找出所有的水仙花数

既然水仙花数都是三位数,我们只需要在上面判断逻辑的外面套上一层 for 循环,让变量 n 从 100 依次遍历到 999,就能把所有的水仙花数全部找出来。

示例 2:遍历 100~999 寻找水仙花数

#include <iostream>

int main() {
    std::cout << "100~999 之间的水仙花数有:";
    for (int n = 100; n <= 999; n++) {
        int a = n / 100;        // 拆出百位
        int b = n / 10 % 10;    // 拆出十位
        int c = n % 10;         // 拆出个位

        int sum = a * a * a + b * b * b + c * c * c;
        if (sum == n) {
            std::cout << n << " ";
        }
    }
    std::cout << '\n';
    return 0;
}

运行结果如下。

100~999 之间的水仙花数有:153 370 371 407

分析:

从运行结果可以看到,在 100~999 之间一共有 153、370、371、407 这 4 个水仙花数。代码中的 for 循环让 n 从 100 递增到 999,每一轮都拆出当前三位数的各位数字并算出立方和 sum,只要满足 sum == n 就将它打印出来。

阿姆斯特朗数与多位数拆位

水仙花数在数学上其实是 “阿姆斯特朗数” 在三位数下的一种特例。更宽泛地说,如果一个 k 位正整数,它各位数字的 k 次方之和恰好等于它本身,就称为阿姆斯特朗数。例如四位数中的 “四叶玫瑰数” 1634,就满足 1⁴ + 6⁴ + 3⁴ + 4⁴ = 1634。

如果待处理的正整数位数不固定,我们可以先把它存进临时变量 temp 中,再配合 while (temp > 0) 循环从低位到高位逐位拆开。每一轮先通过 temp % 10 取出当前的个位,再执行 temp /= 10 去掉最后一位,重复到 temp 变成 0 即可。

上一篇:

下一篇:

给站长反馈

绿叶网正在不断完善中,小伙伴们如果发现任何问题,还望多多给站长反馈,谢谢!

邮箱:lvyenet@vip.qq.com

「绿叶网」服务号
绿叶网服务号放大
关注服务号,微信也能看教程。
绿叶网服务号