[质数筛] 质数筛算法详解

今天给大家讲解质数筛这个算法。

在信息竞赛中,我们总是会遇到很多判断质数的题目,那么在这里就由我来给大家讲解一下质数筛算法(这里所有讲的算法都是基于筛出从 1 1 1 n n n 之间的素数的算法)。

1.普通筛法

最普通的筛法,也就是将前 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)

2.普通筛法的优化

学过奥数的朋友们可能会发现,在判断素数的时候,不一定需要枚举到 i − 1 i-1 i1 只需要枚举到 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 )

3.埃氏筛

我们发现,上面两种筛法会筛到许多没有意义的数,所以我们必须换一种思想方式。

埃氏筛,就是先将 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=2} primei×k<n,k>=2 赋值为 0 0 0

最终筛完之后,如果 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)

4.欧拉筛(线性筛)

我们发现,埃氏筛已经很快了,但是还是有所不足。

因为在埃氏筛中,有很多数有可能被筛到很多次(例如 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)

以上就是我要讲的质数筛算法了,希望对大家有所帮助。

你可能感兴趣的:(算法总结,算法)