C 练习实例13 – 水仙花数(建议收藏)

C 练习实例13 – 水仙花数:从数学谜题到编程实践

在学习 C 语言的过程中,有一类题目特别适合初学者掌握循环、条件判断和数学运算的综合应用——它们就是经典的“数字趣味题”。今天我们要讲解的,正是其中非常经典的一道:水仙花数。

所谓水仙花数,是指一个三位数,它的每个位上的数字的立方和等于它本身。比如 153,因为 1³ + 5³ + 3³ = 1 + 125 + 27 = 153,所以 153 就是一个水仙花数。这类题目不仅能锻炼你的逻辑思维,还能让你更深入地理解 C 语言中变量、循环与数学运算的结合使用。

在接下来的内容中,我们将通过一步步拆解问题,手把手带你写出正确的 C 程序,同时讲解背后的核心知识点。无论你是刚接触 C 语言的新手,还是想巩固基础的中级开发者,相信都能从中有所收获。


什么是水仙花数?从数学角度理解

在正式写代码之前,先让我们从数学角度理解水仙花数的本质。

水仙花数的定义是:一个三位整数,其各位数字的立方和等于该数本身。

举个例子:

  • 153:1³ + 5³ + 3³ = 1 + 125 + 27 = 153 ✅
  • 371:3³ + 7³ + 1³ = 27 + 343 + 1 = 371 ✅
  • 407:4³ + 0³ + 7³ = 64 + 0 + 343 = 407 ✅

而像 123:1³ + 2³ + 3³ = 1 + 8 + 27 = 36 ≠ 123 ❌,就不是水仙花数。

你可以把水仙花数想象成“数字界的自恋者”——它只爱自己,而且是用“立方”这种夸张的方式表达爱意。每一位数字都把自己“升了三次方”,然后加在一起,如果刚好等于自己,那它就是水仙花数。

这个定义虽然简单,但要让程序自动找出所有三位数中的水仙花数,就需要用到循环和数学函数,这正是 C 练习实例13 – 水仙花数的核心价值所在。


如何分解一个三位数的各个数字?

要判断一个数是否是水仙花数,必须先把它拆成百位、十位和个位。这一步是整个程序的关键,也是初学者容易卡住的地方。

在 C 语言中,我们可以通过**取模运算(%)整除运算(/)**来实现数字拆分。

举个例子,对数字 153:

  • 个位:153 % 10 = 3(取余,得到最后一位)
  • 十位:(153 / 10) % 10 = 15 % 10 = 5(先去掉个位,再取余)
  • 百位:153 / 100 = 1(直接整除百位)

这个过程就像从一个“数字盒子”里一层层取出数字,每取出一层,就用 % 和 / 来“切割”。

下面是完整的拆分代码示例:

#include <stdio.h>

int main() {
    int number = 153;  // 要判断的三位数
    int a, b, c;       // 分别存放百位、十位、个位

    // 提取个位
    c = number % 10;

    // 提取十位:先去掉个位,再取个位
    b = (number / 10) % 10;

    // 提取百位:直接整除100
    a = number / 100;

    // 输出结果
    printf("百位: %d, 十位: %d, 个位: %d\n", a, b, c);

    return 0;
}

运行结果:

百位: 1, 十位: 5, 个位: 3

注意:这里我们用 number / 10 是整数除法,会自动舍去小数部分,这是 C 语言的重要特性,必须熟练掌握。


用循环找出所有水仙花数

现在我们知道了如何拆分一个三位数,接下来就是遍历所有三位数(100 到 999),逐个判断是否为水仙花数。

这里要用到 for 循环。循环变量从 100 开始,到 999 结束,每个数都做一次判断。

我们用一个变量 sum 来存储各位数字的立方和,然后和原数比较。

#include <stdio.h>

int main() {
    printf("水仙花数有:\n");

    // 遍历所有三位数
    for (int i = 100; i <= 999; i++) {
        int a, b, c;           // 百位、十位、个位
        int sum = 0;           // 用于累加立方和

        // 拆分数字
        a = i / 100;           // 百位
        b = (i / 10) % 10;     // 十位
        c = i % 10;            // 个位

        // 计算立方和
        sum = a * a * a + b * b * b + c * c * c;

        // 判断是否为水仙花数
        if (sum == i) {
            printf("%d ", i);
        }
    }

    printf("\n");  // 换行结束输出
    return 0;
}

