[数论] 质数
Posted zero_orez6
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[数论] 质数相关的知识,希望对你有一定的参考价值。
定义
在大于1的自然数中,若一个数的因数只有1和他本身,那么这个数被称为质数/素数,否则为合数。
注意,1既不是质数,也不是合数。
证明
那么如何证明一个正整数是不是质数呢(⊙o⊙)?
想必很多同学想到从2枚举到 n − 1 n-1 n−1,若其中有 n n n的因数, n n n就是合数,否则为质数
bool pd(int n)
{
for(int i=2;i<n;i++)
{
if(n%i==0) return 0;
}
return 1;
}
这样的话复杂度较高,为 O ( n ) O(n) O(n)。大家可以仔细思考一下,
若 n = x ∗ y n=x*y n=x∗y的一个因数x小于 n \\sqrt{n} n,那么 y y y 一定大于 n \\sqrt{n} n,
所以我们可以对上面的代码进行修改。
bool pd(int n)
{
for(int i=2;i<=sqrt(n);i++)
{
if(n%i==0) return 0;
}
return 1;
}
筛法求质数
埃式筛
一般情况下,可以先用较为简单的方法将 [ 1 , n ] [1,n] [1,n]的所有质数求出,在调用时即可 O ( 1 ) O(1) O(1)复杂度求出。
那么如何将 [ 1 , n ] [1,n] [1,n]的质数快捷求出?可以直接遍历 [ 1 , n ] [1,n] [1,n]判断是否为质数,复杂度为 O ( n ∗ n ) O(n*\\sqrt n) O(n∗n)。
但是再思考一下质数的性质,在后面的数中,有不少的数是之前质数的倍数,在判断出一个数是质数时,即可将它所有 < = n <=n <=n的倍数标记为非质数。
以上便是埃氏筛的思想,代码如下:
#include<bits/stdc++.h>
using namespace std;
bool f[100086];
bool pd(int x)
{
for(int i=2;i<=sqrt(x);i++)
{
if(x%i==0) return 0;
}
return 1;
}
int main()
{
memset(f,1,sizeof(f));
int n,q;
cin>>n>>q;
for(int i=2;i<=n;i++)
{
if(pd(i)==1)
{
for(int j=2*i;j<=n;j+=i)
{
f[j]=0;
}
}
}
while(q--)
{
int x;
cin>>x;
if(f[x]==0) cout<<"NO"<<endl;
else cout<<"YES"<<endl;
}
return 0;
}
线性筛
即使在优化后,埃式筛仍然会重复标记一些合数,例如对于合数 30 30 30,质数 2 , 3 , 5 2,3,5 2,3,5都会重复标记,所以我们应找出某种方法能够唯一的产生30。
线性筛因此产生,它通过“从大到小累计质因子”来标记每个合数,
设用数组v来记录每个数的最小质因子,按下列步骤来维护数组v
1.从
2
n
2~n
2 n循环每一个数
i
i
i。
2.若
v
[
i
]
=
i
v[i]=i
v[i]=i,则
i
i
i为质数,将它保存下来。
3.扫描不大于i的每个质数
p
p
p,令
v
[
i
∗
p
]
=
p
v[i*p]=p
v[i∗p]=p,因为
p
<
=
i
p<=i
p<=i,所以
p
p
p为
i
∗
p
i*p
i∗p的最小质因子。
每个合数只会被它的最小质因子筛一次,时间复杂度为
O
(
n
)
O(n)
O(n)
int v[10086],prime[10086];
void primes(int n)
{
memset(v,0,sizeof(v));
int m=0;
for(int i=2;i<=n;i++)
{
if(v[i]==0)
{
v[i]=i;
prime[++m]=i;
}
for(int j=1;j<=m;j++)
{
if(prime[j]>v[i]||prime[j]>n/i) brreak;
v[i*prime[j]]=prime[j];
}
}
for(int i=1;i<=m;i++)
{
cout<<prime[i]<<endl;
}
}
以上是关于[数论] 质数的主要内容,如果未能解决你的问题,请参考以下文章