今天给大家讲解质数筛这个算法。
在信息竞赛中,我们总是会遇到很多判断质数的题目,那么在这里就由我来给大家讲解一下质数筛算法(这里所有讲的算法都是基于筛出从 1 1 1 到 n n n 之间的素数的算法)。
最普通的筛法,也就是将前 n n n 个正整数一个一个来判断是否为素数,并且在判断素数的时候要从 2 2 2 枚举到 这个数 − 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 ( n 2 ) O(n^2) O(n2)。
学过奥数的朋友们可能会发现,在判断素数的时候,不一定需要枚举到 i − 1 i-1 i−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 ( n n ) O(n\sqrt{n}) O(nn)。
我们发现,上面两种筛法会筛到许多没有意义的数,所以我们必须换一种思想方式。
埃氏筛,就是先将 p r i m e prime prime 数组全部赋值为 1 1 1。(记得将 p r i m e i prime_i primei 赋值为 0 0 0 )。
仍然是要从 1 1 1 枚举到 n n n 。我们先假设当前枚举到了 i i i 。
如果 p r i m e i = 1 prime_i=1 primei=1 也就是 i i i 为质数,则我们可以知道 i i i 的倍数均为合数,所以我们就将 p r i m e i × k < n , k > = 2 prime_{i\times k
最终筛完之后,如果 p r i m e i = 1 prime_i=1 primei=1 , i i i 就是质数。
关键代码
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 ( n l o g n l o g n ) O(nlognlogn) O(nlognlogn)。
我们发现,埃氏筛已经很快了,但是还是有所不足。
因为在埃氏筛中,有很多数有可能被筛到很多次(例如 6 6 6 , 他就被 2 2 2 和 3 3 3 分别筛了一次)。 所以在欧拉筛中,我们就是在这个问题上面做了优化,使得所有合数只被筛了一次。
首先,我们定义 s t i st_i sti 数组表示 i i i 是否为质数, p r i m e s i primes_i primesi 储存已经找到的所有质数, c n t cnt cnt 储存当前一共找到了多少质数。
如果当前已经枚举到了 i i i 。如果 s t i = 1 st_i=1 sti=1 ,也就是 i i i 为素数。则 p r i m e s c n t + 1 = i primes_{cnt+1}=i primescnt+1=i。
然后我们每一次枚举都要做这个循环: 枚举 j j j 从 1 1 1 到 c n t cnt cnt。 s t p r i m e s j × i = 0 st_{primes_j\times i}=0 stprimesj×i=0(因为 p r i m e s j primes_j primesj 为素数, i i i 就表示这个素数的多少倍,要把他筛掉)。
注意,接下来是重点! 如果 i m o d p r i m e s j = 0 i\mod primes_j=0 imodprimesj=0,跳出第二层循环。(因为欧拉筛默认每一个合数只能由他的最小质因数筛去,而满足以上条件之后, p r i m e s j primes_j primesj 就不是这个数字的最小质因数了,所以我们跳出第二层循环)。 因此,有了这一层优化之后,每一个合数就只能被筛掉一次了。
关键代码
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)。
以上就是我要讲的质数筛算法了,希望对大家有所帮助。