自己来筛一筛质数(埃氏筛 & 线性(欧拉)筛)

埃氏筛

简单,暴力。

int isprime[50000];
void getlist(int size) {
	memset(isprime, 1, sizeof(isprime));
	
	isprime[1] = 0;
	for(int i = 2; i <= size; i++) if(isprime[i]) 
	for(int j = 2; i*j <= size; j++) isprime[i*j] = 0; 
	
	return;
}

看似简单,但不真搞明白这个,无法学会线性 ( 欧拉 ) 筛。

关键在于如何去做的合数,用了两个参数,一个是质数,即 prime[ i ] ,另一个是质数的倍数,即 j 。相乘得到合数。

线性筛

埃氏筛的改良版,使一个合数只被做出来一次。

类比dfs的一些判重,用一定的顺序( 如从小到大 )来去重。在这里,我们便是保证每个合数只被最小的质因数做出来。

在线性筛里,我们并不是先找质数,再用不同的倍数去乘它。我们遍历倍数,用不同的质数去乘它。当某个质数是这个倍数的因数时,换下一个倍数。

为什么呢?

引用一段标准解释:

prime[] 数组中的素数是递增的,当i能整除 prime[ j ],那么 i*prime[ j+1 ]这个合数肯定被 prime[ j ] 乘以某个数筛掉。

因为i中含有 prime[ j ] , prime[ j ] 比 prime[ j+1 ]小,即 i = k*prime[ j ]。

那么 i * prime[ j+1 ] = (k*prime[ j ]) * prime[ j+1 ] = k’ * prime[ j ],接下去的素数同理。所以不用筛下去了。

因此,在满足 i%prime[ j ] == 0这个条件之前以及第一次满足改条件时, prime[ j ] 必定是 prime[ j ] * i 的最小因子。

请看下面一个样例,假设我们的倍数遍历到了9,而去乘它的质数到了3。

遍历的倍数     9

质数表 2  3  5  7

这时候我们就可以换下一个倍数10了,为什么呢?

假如我们继续,用9*5得到45,请注意,因为3是9的因数,所以3也是45的因数,且必定是45的最小质因数。因为3是9的最小质因数,除非9乘的质因数小于3,否则45的最小质因数就是3。5在3的右边,所以winner就是3了。那么15就是造这个合数所需要的倍数,肯定大于9,会在后面遍历到这个倍数。

所以在3*9后,我们可以换下一个倍数10了。

我将 i ,j 对换,来匹配埃氏筛的意义。 

int isprime[50000], prime[10000], tot;
void getlist(int size) {
	memset(isprime, 1, sizeof(isprime));
	isprime[1] = 0;
	for(int j = 2; j <= size; j++) {
		if(isprime[j]) prime[++tot] = j;
		for(int i = 1; i <= tot && j*prime[i] <= size; i++) {
			isprime[j*prime[i]] = 0;
			if(j%prime[i] == 0) break;
		}
	}
	return;
}

还是得多去复习。

猜你喜欢

转载自blog.csdn.net/DWAE86/article/details/81220385