素数筛(从O(N*sqrt(N))到O(N))


原始版本 O ( n n ) O(n\sqrt n) O(nn )

1.比质数p小的数都和p互质。
2.如果一个数x存在两个因子n, m,那一定满足 n < = x 且 m > = x n <=\sqrt x且m>=\sqrt x n<=x m>=x 或者 n > = x 且 m < = x n >=\sqrt x且m<=\sqrt x n>=x m<=x
那我们只要验证x是否为素数,我们就可以在 [ 2 , x ] [2,\sqrt x] [2,x ]范围中寻找就足够了。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5+8;
ll p1[N], u;
bool st[N];
int main() {
    
    
	ll x = 0;
	bool f;
	for(int i=2; i<N; i++) {
    
    
		f = true;
		for(int j=2; j<sqrt(i)+1; j++) {
    
    
			x ++;//记录循环次数
			if(i != j && i%j == 0){
    
    
				f = false;
				break;
			}
		}
		if(f) p1[u++] = i;
	}
	cout << u << ' ' << x << endl;
	//u = 9593; x = 2755713;
	return 0;
}

埃氏筛法 O ( n l o g l o g ( n ) ) O(nloglog(n)) O(nloglog(n))

埃氏筛法的主要思路:
i i i从2开始枚举,将 i i i之后的所有 i i i的倍数 ( i ∗ j < N ) (i*j<N) (ij<N)都标记。在枚举到未被标记的 i i i时,说明小于i的元素中没有 i i i的倍数,等价于 i i i就是素数。

优化版本:将要被的标记元素从 i ∗ i i*i ii开始,每次加 i i i,相当于 i ∗ ( i + j ) i*(i+j) i(i+j)
我们来列举一下。
2 ∗ 2      3 ∗ 3      4 ∗ 4      5 ∗ 5 2*2~~~~3*3~~~~4*4~~~~5*5 22    33    44    55
2 ∗ 3      3 ∗ 4      4 ∗ 5      5 ∗ 6 2*3~~~~3*4~~~~4*5~~~~5*6 23    34    45    56
2 ∗ 4      3 ∗ 5      4 ∗ 6      5 ∗ 7 2*4~~~~3*5~~~~4*6~~~~5*7 24    35    46    57
2 ∗ 5      3 ∗ 6      4 ∗ 7      5 ∗ 8 2*5~~~~3*6~~~~4*7~~~~5*8 25    36    47    58
. . . . . . .      . . . . . . .      . . . . . . .      . . . . . . . .......~~~~.......~~~~.......~~~~....... .......    .......    .......    .......
我们能够发现, i ∗ i i*i ii之前的 i ∗ ( i − j )    j > 0 i*(i-j)~~j>0 i(ij)  j>0的倍数都会被 ( i − j ) ∗ i (i-j)*i (ij)i标记,我们在这里减少了重复标记的次数。
优化中还有一个小问题:为啥 i i i只需要枚举到 N \sqrt N N 就行了i<sqrt(N)+1
因为我们在第二层循环中是从 i ∗ i i*i ii开始,到N结束,所以我们能够标记区间 [ 2 , N ] [2, N] [2,N]的全部元素

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5+8;
ll p2[N], p3[N], p4[N], u;
bool st[N];
int main() {
    
    
	ll x = 0;
	for(int i=2; i<N; i++) {
    
    
		if(!st[i]){
    
    
			p2[u++] = i;
			for(int j=2; j*i<N; j++) {
    
    
				st[j*i] = true;
				x++;
			}
		}  	
	}
	cout << u << ' ' << x << endl;
	//u = 9593; x = 256826;

	//优化
	memset(st, 0, sizeof st);
	u = x = 0;
	for(int i=2; i<sqrt(N)+1; i++) {
    
    
		if(!st[i]){
    
    
			for(int j=i*i; j<N; j+=i) {
    
    
				st[j] = true;
				x++;
			}
		} 
	}
	for(int i=2; i<N; i++) {
    
    
		if(!st[i]) p3[u++] = i;
	}
	cout << u << ' ' << x << endl;
	//u = 9593; x = 193091;
	return 0;
} 

线性筛 O ( n ) O(n) O(n)

这条语句是减少复杂度的关键if(i % p4[j] == 0) break;
我们来列举一下被标记元素 i ∗ p 4 [ j ] i * p4[j] ip4[j]变化过程:

2 3 4 5 6 7 8 9 10
2*2 3*2 4*2 5*2 6*2 7*2 8*2 9*2 10*2
3*3 5*3 7*3 9*3
5*5 7*5
7*7

加入把关键句去掉,将会出现 4 ∗ 3 , 6 ∗ 3 4*3,6*3 4363等。
但我们在后面寻找可以发现 4 ∗ 3 < = > 6 ∗ 2 , 6 ∗ 3 < = > 9 ∗ 2 4*3<=>6*2,6*3<=>9*2 43<=>6263<=>92,这样就减少了重复标记的次数。
这样就保证了每个合数只会被它的最小质因数标记,所以时间复杂度为 O ( N ) O(N) O(N)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5+8;
ll p4[N], u;
bool st[N];
int main() {
    
    
	ll x = 0;
	memset(st, 0, sizeof st);
	u = x = 0;
	for(int i=2; i<N; i++) {
    
    
		if(!st[i]) p4[u++] = i;
		for(int j=0; j<u && i*p4[j]<N; j++) {
    
    
			x++;
			st[i*p4[j]] = true;
			if(i % p4[j] == 0) break;
			//在删掉这条语句之后u = 9593, x = 193091;
			//证明这条语句是减少复杂度的关键。
		}
	}
	cout << u << ' ' << x << endl;
	//u = 9593; x = 90413;
	return 0;
} 

三合一代码。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1e5+8;
ll p1[N], p2[N], p3[N], p4[N], u;
bool st[N];
int main() {
    
    
	ll x = 0;
	bool f;
	for(int i=2; i<N; i++) {
    
    
		f = true;
		for(int j=2; j<sqrt(i)+1; j++) {
    
    
			x ++;
			if(i != j && i%j == 0){
    
    
				f = false;
				break;
			}
		}
		if(f) p1[u++] = i;
	}
	cout << u << ' ' << x << endl;
	u = x = 0;
	for(int i=2; i<N; i++) {
    
    
		if(!st[i]){
    
    
			p2[u++] = i;
			for(int j=2; j*i<N; j++) {
    
    
				st[j*i] = true;
				x++;
			}
		}  	
	}
	cout << u << ' ' << x << endl;
	memset(st, 0, sizeof st);
	u = x = 0;
	for(int i=2; i<sqrt(N)+1; i++) {
    
    
		if(!st[i]){
    
    
			for(int j=i*i; j<N; j+=i) {
    
    
				st[j] = true;
				x++;
			}
		} 
	}
	for(int i=2; i<N; i++) {
    
    
		if(!st[i]) p3[u++] = i;
	}
	cout << u << ' ' << x << endl;
	memset(st, 0, sizeof st);
	u = x = 0;
	for(int i=2; i<N; i++) {
    
    
		if(!st[i]) p4[u++] = i;
		for(int j=0; j<u && i*p4[j]<N; j++) {
    
    
			x++;
			st[i*p4[j]] = true;
			if(i % p4[j] == 0) break;
		}
	}
	cout << u << ' ' << x << endl;
	return 0;
} 

猜你喜欢

转载自blog.csdn.net/weixin_45363113/article/details/106786695