【数论】两种特殊的质数筛法
数论——两种特殊的质数筛法(up2021729)
1. 埃氏筛
\[O(n loglogn )
\]
- 质数的倍数都是合数。借助这个性质,我们可以先找到一个质数,并利用这个质数,将范围内所有非质数(合数)的给先打上标记
- 缺点:6会被2和3给打上两次标记,会造成计算的浪费
bool nprime[N];//0为否定,为质数 ,1为肯定,为非质数
int prime[N],cntprime=0;
int main()
{
int n;
cin>>n;
for(int i=2;i<=n;i++)
{
if(!nprime[i])//质数为0,加上!来使得条件为true
{
prime[cntprime++]=i;
for(int j=2*i;j<=n;j=j+i)
nprime[j]=1;
}
}
}
2.欧拉筛
\[𝑂(𝑛)
\]
-
算法思想:限定每个数只会被其最小质因数筛到,而如果操作过程中出现比其大的数,那么直接退出了
-
算法分析:
m_prime数组用来记录这些数的最小质因数。
质数的最小质因数为本身,而这个质数的倍数的最小质因数为这个质数。
-
若
m_prime[某个数]==初始化的值
,这个数为质数。 -
当
m_prime[某个数]!=某个数本身
时,这个数不是质数。
(有点类似
BFS
)将存放质数的数组中各个质数乘上i(循环次数/当前轮次的放大倍数)-
循环结束条件:
-
当前质数*i>给定范围,质数如果再变大也必然会超过给定范围,所以是需要终止的。
-
当前质数大于m_prime[i]
- prime[j]>m_prime[i], m_prime[i]是i的最小质因数,而这里是尝试将prime[j]*i(i的倍数)的最小质因数赋值成prime[j] (一个大于m_prime[i]的数),所以果断跳出。
-
-
欧拉筛模板
#include<bits/stdc++.h>
using namespace std;
const int N = 数据筛查范围上界;
int main()
{
int 每个数得最小质因子,质数数组,用来统计质数个数得变量;
int n;
cin>>n;
for(int i=2;i<=n;i++)
{
if(!若当前这个数的最小质因子仍未被赋值) 则说明这个数是质数(不是其他质数的倍数)
将这个数的最小质因子记录一下,并将新的质数统计进来
接下来以这个数为基础去不断确认合数(通过这个数乘上一些比自己最小质因子还小的质数,来确定新生成的数是合数,并对他们的最大质因子作记录)
for(int j=0;j<已有的质数个数;j++)
{
if(超过了已有的数据范围,就可以歇息一下了||当前遍历)
break;
else
确认合数;
}
}
cout<<cntprime;
return 0;
}
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int main()
{
int m_prime[N],prime[N],cntprime=0;
int n;
cin>>n;
for(int i=2;i<=n;i++)
{
if(!m_prime[i])
m_prime[i]=prime[cntprime++]=i;
for(int j=0;j<cntprime;j++)
{
if(prime[j]*i>n||prime[j]>m_prime[i])
break;
else
m_prime[i*prime[j]]=prime[j];
}
}
cout<<cntprime;
return 0;
}
题目集
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+10;
int main()
{
int m_prime[N],prime[N],cntprime=0;
//O(n) 记录了每个数的最小质因子,和范围内所有的质数
int n;
cin>>n;
for(int i=2;i<=n;i++)
{
if(!m_prime[i])
m_prime[i] = prime[cntprime++] = i;
for(int j=0;j>cntprime;j++)
{
if(prime[j]*i>n||prime[j]>m_prime[i])
//到已经存好的质数里去找,一旦prime[j]大于i的最小质因子
//就没有必要再将i的倍数的最小质数打上prime[j]
break;
else
m_prime[i*prime[j]]=prime[j];
}
}
cout<<cntprime;
return 0;
}