线性筛法(C++)

我终于终于终于........来了。

我们今天直接上干货------干筛法。

我们首先考虑这样一个事,一个数如果是质数,那他的倍数一定是合数,我们根据这一结论筛掉不必要的判断。

int primes(int n)
{
	st[1] = true;
	for(int i=2;i<=n;i++)
	{
		if(!st[i])prime[++k] = i; //如果这个数没有筛过,就把它放入数组
		
		for(int j=1;prime[j]<=n/i;j++)//我们遍历素数数组,将全部素数的i倍进行标记
		{
			st[prime[j] * i] = true;
		}
	}
	return k; 
}

这样的话,时间复杂度是O( n * log log n).。

没错这个就是埃及人的筛法,简称埃式筛法。

然后我们会发现一个问题,同一个合数可能会被两个质数筛,举个栗子:

当 i = 4的时候,数组中元素 [ 2,3 ]  3会把12筛掉;

当 i = 6的时候,数组中元素[ 2, 3, 5] 这时候 2 又会发挥作用,又筛了一遍 12 ;

那我们有没有办法,只筛一次呢,线性筛正式登场。

先上代码:

int primes(int n)
{
	st[1] = true;
	for(int i=2;i<=n;i++)
	{
		if(!st[i])prime[++k] = i;
		
		for(int j=1;prime[j]<=n/i;j++)
		{
			st[prime[j] * i] = true;
			if(i % prime[j] == 0)break;
		}
	}
	return k; 
}

这时候没有仔细看的小朋友就说了,这俩代码有区别吗。

没错有区别,而且区别很大,这个代码的时间复杂度是O( n );

区别就在 if(i % prime[j] == 0)break; 这样我们就可以保证我们的代码,对于同一个合数,只会被他最小的质因子筛掉,尽管看着是两重循环,但是时间上代码也只是遍历了一遍。

重点来了!!!

当i是prime[j]的整数倍时,记 m = i / prime[j],那么 i * prime[j+1] 
就可以变为 (m * prime[j+1]) * prime[j],这说明 i * prime[j+1] 是 prime[j] 的整数倍
不需要再进行标记(在之后会被 prime[j] * 某个数 标记)
对于 prime[j+2] 及之后的素数同理,直接跳出循环,这样就避免了重复标记。

还是刚才的例子;

当 i= 4 的时候;i % 2 == 0 ;跳出循环,这样就防止了 12 被 3 标记。

当 i= 6 的时候  12 就乖乖的被 2 标记了,要是还是不明白,可以自己模拟一下。

我们来说一下为什么循环判断中,没有j
prime数组中存有<=i的所有质数
当i是合数时, pj为i的最小质因子时break, j不会越界;
当i是质数时, primes数组的最后一个元素就是i, 当pj==i时, i%pj==0; break, j也不会越界;

洛谷题:【模板】线性筛素数 - 洛谷

  

 

你可能感兴趣的:(数论,算法,数据结构)