[算法学习记录] 因数分解、质因数分解、埃氏筛法
因数分解
如果要求一个数n的所有质因数,考虑到n = a * b,如果a<b,那么一定有$$a\leq\sqrt{n}\leq b$$考虑枚举a,那么b = n/a,相较于使用暴力枚举,这种方法可以显著降低时间复杂度。
例题 星码Starrycoding 【模板】求N的所有因子
本题就是一个因数分解的模版题,直接上代码:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e6+9;
//题目中数据量为1e12,,则最多有1e6个质因数
vector<ll> a;
void solve()
{
ll n;cin >> n;
for(ll i = 1;i * i <= n;i++)
{
if(n%i==0)
{
a.push_back(i);
if(i != n / i)a.push_back(n / i);
}
}
//n = a*b;所以,只要枚举出一个a就能得到另一个b
sort(begin(a),end(a));
//按题目要求从小到大排序
for(const auto &num : a)cout << num <<" ";
cout << "\n";
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;
while(_--)solve();
return 0;
}
质因数分解
同样是找因数,不过这次多了一个限制条件,找质因数直接用循环暴力判断肯定是行不通的,所以我们要考虑其它方法。
根据唯一分解定理可知:对于任意N>1,都可以分解为若干质数的乘积,且分解方法唯一;我们可以利用这一特性,每当找到一个质因数,就一直用原数去除以这个质因数,直到除不尽,这样就可以动态缩小数据范围,以优化时间复杂度,代码实现如下:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
vector<ll> a;
void solve()
{
ll n;cin >> n;
for(ll i = 2;i * i<=n;i++)
{
if(n % i)continue;
a.push_back(i);
while(n % i == 0) n/=i;
}
a.push_back(n);
//要记得把最后剩下的n也加进去,此时的n一定是一个质因数
for(auto &b : a)cout << b <<" ";
cout << "\n";
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;
while(_--)solve();
return 0;
}
既然说到了质数,就在补充一个快速筛选一个区间内的质数的方法。
埃氏筛法
埃氏筛法是一种快速寻找某一区间的质数的算法,实现方法如下:
- 创建一个数组用来标记质数,默认初始化为0,并将0与1初始化为1(1为合数,0为质数);
- 从2开始遍历,每当找到一个值为0的坐标就把它的所有倍数标记为1,并重复此操作,直到完成筛选。
代码实现如下:
#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 2e6 + 5;
bitset<N>vis;
//用来标记质数,1代表该数字为合数,0代表该数字为质数
void solve()
{
int n;cin >> n;
vis[0] = 1,vis[1] = 1;
//0,1一定不是质数
for(int i = 2;i <= n;i++)
{
if(!vis[i])
{
for(int j = 2;j*i<=n;j++)
{
vis[i * j] = 1;
}
//把所有倍数标记为1
}
}
for(int i = 2;i<=n;i++) if(!vis[i])cout << i <<" ";
//输出
}
int main()
{
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
int _ = 1;
while(_--)solve();
return 0;
}
本人初学算法,能力有限,如有错误,敬请指正。

浙公网安备 33010602011771号