素数(质数):质数是指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数
初学者的素数判定
- 判断一个数是否是素数
- 时间复杂度:O( sqrt(n) )
bool is_prime(int n)
{
if(n==1) return false; //1不是素数,2是素数
for(int i=2;i*i<=n;i++) //判断 2~sqrt(n)是否有n的因数
{
if(n%i==0)
return false;
}
return true;
}
素数筛
- 判定1~n中有哪些是素数
1.埃筛
(1)最暴力的筛法:从2开始一直筛到n,每次筛掉i的倍数
-
因为对于i: 2~n ,i 的倍数一定不是素数
例如3是素数,但3的倍数 6,9,12…3n,一定不是素数
-
时间复杂度: O(nlog n)
const int N=1000;
int isp[N]; //1代表不是素数,0代表是
void is_prime(int n)
{
isp[1]=1; //1不是素数
for(int i=2;i<=n;i++)
{
for(int j=i*2;j<=n;j+=i)
isp[j]=1;
}
}
(2)埃筛的改进:
- 已被筛过的数不需要再筛
- 内层循环从i*i开始
- 时间复杂度 O(nlog log n)
const int N=1000;
int isp[N]; //1代表不是素数,0代表是
void is_prime(int n)
{
isp[1]=1; //1不是素数
for(int i=2;i<=n;i++)
{
if( isp[j]) continue;
for(int j=i*i;j<=n;j+=i)
isp[j]=1;
}
}
埃筛的缺陷:用埃筛,有些数被筛了很多次
例如 30=2*15,在2的时候被筛掉,30=5 *6,在5的时候又被筛掉。
那么如何确保每个合数只被筛选一次呢?我们只要用它的最小质因子来筛选即可,这便是欧拉筛法。
2.欧拉筛(线性筛)
- 欧拉筛法的基本思想 :在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。每个数只筛一次。
- 时间复杂度:O(n)
const int N=1000;
int isp[N],pri[N],cnt=0;
//isp:判断是否是素数, pri:记录筛出来的质数, cnt:计数
void is_prime(int n)
{
isp[1]=1; //1不是素数
for(int i=2;i<=n;i++)
{
if(!isp[i]) pri[cnt++]=i; //记录素数
for(int j=0;j<cnt;j++)
{
if(i*pri[j]>n) break;
isp[i*pri[j]] =1; //pri[j]相当于最小质因子,筛掉以pri[j]为最小质因子的数
if(i%pri[j]==0) break;
}
}
}
(此处运用某位大佬的解释)
对于 i%pre[j] == 0 就break的解释 :当 i是pri[j]的倍数时,i = k*pri[j].
如果继续运算 j+1,i * pri[j+1] = k * pri[j] * pri[j+1],这里pri[j]是最小的素因子,当i = k * prime[j+1]时会重复,所以才跳出循环。
举个例子 :i = 8 ,j = 1,prime[j] = 2,如果不跳出循环,prime[j+1] = 3,
8 * 3 = 2 * 4 * 3 = 2 * 12,在i = 12时会计算。因为欧拉筛法的原理便是通过最小素因子来消除。
程序理解如图:
推荐博客:
link 埃筛与欧拉筛
推荐题目:
link 洛谷欧拉筛模板题