文章目录
- 💯前言
- 💯题目介绍
- 输入格式
- 输出格式
- 输入输出样例
- 说明/提示
- 💯实现方法一:单层 `for` 循环计算
- 代码实现
- 运行逻辑解析
- 优点
- 不足
- 💯实现方法二:双层 `for` 循环计算
- 代码实现
- 运行逻辑解析
- 优点
- 不足
- 💯实现方法三:`while` 循环实现
- 代码实现
- 运行逻辑解析
- 优点
- 不足
- 💯对比与总结
- 效率对比
- 适用场景
- 💯优化与扩展
- 提前终止优化
- 现代化输出
- 函数封装
- 💯小结
💯前言
- 在计算机科学和数学中,自然对数底数 e e e 是一个重要的数学常数,出现在许多算法和模型中。在本次分析中,我们将基于一个经典的编程题目,通过不同实现方法计算 e e e 的值,探讨各种实现的优劣,最终总结出最佳实践和相关优化方案。本文将从题目介绍开始,逐步讲解每种实现方式的特点及适用场景,同时延伸到更广泛的优化和扩展。
C++ 参考手册
💯题目介绍
B2079 求出 e 的值
题目描述:
利用公式
e = 1 + 1 1 ! + 1 2 ! + 1 3 ! + ⋯ + 1 n ! e = 1 + \frac{1}{1!} + \frac{1}{2!} + \frac{1}{3!} + \cdots + \frac{1}{n!} e=1+1!1+2!1+3!1+⋯+n!1
求自然对数底数 e e e 的值,要求保留小数点后10位。
输入格式
输入只有一行,该行包含一个整数 n n n,表示计算 e e e 时累加到 1 / n ! 1/n! 1/n!。
输出格式
输出只有一行,该行包含计算出来的 e e e 的值,要求打印小数点后 10 位。
输入输出样例
- 输入 #1:
10
- 输出 #1:
2.7182818011
说明/提示
- 输入范围为 2 ≤ n ≤ 15 2 \leq n \leq 15 2≤n≤15。
- 本题的阶乘增长较快,但计算范围内( n ≤ 15 n \leq 15 n≤15),可以通过合理的数据类型避免溢出。
💯实现方法一:单层 for
循环计算
这是初学者常用的一种实现方法,逻辑清晰且代码简洁,能够直接累积计算每一项的值。以下为代码及详细分析:
代码实现
#include <iostream>
#include <cstdio>
using namespace std;int main() {int n = 0;cin >> n; // 输入阶数 ndouble e = 1; // 初始化 e 的值为公式第一项 1long long m = 1; // 阶乘初始化为 1,使用 long long 避免溢出for (int i = 1; i <= n; i++) { // 从第 1 项开始累加m *= i; // 累乘计算阶乘 i!e += 1.0 / m; // 累加当前项 1/i! 到 e}printf("%.10lf", e); // 保留 10 位小数输出结果return 0;
}
运行逻辑解析
-
变量初始化:
n
表示输入的阶数。e
存储计算结果,初始值为 1(对应公式中的第 0 项)。m
用于存储阶乘,初始值为 1。
-
循环计算:
- 从 1 开始,逐步累乘得到当前的阶乘值 i ! i! i!。
- 累加每一项的倒数值 1 i ! \frac{1}{i!} i!1 到 e e e。
-
输出结果:
- 使用
printf
格式化输出,保留小数点后 10 位。
- 使用
优点
- 效率高:每次计算利用上一次的结果,无需重复计算阶乘。
- 实现简单:逻辑直观,代码量少。
- 时间复杂度: O ( n ) O(n) O(n)。
不足
- 逻辑固定,适用于计算所有项都必须累加的场景。
💯实现方法二:双层 for
循环计算
这是一种更贴近数学公式的实现方法,通过嵌套循环完成阶乘计算和累加。代码如下:
代码实现
#include <iostream>
#include <cstdio>
using namespace std;int main() {int n = 0;cin >> n; // 输入阶数 ndouble e = 1; // 初始化 e 的值为公式第一项 1for (int i = 1; i <= n; i++) { // 外层循环控制累加项数long long m = 1; // 初始化当前项阶乘为 1for (int j = 1; j <= i; j++) { // 内层循环计算阶乘 i!m *= j;}e += 1.0 / m; // 累加当前项到 e}printf("%.10lf", e); // 保留 10 位小数输出结果return 0;
}
运行逻辑解析
-
外层循环:
- 控制当前计算项的序号,从 1 到 n n n。
-
内层循环:
- 逐步累乘,计算当前项的阶乘 i ! i! i!。
-
累加计算:
- 将当前项 1 i ! \frac{1}{i!} i!1 累加到 e e e 中。
优点
- 逻辑清晰:每一层循环分别处理阶乘和累加。
- 贴近数学公式:非常直观地将公式分解为两部分。
不足
- 效率低:阶乘值每次都需要重新计算,存在大量重复计算。
- 时间复杂度: O ( n 2 ) O(n^2) O(n2)。
💯实现方法三:while
循环实现
这是老师给出的实现方式,使用 while
循环控制计算过程,进一步简化了逻辑,同时保留高效性。
代码实现
#include <iostream>
#include <cstdio>
using namespace std;int main() {int n = 0;cin >> n; // 输入阶数 nint i = 1; // 循环计数器,初始为 1double e = 1; // 初始化 e = 1long long fac = 1; // 阶乘初始化为 1while (i <= n) { // 循环从 1 到 nfac *= i; // 更新阶乘值e += 1.0 / fac; // 累加当前项i++; // 计数器自增}printf("%.10lf\n", e); // 输出结果return 0;
}
运行逻辑解析
-
变量初始化:
- 循环计数变量
i
初始为 1,用于控制循环。 - 阶乘值
fac
初始为 1,用于累乘计算。 - 累加结果
e
初始为 1。
- 循环计数变量
-
while
循环:- 当 i ≤ n i \leq n i≤n 时,计算当前项的阶乘,并累加其倒数到 e e e。
-
输出结果:
- 使用
printf
输出结果,保留小数点后 10 位。
- 使用
优点
- 逻辑清晰:将循环控制变量与计算逻辑分离,代码条理性更强。
- 高效:与单层
for
循环一样,每次计算直接更新阶乘值,避免重复计算。 - 灵活性:
while
循环适合条件控制较多的场景,易于扩展。
不足
- 与
for
循环相比,稍显冗长。
💯对比与总结
效率对比
实现方式 | 时间复杂度 | 重复计算 | 灵活性 |
---|---|---|---|
单层 for 循环 | O ( n ) O(n) O(n) | 无 | 中等 |
双层 for 循环 | O ( n 2 ) O(n^2) O(n2) | 有 | 中等 |
while 循环实现 | O ( n ) O(n) O(n) | 无 | 高 |
适用场景
- 单层
for
循环:适合简单、固定范围的计算。 - 双层
for
循环:更适合教学场景,便于逐步拆解公式。 while
循环:适合条件较复杂的计算,尤其是需要提前终止的场景。
💯优化与扩展
提前终止优化
对于较大的 n n n,后续项对 e e e 的贡献极小,可以通过设置阈值提前终止循环:
while (i <= n) {fac *= i;double term = 1.0 / fac;if (term < 1e-12) break; // 如果当前项小于阈值,提前退出e += term;i++;
}
现代化输出
使用 C++ 的 cout
和 setprecision
输出结果:
#include <iostream>
#include <iomanip>
using namespace std;int main() {int n;cin >> n;double e = 1;long long fac = 1;for (int i = 1; i <= n; i++) {fac *= i;e += 1.0 / fac;}cout << fixed << setprecision(10) << e << endl;return 0;
}
函数封装
将阶乘计算或 e e e 的累加封装为函数,方便复用:
double calculateE(int n) {double e = 1;long long fac = 1;for (int i = 1; i <= n; i++) {fac *= i;e += 1.0 / fac;}return e;
}
💯小结
本文通过一个经典的编程题目,探讨了 e e e 的计算方法及其优化。无论是单层 for
循环、双层循环,还是 while
循环,各有其适用场景和优劣。根据实际需求选择合适的方法,同时可通过提前终止、现代化输出、函数封装等方式进一步优化。希望本文能为读者带来关于算法实现与优化的启发!