今天给大家讲解质数筛这个算法。
在信息竞赛中,我们总是会遇到很多判断质数的题目,那么在这里就由我来给大家讲解一下质数筛算法(这里所有讲的算法都是基于筛出从 111 到 nnn 之间的素数的算法)。
1.普通筛法
最普通的筛法,也就是将前 nnn 个正整数一个一个来判断是否为素数,并且在判断素数的时候要从 222 枚举到 这个数−1-1−1 来判断。
关键代码
for(int i=1;i<=n;++i)//枚举1到n
{
bool flg=0;
for(int j=2;j<i;++j)//枚举2到i
{
if(i%j==0)//如果i%j=0,也就是i已经不为素数了
{
flg=1;//打上标记
break;//跳出循环,不用再枚举了
}
}
if(flg==0)//如果没有被打上标记
prime[i]=1;//prime来标记这个数是否为素数。
}
这样的时间复杂度为近似 O(n2)O(n^2)O(n2)。
2.普通筛法的优化
学过奥数的朋友们可能会发现,在判断素数的时候,不一定需要枚举到 i−1i-1i−1 只需要枚举到 i\sqrt{i}i 就可以判断出来了。
关键代码
for(int i=1;i<=n;++i)//枚举1到n
{
bool flg=0;
for(int j=2;j*j<=i;++j)//枚举2到i
{
if(i%j==0)//如果i%j=0,也就是i已经不为素数了
{
flg=1;//打上标记
break;//跳出循环,不用再枚举了
}
}
if(flg==0)//如果没有被打上标记
prime[i]=1;//prime来标记这个数是否为素数。
}
这样的时间复杂度为近似 O(nn)O(n\sqrt{n})O(nn)。
3.埃氏筛
我们发现,上面两种筛法会筛到许多没有意义的数,所以我们必须换一种思想方式。
埃氏筛,就是先将 primeprimeprime 数组全部赋值为 111。(记得将 primeiprime_iprimei 赋值为 000 )。
仍然是要从 111 枚举到 nnn 。我们先假设当前枚举到了 iii 。
如果 primei=1prime_i=1primei=1 也就是 iii 为质数,则我们可以知道 iii 的倍数均为合数,所以我们就将 primei×k<n,k>=2prime_{i\times k<n ,k>=2}primei×k<n,k>=2 赋值为 000 。
最终筛完之后,如果 primei=1prime_i=1primei=1 , iii 就是质数。
关键代码
memset(prime,1,sizeof(prime));
priem[1]=0;
for(int i=1;i<=n;++i)
{
if(prime[i])
{
for(int j=2;j*i<=n;++j)
prime[i*j]=0;
}
}
这样的时间复杂度为 O(nlognlogn)O(nlognlogn)O(nlognlogn)。
4.欧拉筛(线性筛)
我们发现,埃氏筛已经很快了,但是还是有所不足。
因为在埃氏筛中,有很多数有可能被筛到很多次(例如 666 , 他就被 222 和 333 分别筛了一次)。 所以在欧拉筛中,我们就是在这个问题上面做了优化,使得所有合数只被筛了一次。
首先,我们定义 stist_isti 数组表示 iii 是否为质数,primesiprimes_iprimesi 储存已经找到的所有质数,cntcntcnt 储存当前一共找到了多少质数。
如果当前已经枚举到了 iii 。如果 sti=1st_i=1sti=1 ,也就是 iii 为素数。则 primescnt+1=iprimes_{cnt+1}=iprimescnt+1=i。
然后我们每一次枚举都要做这个循环: 枚举 jjj 从 111 到 cntcntcnt。stprimesj×i=0st_{primes_j\times i}=0stprimesj×i=0(因为 primesjprimes_jprimesj 为素数,iii 就表示这个素数的多少倍,要把他筛掉)。
注意,接下来是重点! 如果 imod primesj=0i\mod primes_j=0imodprimesj=0,跳出第二层循环。(因为欧拉筛默认每一个合数只能由他的最小质因数筛去,而满足以上条件之后,primesjprimes_jprimesj 就不是这个数字的最小质因数了,所以我们跳出第二层循环)。 因此,有了这一层优化之后,每一个合数就只能被筛掉一次了。
关键代码
memset(st,0,sizeof(st));
st[1]=0;
for(i=2;i<=n;i++)
{
if(st[i])
primes[cnt++]=i;
for(j=0;primes[j]*i<=n&&j<=cnt;j++)
{
st[primes[j]*i]=0;
if(i%primes[j]==0)
break;
}
}
这样的时间复杂度为 O(n)O(n)O(n)。
以上就是我要讲的质数筛算法了,希望对大家有所帮助。