欧拉筛和区间筛
Posted bianjunting
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了欧拉筛和区间筛相关的知识,希望对你有一定的参考价值。
首先介绍一下线性筛,之所以称之为线性筛是因为它的复杂度为O(n)。
与埃氏筛相比,欧拉筛不会对已经被标记过的合数再进行重复标记,它们保证每个合数只会被它的最小质因数筛去故效率更高。欧拉筛将合数分解为 (最小质因数 * 一个合数) 的形式,通过最小质因数来判断当前合数是否已经被标记过。
1 #include <cstdio> 2 #include <cstring> 3 using namespace std; 4 5 const int maxn = 10000, n = 2000; 6 bool isvisited[maxn]; 7 int prime[n]; 8 int cnt; 9 10 int main () { 11 for(int i = 2; i < maxn; i ++) { 12 if(!isvisited[i]) 13 prime[cnt ++] = i; 14 for(int j = 0; j < cnt; j ++) { 15 if(i * prime[j] > maxn) break; 16 isvisited[i * prime[j]] = true;//每个合数都被他的最小质因数标记 17 if(i % prime[j] == 0) break; //当发现某个合数存在更小的质因数时就停止标记 18 } 19 } 20 for(int i = 0; i < cnt; i ++) 21 printf("%d ", prime[i]); 22 return 0; 23 }
接着再介绍一下素数的区间筛法。
给定整数a和b,请问区间[a,b)内有多少个素数? a<b<=10^12,b-a<=10^6。因为b以内合数的最小质因数一定不超过sqrt(b),如果有sqrt(b)以内的素数表的话,就可以把线性筛用在[a,b)上了,
先分别做好[2,sqrt(b))的表和[a,b)的表,然后从[2,sqrt(b))的表中筛得素数的同时,也将其倍数从[a,b)的表中划去,最后剩下的就是区间[a,b)内的素数了。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int maxn = 1e6 + 5; 7 bool is_prime[maxn], is_prime_small[maxn]; 8 ll prime[maxn]; 9 ll prime_num = 0; 10 11 //对区间[a, b)的整数执行筛法,is_prime[i - a] = true <=> i 是素数 12 void segment_sieve(ll a, ll b) { 13 for(int i = 0; (ll)i * i < b; i ++) is_prime_small[i] = true;//对[2, sqrt(b)]进行初始化 14 for(int i = 0; i < b - a; i ++) is_prime[i] = true; 15 for(int i = 2; (ll)i * i < b; i ++) { 16 if(is_prime_small[i]) { 17 for(ll j = 2 * i; (ll)j * j < b; j += i) is_prime_small[j] = false;//筛[2, sqrt(b)) 18 for(ll j = max(2LL, (a + i - 1) / i) * i; j < b; j += i) is_prime[j - a] = false;//筛[a,b) 19 } 20 } 21 for(ll i = 0; i < b - a; i ++) 22 if(is_prime[i]) prime[prime_num ++] = i + a; 23 } 24 25 int main () { 26 ll a, b; 27 while(~scanf("%d %d", &a, &b)) { 28 prime_num = 0; 29 memset(prime, 0, sizeof(prime)); 30 segment_sieve(a, b); 31 printf("%lld ", prime_num); 32 } 33 return 0; 34 }
以上是关于欧拉筛和区间筛的主要内容,如果未能解决你的问题,请参考以下文章