文章目录
- 💯前言
- 💯 题目描述:质因数分解
- 输入格式
- 输出格式
- 输入输出样例:
- 💯 问题解析
- 1. 质数的定义
- 2. 题目特点
- 3. 约束范围
- 4. 问题分解
- 💯 解题过程
- 我的初始代码与错误
- 代码尝试 1:错误的暴力解法
- 问题分析:
- 错误修正:使用单层循环优化
- 问题分析:
- 老师的做法与分析
- 老师的代码:
- 分析:
- 改进与优化
- 改进点 1:限制循环范围
- 改进点 2:质数判断
- 最终代码:
- 完整解析
- 思路总结:
- 复杂度分析:
- 💯小结
💯前言
- 本次分析的题目是关于质因数分解的。题目要求我们通过数学和编程技巧,在已知整数
n
n
n 的前提下找到两个质因数中较大的那个。经过分析与尝试,我们对本题进行了深入剖析,不仅学习了老师的实现方法,还对自己的方法进行了调整和优化,最终得出了准确的解法。
以下内容详细记录了题目的解决过程,包括理解问题、自己代码的探索和错误排查、与老师代码的对比、以及从中学习到的知识点。
C++ 参考手册
💯 题目描述:质因数分解
已知正整数 n n n 是两个不同的质数的乘积,试求出较大的那个质数。
输入格式
输入只有一行,包含一个正整数 n n n( 6 < n < 1 0 9 6 < n < 10^9 6<n<109)。
输出格式
输出只有一行,包含一个正整数 p p p,即较大的那个质数。
输入输出样例:
输入:
21
输出:
7
💯 问题解析
要解答本题,首先需要理解题目中的关键概念:
1. 质数的定义
质数是一个大于 1 的正整数,且除了 1 和自身之外,不可被其他数整除。例如,2、3、5、7 是质数,而 4、6、9 不是质数。
2. 题目特点
根据题意,输入的
n
n
n 是两个不同质数的乘积:
n
=
m
1
×
m
2
n = m_1 \times m_2
n=m1×m2
其中
m
1
m_1
m1 和
m
2
m_2
m2 都是质数,且
m
1
≠
m
2
m_1 \neq m_2
m1=m2。需要输出较大的质数
m
2
m_2
m2。
3. 约束范围
6 < n < 1 0 9 6 < n < 10^9 6<n<109。这一范围很大,因此暴力解法需要优化。
4. 问题分解
- 找到 n n n 的两个因子 m 1 m_1 m1 和 m 2 m_2 m2。
- 验证它们是否为质数。
- 输出较大的因子 m 2 m_2 m2。
💯 解题过程
在解题过程中,我们先后尝试了自己的方法和老师的方法,同时通过不断调整和优化代码,逐步发现并解决了问题。
我的初始代码与错误
代码尝试 1:错误的暴力解法
初始时,我采用了一种双重循环的方法,从 i = 1 i = 1 i=1 开始遍历所有可能的因子组合,并比较大小:
#include <iostream>
using namespace std;
int main() {
int n = 0;
cin >> n;
for (int i = 0; i < n; i++) {
for (int j = i; j < n; j++) {
if (i * j == n) {
if (i > j)
cout << i << endl;
else
cout << j << endl;
}
}
}
return 0;
}
问题分析:
- 逻辑错误:
- 双重循环虽然遍历了所有可能的因子组合,但没有判断因子是否是质数。
- 不符合题目要求,结果不一定正确。
- 效率低下:
- 两层循环的时间复杂度是 O ( n 2 ) O(n^2) O(n2)。对于题目输入范围( n < 1 0 9 n < 10^9 n<109),运行时间过长,导致超时(TLE)。
错误修正:使用单层循环优化
我尝试用单层循环替代双重循环,从 2 开始查找因子:
#include <iostream>
using namespace std;
int main() {
int n = 0;
cin >> n;
for (int i = 2; i < n; i++) {
double m = n * 1.0 / i;
if (n / i == m) {
cout << m << endl;
break;
}
}
return 0;
}
问题分析:
- 浮点数误差:
- 使用
double
存储因子可能导致计算精度问题。 - 如输入较大值,可能因浮点数误差导致判断错误。
- 使用
- 缺乏质数判断:
- 即使结果符合 i × j = n i \times j = n i×j=n, i i i 和 j j j 也不一定是质数。
老师的做法与分析
老师的代码:
老师的代码更简洁,利用了 n % i == 0
的逻辑直接判断因子,同时通过 break
提前终止循环:
#include <iostream>
using namespace std;
int main() {
int n = 0;
cin >> n;
for (int i = 2; i < n; i++) {
if (n % i == 0) {
cout << n / i << endl;
break;
}
}
return 0;
}
分析:
- 简洁性:
- 利用
n % i == 0
查找最小因子 m 1 m_1 m1,直接计算出另一个因子 m 2 = n / m 1 m_2 = n / m_1 m2=n/m1。 - 输出 m 2 m_2 m2 时无需额外判断,因为较大的因子总是 m 2 m_2 m2。
- 利用
- 效率:
- 单层循环,复杂度为 O ( n ) O(n) O(n)。适用于小范围的 n n n。
- 缺陷:
- 没有判断 m 1 m_1 m1 和 m 2 m_2 m2 是否为质数。
- 若 n n n 不满足题目要求(即 n n n 不等于两个质数的乘积),结果不一定正确。
改进与优化
在学习老师代码的基础上,我们对算法进行了改进,以提升效率和保证正确性。
改进点 1:限制循环范围
- 根据因子分布规律,较小因子必然小于 n \sqrt{n} n。
- 只需遍历 2 2 2 到 n \sqrt{n} n。复杂度降为 O ( n ) O(\sqrt{n}) O(n)。
改进点 2:质数判断
- 增加一个辅助函数
isPrime
判断因子是否为质数:
bool isPrime(int x) {
if (x < 2) return false;
for (int i = 2; i <= sqrt(x); i++) {
if (x % i == 0) return false;
}
return true;
}
最终代码:
#include <iostream>
#include <cmath>
using namespace std;
bool isPrime(int x) {
if (x < 2) return false;
for (int i = 2; i <= sqrt(x); i++) {
if (x % i == 0) return false;
}
return true;
}
int main() {
int n;
cin >> n;
for (int i = 2; i <= sqrt(n); i++) {
if (n % i == 0) {
int j = n / i;
if (isPrime(i) && isPrime(j)) {
cout << max(i, j) << endl;
return 0;
}
}
}
return 0;
}
完整解析
思路总结:
- 限制因子查找范围到 2 2 2 到 n \sqrt{n} n。
- 对每个找到的因子 i i i,计算另一个因子 j = n / i j = n / i j=n/i。
- 判断 i i i 和 j j j 是否为质数。
- 输出较大的质数因子。
复杂度分析:
- 主循环: O ( n ) O(\sqrt{n}) O(n)。
- 质数验证: 对每个因子进行一次 O ( factor ) O(\sqrt{\text{factor}}) O(factor) 的检查。
- 总复杂度: 接近 O ( n ) O(\sqrt{n}) O(n)。
💯小结
本题通过分析因子规律和质数特性,最终实现了从 O ( n 2 ) O(n^2) O(n2) 到 O ( n ) O(\sqrt{n}) O(n) 的优化。题目看似简单,但其中蕴含了许多算法优化的技巧和数学思想。通过这次练习,我们不仅掌握了质数分解的基本方法,也学习了如何通过数学推导减少计算量,提高程序的运行效率。