欧拉筛和区间筛

  

首先介绍一下线性筛,之所以称之为线性筛是因为它的复杂度为O(n)。

  与埃氏筛相比,欧拉筛不会对已经被标记过的合数再进行重复标记,它们保证每个合数只会被它的最小质因数筛去故效率更高。欧拉筛将合数分解为 (最小质因数 * 一个合数) 的形式,通过最小质因数来判断当前合数是否已经被标记过。

  2020-03-03-17:56:55

  我现在才知道循环最后为什么 i % prime[j] == 0 时要break;

    因为此时,如果i % prime[j] == 0, i = k * prime[j] i * prime[j + 1] = k * prime[j] * prime[j + 1],k肯定大于等于二,也就是说k * prime[j] > prime[j + 1],也就是说prime[j] 不是i * prime[j]的最小质因子,反而是prime[j + 1]。

 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 }

  

原文地址:https://www.cnblogs.com/bianjunting/p/10478755.html