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。
常见错误与调试技巧
- 忘记初始化 result 为 1:会导致结果为 0。
- 循环从 0 开始:0 × 任何数 = 0,会破坏结果。
- 递归没有基础情况:程序崩溃(栈溢出)。
- 输入类型不匹配:比如输入字母,程序会死循环或报错。
调试建议:
- 在循环中加入
cout << "i = " << i << ", result = " << result << endl;查看中间值。 - 使用 GDB 或 IDE 断点调试,观察变量变化。
总结:C++ 实例 – 求一个数的阶乘 的完整实践
通过本篇文章,我们从基础的 for 循环实现,到递归函数,再到大数处理,系统地掌握了“C++ 实例 – 求一个数的阶乘”的多种实现方式。每个方法都有其适用场景:
- 循环法:高效、安全,适合大多数情况。
- 递归法:逻辑清晰,适合学习递归思想。
- 大数模拟:突破数值限制,适合研究超大阶乘。
无论你是编程初学者,还是希望巩固 C++ 基础的中级开发者,这个例子都值得你动手敲一遍、跑一遍、改一遍。编程的本质,不在于记住代码,而在于理解每一步背后的逻辑。
下一次当你看到“阶乘”这个词时,希望你能想到的不只是 5! = 120,而是:这是一场关于循环、递归与数值边界的探索之旅。