质数

定义:在大于一的整数中,如果只包含1和它本身两个约数,则被称为质数,也叫素数

质数的判定——试除法

若$n$小于2,则不是质数

从2开始,一直判定到$n-1$,若均无法整除,则为质数

1
2
3
4
5
6
7
bool isprime(int x){
if(x<2)return 0;
for(int i=2;i<x;i++){
if(x%i==0)return 0;
}
return 1;
}

优化:可以发现,约数是成对出现的,所以只用循环到$\sqrt{n}$即可

1
2
3
4
5
6
7
8
bool isprime(int x){
if(x<2)return 0;
int t=sqrt(x);
for(int i=2;i<=t;i++){
if(x%i==0)return 0;
}
return 1;
}

分解质因数——试除法

从小到大枚举$n$的约数,若整除,则为约数,此时求其次数

1
2
3
4
5
6
7
8
9
10
11
12
void divide(int x){
for(int i=2;i<=x;i++){
if(x%i==0){
int s=0;
while(x%i==0){
x/=i;
s++;
}
cout << i << ' ' << s << endl;
}
}
}

考虑优化:

由于$n$中最多只包含一个大于$\sqrt{n}$的质因子,所以只需枚举到$n/i$,最后会剩余一个数,若这个数大于$n$,则此数为大于$\sqrt{n}$d的那个素因子,单独判断即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void divide(int x){
for(int i=2;i<=x/i;i++){
if(x%i==0){
int s=0;
while(x%i==0){
x/=i;
s++;
}
cout << i << ' ' << s << endl;
}
}
if(x>1){
cout << x << ' ' << 1 << endl;
}
}

质数筛法

朴素筛法

每次循环每个数的倍数筛去

1
2
3
4
5
6
7
8
9
10
bool st[N];
int prime[N],cnt;
void getprime(int n){
for(int i=2;i<=n;i++){
if(!st[i])prime[++cnt]=i;
for(int j=i*2;j<=n;j+=i){
st[j]=1;
}
}
}

复杂度$O(\log n)$

埃氏筛

每次筛时只筛素数的倍数

1
2
3
4
5
6
7
8
9
10
11
12
bool st[N];
int prime[N],cnt;
void getprime(int n){
for(int i=2;i<=n;i++){
if(!st[i]){
prime[++cnt]=i;
for(int j=i*2;j<=n;j+=i){
st[j]=1;
}
}
}
}

复杂度$O(n\log \log n)$

线性筛

每个数只会被其最小质因子筛掉

循环,每次筛小于$n/i$的质数的$i$倍直到$i%prime_j=0$

每次筛数时:

  1. 若$i%prime_j=0$,则$prime_j$一定是$i$的最小质因子,$prime_j$也一定是$prime_j*i$的最小质因子
  2. 若$i%prime_j\not = 0$,,则$prime_j$一定小于$i$的所有质因子,$prime_j$也一定是$prime_j*i$的最小质因子

对于一个合数$x$,假设$prime_j$是$x$的最小质因子,当$i$枚举到$x/prime_j$时,一定会被筛掉

1
2
3
4
5
6
7
8
9
10
11
bool st[N];
int prime[N],cnt;
void getprime(int n){
for(int i=2;i<=n;i++){
if(!st[i])prime[++cnt]=i;
for(int j=1;prime[j]<=n/i;j++){
st[prime[j]*i]=1;
if(i%prime[j]==0)break;
}
}
}

复杂度$O(n)$