代码解析:

  • for (int i = 100; i <= 999; i++):从 100 到 999 遍历所有三位数。
  • a = i / 100:取出百位数字。
  • b = (i / 10) % 10:先去掉个位,再取个位。
  • c = i % 10:直接取个位。
  • sum = a*a*a + b*b*b + c*c*c:计算立方和。
  • if (sum == i):如果立方和等于原数,说明是水仙花数,打印出来。

运行结果:

水仙花数有:
153 371 407

这说明,三位数中只有 153、371、407 是水仙花数,一共三个。


优化思路:使用 pow 函数简化立方计算

上面的代码中我们用了 a*a*a 这种写法来计算立方。其实 C 语言标准库中有一个 pow 函数,可以计算幂运算,比如 pow(a, 3) 表示 a 的三次方。

不过要注意:pow 函数返回的是 double 类型,而我们处理的是整数,所以需要强制转换。

#include <stdio.h>
#include <math.h>  // 引入数学库,使用 pow

int main() {
    printf("水仙花数有:\n");

    for (int i = 100; i <= 999; i++) {
        int a, b, c;
        double sum;  // 用 double 存储 pow 的结果

        a = i / 100;
        b = (i / 10) % 10;
        c = i % 10;

        // 使用 pow 函数计算立方
        sum = pow(a, 3) + pow(b, 3) + pow(c, 3);

        // 强制转换为 int 并比较
        if ((int)sum == i) {
            printf("%d ", i);
        }
    }

    printf("\n");
    return 0;
}

注意事项:

  • 必须包含头文件 <math.h>
  • pow 返回 double,所以需要 (int)sum 转换。
  • 由于浮点数精度问题,建议在实际项目中优先使用 a*a*a 这种整数运算,更安全、高效。

常见错误与调试建议

在写 C 练习实例13 – 水仙花数时,初学者常犯几个错误:

错误类型 说明 正确做法
数字拆分错误 比如误用 i % 100 来取十位 应使用 (i / 10) % 10
循环范围错误 写成 i < 100i > 999 三位数是 100 到 999,包含边界
忽略头文件 使用 pow 但未包含 <math.h> 编译时会报错,必须添加
浮点数比较错误 直接用 sum == i 比较 double 和 int 应转换为整数后比较

建议在调试时,先打印中间变量的值,比如 abcsum,确认拆分和计算是否正确。


实际应用场景与拓展思考

虽然水仙花数本身是数学趣味题,但它的解法思路在现实编程中非常有用:

  • 数据验证:检查用户输入的数字是否符合某种规则。
  • 数字处理:在密码学、验证码生成中,常需要拆分和重组数字。
  • 算法训练:培养对循环、条件判断和数学运算的综合运用能力。

你可以进一步拓展这个程序:

  • 扩展到四位数、五位数的“自恋数”(如 1634 = 1⁴ + 6⁴ + 3⁴ + 4⁴)。
  • 让用户输入一个数字,判断是否为水仙花数。
  • 统计所有三位数中水仙花数的个数。

这些拓展都能帮助你加深对 C 语言的理解。


总结与回顾

C 练习实例13 – 水仙花数,看似简单,实则融合了多个核心知识点:循环、整除、取模、条件判断、数学运算等。通过这道题,你不仅能写出一个完整的程序,还能理解“如何把数学问题转化为程序逻辑”。

关键点回顾:

  • %/ 拆分数字是基础技能。
  • for 循环遍历范围要准确。
  • 立方和计算可以使用 a*a*apow,但注意类型转换。
  • 调试时打印中间值,能快速定位错误。

这道题就像一道“编程小测验”,既检验了你的语法掌握程度,也锻炼了你的逻辑思维能力。当你成功运行出 153、371、407 时,那种“我做到了”的成就感,正是学习编程最宝贵的财富。

坚持练习,你会发现,每一个看似简单的例子,背后都藏着通往复杂程序的大门。