C++ 实例 – 求一个数的阶乘(手把手讲解)

C++ 实例 – 求一个数的阶乘:从零开始的递归与循环之旅

在学习 C++ 的过程中,阶乘是一个经典且极具教学价值的编程实例。它不仅帮助初学者理解循环结构和函数调用,还能引导你深入思考递归思想与数值溢出问题。今天,我们就以“C++ 实例 – 求一个数的阶乘”为主题,带你一步步构建完整的解决方案,从最基础的循环实现,到递归写法,再到处理大数的进阶技巧。


什么是阶乘?一个数学小故事

在数学中,一个正整数 n 的阶乘(记作 n!)表示从 1 到 n 所有正整数的乘积。比如:

  • 5! = 5 × 4 × 3 × 2 × 1 = 120
  • 4! = 4 × 3 × 2 × 1 = 24
  • 1! = 1
  • 0! 被定义为 1,这是一个约定俗成的数学规定。

你可以把阶乘想象成“乘法的接力赛”:每一步都把当前结果乘上下一个数,直到跑完所有数字。这个过程非常适合用程序来模拟,而 C++ 正好提供了丰富的语法工具来实现它。


方法一:使用 for 循环实现阶乘

这是最直观、最高效的方式。我们用一个循环变量 i 从 1 递增到目标数 n,每次将 i 乘入结果变量 result 中。

#include <iostream>
using namespace std;

int main() {
    int n;         // 存储用户输入的整数
    long long result = 1;  // 使用 long long 防止溢出,初始值为 1

    // 提示用户输入
    cout << "请输入一个非负整数:";
    cin >> n;

    // 检查输入是否合法
    if (n < 0) {
        cout << "错误:负数没有阶乘!" << endl;
        return 1;
    }

    // 使用 for 循环计算阶乘
    for (int i = 1; i <= n; i++) {
        result = result * i;  // 每次将 i 乘入 result
    }

    // 输出结果
    cout << n << "! = " << result << endl;

    return 0;
}

代码说明:

  • long long result = 1:使用 long long 类型(64 位整数)可以存储更大的数值,避免在计算 20! 时溢出。
  • for (int i = 1; i <= n; i++):循环从 1 开始,到 n 结束,每次 i 自增 1。
  • result = result * i:这是核心逻辑,将当前乘积与下一个数相乘。
  • 输入合法性检查:负数无法定义阶乘,程序应提前拒绝。

💡 小提示:这个版本适合计算 n ≤ 20 的阶乘。超过 20 后,数值会超出 long long 范围。


方法二:使用递归函数实现阶乘

递归是 C++ 中一种强大的编程范式。它把大问题拆解为相同结构的小问题。阶乘本身就是一个天然适合递归的例子。

#include <iostream>
using namespace std;

// 递归函数:计算 n 的阶乘
long long factorial(int n) {
    // 基础情况:0! = 1,1! = 1
    if (n == 0 || n == 1) {
        return 1;
    }

    // 递归情况:n! = n × (n-1)!
    return n * factorial(n - 1);
}

int main() {
    int n;

    cout << "请输入一个非负整数:";
    cin >> n;

    if (n < 0) {
        cout << "错误:负数没有阶乘!" << endl;
        return 1;
    }

    // 调用递归函数计算阶乘
    long long result = factorial(n);

    cout << n << "! = " << result << endl;

    return 0;
}

代码说明:

  • if (n == 0 || n == 1):这是递归的“终止条件”,也叫“基础情况”。没有它,函数会无限调用自己,导致栈溢出。
  • return n * factorial(n - 1):递归调用自身,计算 (n-1)!,然后乘以 n。
  • 递归调用的过程像“一层层剥洋葱”:先调用 factorial(5),它需要 factorial(4),而 factorial(4) 又需要 factorial(3),直到 reach factorial(1),然后逐层返回结果。

🧠 递归的思维:不是一步步算,而是“相信函数能算出小问题,我只要处理好当前层”。


比较循环与递归:谁更优?

特性 循环实现 递归实现
时间复杂度 O(n) O(n)
空间复杂度 O(1) O(n)(递归栈)
可读性 高,逻辑清晰 高,符合数学定义
安全性 高,不易栈溢出 低,n 过大可能崩溃
适用场景 大数计算、性能要求高 教学演示、小规模问题

✅ 建议:在生产环境中,优先使用循环实现。递归更适合理解算法思想。


处理大数阶乘:超大数的挑战

当 n 超过 20 时,阶乘值会迅速超过 long long 的最大值(约 9.2 × 10^18)。比如 21! 已经是 51,090,942,171,709,440,000,远超范围。

这时,我们需要用“大整数”思想:把数字当作字符串来处理,模拟手工乘法。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

// 将大数乘以一个整数
vector<int> multiply(vector<int> num, int x) {
    vector<int> result;
    int carry = 0;  // 进位

    for (int i = 0; i < num.size(); i++) {
        int product = num[i] * x + carry;
        result.push_back(product % 10);  // 保留个位
        carry = product / 10;           // 进位
    }

    // 处理最后的进位
    while (carry) {
        result.push_back(carry % 10);
        carry /= 10;
    }

    return result;
}

// 输出大数阶乘
void printFactorial(int n) {
    vector<int> result = {1};  // 初始化为 1

    for (int i = 2; i <= n; i++) {
        result = multiply(result, i);  // 依次乘 2, 3, ..., n
    }

    // 逆序输出(因为存储时是低位在前)
    cout << n << "! = ";
    for (int i = result.size() - 1; i >= 0; i--) {
        cout << result[i];
    }
    cout << endl;
}

int main() {
    int n;
    cout << "请输入一个非负整数(建议 ≤ 100):";
    cin >> n;

    if (n < 0) {
        cout << "错误:负数没有阶乘!" << endl;
        return 1;
    }

    printFactorial(n);

    return 0;
}

代码说明:

  • vector<int> result = {1}:用数组模拟大整数,每一位存储一个数字(0–9),低位在前。
  • multiply 函数:模拟“竖式乘法”,从右到左逐位相乘,处理进位。
  • printFactorial:调用 multiply 多次,最终逆序输出结果。

🌟 举个例子:计算 10!,程序会依次乘 2, 3, ..., 10,最终输出 3628800。


常见错误与调试技巧

  1. 忘记初始化 result 为 1:会导致结果为 0。
  2. 循环从 0 开始:0 × 任何数 = 0,会破坏结果。
  3. 递归没有基础情况:程序崩溃(栈溢出)。
  4. 输入类型不匹配:比如输入字母,程序会死循环或报错。

调试建议:

  • 在循环中加入 cout << "i = " << i << ", result = " << result << endl; 查看中间值。
  • 使用 GDB 或 IDE 断点调试,观察变量变化。

总结:C++ 实例 – 求一个数的阶乘 的完整实践

通过本篇文章,我们从基础的 for 循环实现,到递归函数,再到大数处理,系统地掌握了“C++ 实例 – 求一个数的阶乘”的多种实现方式。每个方法都有其适用场景:

  • 循环法:高效、安全,适合大多数情况。
  • 递归法:逻辑清晰,适合学习递归思想。
  • 大数模拟:突破数值限制,适合研究超大阶乘。

无论你是编程初学者,还是希望巩固 C++ 基础的中级开发者,这个例子都值得你动手敲一遍、跑一遍、改一遍。编程的本质,不在于记住代码,而在于理解每一步背后的逻辑。

下一次当你看到“阶乘”这个词时,希望你能想到的不只是 5! = 120,而是:这是一场关于循环、递归与数值边界的探索之旅。