Appearance
数论入门 - 质因数分解
一、课程目标
- 唯一分解定理
- 朴素算法
- 算法优化
二、目标详解
1、唯一分解定理
唯一分解定理(又称算术基本定理) ,即:每个大于
对任意大于
每个合数都可以写成若干质数因子相乘的形式,这种形式叫做质因子分解。
例如:
例如:
分解质因数只针对合数,因为质数没有其它质因子。
2、质因数分解
朴素算法
假设
那么我们可以循环从
c++
while(n % 2 == 0) {
cout << 2 << " ";
n /= 2;
}
质因子从小到大被剥离,最后
例如:2 2 5 5
后,
算法代码如下:
c++
for(int i=2; n>1; i++)
while(n%i==0) {
cout<<i<<" ";
n /= i;
}
时间复杂度:如果
算法优化
朴素算法超时,原因是枚举的范围太大了,那这个范围是否可以缩小呢?
回顾 唯一分解定理 :对任意大于
引理:对任意大于
证明:
- 假设
有两个大于 的质因子 ,写出 的唯一分解式 ,其中 。得 ,矛盾。这就证明了至多只有一个大于 的质因子。 - 在上式中取
,矛盾仍成立。这就证明了若唯一大于 的质因子存在,则它在唯一分解式中的指数一定是 。
质因子分解:根据上述引理,只需要枚举
以上判断,其实与查找一个数sqrt(n)
思路一致!
c++
int n;
cin >> n;
for (int i = 2; 1ll * i * i <= n; i++) { //i*i如果超过int,就需要转为long long,1ll就是long long 类型的1
while (n % i == 0) {
cout << i << " ";
n /= i;
}
}
if (n != 1) { // 如果n本身是质数
cout << n << " ";
}
演示-质因子分解
题目描述
输入一个整数n,求其质因数。
输入样例
text
100
输出样例1
text
2 2 5 5
练习-序列质因子分解
题目描述
输入
输入格式
- 第一行
- 第二行
个数
输出格式
行,每行都是对应的质因子
输入样例
text
3
12 50 240
输出样例
text
2 2 3
2 5 5
2 2 2 2 3 5
数据范围
code:
c++
#include <iostream>
using namespace std;
long long n,x;
int main() {
cin >> n;
while(n--) {
cin >> x;
for (int i = 2; 1ll * i * i <= x; i++) {
while (x % i == 0) {
cout << i << " ";
x /= i;
}
}
if (x != 1) { // 如果n本身是质数
cout << x << " ";
}
cout << endl;
}
return 0;
}
练习-质因子序列
题目描述
对于
输入格式
- 第一行
- 第二个
个正整数
输出格式
- 最小公倍数的质因子序列,从小到大排列
输入样例
text
3
8 15 6
输出样例
text
2 2 2 3 5
数据范围
代码参考
40分代码,辗转相除法+暴力
c++
#include <iostream>
using namespace std;
int n, a[105];
int gcd(int a, int b) {
while(b != 0) {
int t = a % b;
a = b;
b = t;
}
return a;
}
int lcm(int a, int b) {
return a/gcd(a, b)*b;
}
void factor(int x) {
for (int i = 2; i * i <= x; i++) {
while(x % i == 0) {
cout << i << " ";
x /= i;
}
}
if (x > 1) {
cout << x;
}
cout << endl;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
int lcm_ma = a[1];
for (int i = 2; i <= n; i++) {
lcm_ma = lcm(lcm_ma, a[i]);
}
factor(lcm_ma);
return 0;
}
40分代码,辗转相除法+分治算法
c++
#include <iostream>
using namespace std;
int n, a[105];
int gcd(int a, int b) {
while(b != 0) {
int t = a % b;
a = b;
b = t;
}
return a;
}
long long lcm(long long a, long long b) {
return a/gcd(a, b)*b;
}
int lcm_find(int left, int right) {
if (left == right) {
return a[left];
}
int mid = (left + right) / 2;
int left_lcm = lcm_find(left, mid);
int right_lcm = lcm_find(mid+1, right);
return lcm(left_lcm, right_lcm);
}
void factor(long long x) {
for (long long i = 2; i * i <= x; i++) {
while(x % i == 0) {
cout << i << " ";
x /= i;
}
}
if (x > 1) {
cout << x;
}
cout << endl;
}
int main() {
cin >> n;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
long long lcm_ma = lcm_find(1, n);
factor(lcm_ma);
return 0;
}
算法优化,请阅读以下文档
先自己尝试写代码,然后再查看以下代码:
满分代码
c++
#include <iostream>
#include <cmath> // 用于sqrt函数
using namespace std;
int n,x,tong[10005];
// 求一个数的质因子
void factorize(int num) {
for (int i = 2; i * i <= num; ++i) {
int cnt_i = 0;
while (num % i == 0) {
cnt_i++;
num /= i; // 继续除以当前因子,直到不能再整除
}
if (tong[i] < cnt_i) tong[i] = cnt_i;
}
// 如果剩下的数大于1,说明它是质数
if (num > 1 && tong[num] == 0) {
tong[num] = 1;
}
}
int main() {
cin >> n;
for (int i = 0; i < n; ++i) {
cin >> x;
factorize(x);
}
// 输出所有的质因子
for (int i = 2; i <= 10000; ++i) {
while(tong[i]--) {
cout << i << " ";
}
}
cout << endl;
return 0;
}
:::