质数、质因数
1.判定质数
暴力做法,判断一个数:O(n)
优化暴力做法:只判断一个数的1~根号n O(sqrt(n))
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100005;
bool is_prime(int x)
{
//1不是质数,也不是合数
if(x==1) return false;
//从2开始,1不应该加入
for (int i = 2;i <= x / i;i++)
{//i<=n/i 可以防止数据溢出
if (x % i == 0) return false;
}
return true;
}
int main()
{ int q;
cin >> q;
while(q--)
{
int n;
cin>>n;
if (is_prime(n))
{
cout << "Yes"<<'\n';
}
else cout << "No"<<'\n';
}
}
2、分解质因数
基本信息:
质因数(素因数或质因子)在数论里是指能整除给定正整数的质数
概况:算术基本定理:“每一个大于1的整数都能分解成质因数乘积的形式,并且如果把质因数按照由小到大的顺序排列在一起,相同的因数的积写成幂的形式,那么这种分解方法是唯一的。”——又称为“质因数分解定理”
这里有个性质:n中最多只含有一个大于sqrt(n)的因子。证明通过反证法:如果有两个大于sqrt(n)的因子,那么相乘会大于n,矛盾。证毕
于是我们发现最多只有一个大于sqrt(n)的因子,对其进行优化。先考虑比sqrt(n)小的,代码和质数的判定类似
最后如果n还是>1,说明这就是大于sqrt(n)的唯一质因子,输出即可。
证明
1.i为什么一定是质数
首先i是一个最小因子,假设i不是质数,是一个合数,合数必然存在除了1外的其他最小因子,这样子i就不是一个最小因子了,矛盾,证明结束
2.for循环结束,为什么x是一个最大的质数
设for循环结束的x为t
反证法
当t>1时,for循环如果有一个i是大于t的,那么在遇到这个i之前,x会整除t^s,for循环结束后的x就不会是t了,矛盾。证明结束
#include <iostream>
#include <algorithm>
using namespace std;
void divide(int x)
{
for (int i = 2;i <= x / i;i++)
{
if (x % i == 0) //此时i一定是一个质数
{
int s = 0; //表示底数的次方(指数)
while (x % i == 0)
{//i可以被整除,指数+1
x /= i;
s++;
}
cout << i << ' ' << s << ' ' << endl;
//重新循环,x--->x/(i^s)
}
}
//此时还>1就说明他是最大的质因数
//因为小的质因数已经被上一步给整除掉了
if (x > 1)
{
cout << x << ' ' << 1 << endl;
}
cout << endl;
}
int main()
{
int n;
cin >> n;
while (n--)
{
int x;
cin >> x;
divide(x);
}
return 0;
}
3.筛质数
埃氏筛质数法 O(n loglog n)
发现一个质数,就去把 这个质数 作为因子 的所有合数 都标记
void get_primes()
{//找出1~n的所有质数,所以不从1开始,直接从2开始
for (int i = 2;i <= n;i++)
{
if (st[i]) //i是合数
continue;
primes[cnt++] = i; //i是质数,加入队列
//这里的j=i*i时间会更快,但是会爆int
for (int j = i + i;j <= n;j += i)
st[j] = true;
}
}
线性筛法(欧拉筛法) O(n)
埃氏筛法的缺陷 :对于一个合数,有可能被筛多次。例如 30 = 2 * 15 = 3 * 10 = 5*6……那么如何确保每个合数只被筛选一次呢?我们只要用它的最小质因子来筛选即可,这便是欧拉筛法。
欧拉筛法:在埃氏筛选法的基础上,只让一个数的最小质因子去染色他,避免重复
两种情况
1.i%primes[j]==0 说明pj是i的最小质因子,同时也是i*pj的最小质因子(染色)
- 由于我们要确保一个合数只会被一个最小质因子染色,所以如果i%pj==0
说明下一个比pj大的质数必然不是最小质因子,没有意义应该break
(下一个pj’*i仍然是以i为约数,可知i能被当前pj染色,下一个pj’不是pj‘i的最小质因子,pj是)
2.i%primes[j]!=0 说明pj不是i的最小质因子,应该继续往后找到i的最小质因子
但是pj是ipj的最小质因子,所以也要染色(因为i从2开始,这样子确保了所有以pj为最小质因子的数都会被染色)
问题
1.为什么某个较大质数,例如5,不用让他去染色 52,53…呢
我们发现,如果一个数t再去染色 tj (j=0…t-1)的数,那么t必然不是最小质因子,一个数以t为最小质因子的话,t对应的约数一定≥t ,所以只有一个数被放入质数队列中,当前的i>=t,我们才需要去染色 it
例如 25=55 49=77…
还有2^n次方 3^n 都会被 i 遍历到
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e6 + 6;
int n;
int cnt; //记录当前有几个质数
bool st[N]; //记录是否是质数
int primes[N]; //记录质数
void get_primes()
{//找出1~n的所有质数,所以不从1开始,直接从2开始
for (int i = 2;i <= n;i++)
{
if (!st[i]) //是质数,加入
primes[cnt++] =i;
//这里和埃氏筛法不同的地方在于,即使是一个合数,也会被遍历
//这样子确保了2*2,2*3,2*4...都会被筛选到
for (int j = 0;primes[j] <= n / i;j++) 、
//变形得到primes[j]*i<=n 不去筛选大于n的数
{
st[i * primes[j]] = true;
if (i % primes[j] == 0) //pj是i的最小质因子
break;
}
}
}
int main()
{
cin >> n;
get_primes();
cout << cnt;
}
之前还看到一种也很快的
#include <iostream>
#include <cmath> //要用平方根函数
using namespace std;
#define ll long long //处理较大的数
bool panduan(ll a) {
if(a==1)
return false; //1既不是质数也不是合数
if(a==2||a==3) //5以下得数特判,2,3,都是质数
return true;
if(a%6!=5&&a%6!=1) //5以上的数,判断是不是在6周围,如果不是,肯定是合数
return false;
for(int i=5;i<=sqrt(a);i+=6){ //剩下的6周围的数进行判断能不能整除
if(a%i==0||a%(i+2)==0)
return false;
}
return true;
}
这里附一道题,是关于质因数的。2022ccpc高职组
J 密码
爱丽丝发现了一个宝箱,但是打开宝箱需要破解若干组密码。
每组密码都是一对数字(p,q),同时宝箱会给出两个数字A,B作为提示,爱丽丝需要找到所有满足以下条件的(p,q)。
- p,q均为质数。
- A×p+B×q 是p×q的倍数。
由于满足条件的解有很多,你只需要算出有多少对满足条件的(p,q)。
输入格式:
第一行输入1个整数T(1≤T≤1000)表示询问个数。
接下来T行,每行输入两个整数A,B(1≤A,B≤1012)表示一个询问。
输出格式:
对于每个询问输出一行一个整数,表示答案。
输入样例:
3
2 3
3 7
10 8
输出样例:
2
3
3
A=2,B=3时,(p,q)可能的两个解是(3,2)和(5,5).
A的质因数,B的质因数的组合,加上A+B的质因数的组合种类。
#include <bits/stdc++.h>
#define x first
#define y second
#define ll long long
#define PII pair<int, int>
using namespace std;
bool vis[10000010];
int pri[10000010];
int idx = 0;
void solve() {
ll a, b, c;
cin >> a >> b;
c = a + b;
vector<int> st;
for (int i = 0; pri[i] <= c / pri[i]; i++) {//st存储a+b的质因数
if (c % pri[i] == 0) {
st.push_back(pri[i]);
}
while (c % pri[i] == 0) {
c /= pri[i];
}
}
if (c > 1) {
if (c >= 1000000000)
st.push_back(-1);
else
st.push_back(c);
}
set<int> aa;
for (int i = 0; pri[i] <= a / pri[i]; i++) {//aa存储a的质因数
if (a % pri[i] == 0) {
aa.insert(pri[i]);
}
while (a % pri[i] == 0) {
a /= pri[i];
}
}
if (a > 1) {
if (c >= 1000000000) {
if (a == c)
aa.insert(-1);
else
aa.insert(-2);
} else
aa.insert(a);
}
set<int> bb;
for (int i = 0; pri[i] <= b / pri[i]; i++) {
if (b % pri[i] == 0) {
bb.insert(pri[i]);
}
while (b % pri[i] == 0) {
b /= pri[i];
}
}
if (b > 1) {
if (c >= 1000000000) {
if (b == c)
bb.insert(-1);
else if (a == b)
bb.insert(-2);
else
bb.insert(-3);
} else
bb.insert(b);
}
ll ans = aa.size() * bb.size() + st.size();
for (int i = 0; i < st.size(); i++) {
if (aa.count(st[i]) && bb.count(st[i]))
ans--;
}
cout << ans << "\n";
}
int main() {
set<PII > st;
for (int i = 2; i <= 10000000; i++) {//欧拉筛,筛1~10000000内的质数
if (!vis[i])//是质数加入
pri[idx++] = i;
for (int j = 0; pri[j] <= 10000000 / i; j++) {
//变形得到primes[j]*i<=n 不去筛选大于n的数
vis[i * pri[j]] = true;
if (i % pri[j] == 0)pj是i的最小质因子
break;
}
}
int t = 1;
cin >> t;
while (t--) {
solve();
}
return 0;
}
引用原文链接:https://blog.csdn.net/qq12323qweeqwe/article/details/121982783