区间筛

区间筛:类似于埃式筛法

【分析】b以内的合数的最小质因数一定不超过sqrt(b)。如果有sqrt(b)以内的素数表的话,就可以把埃式筛法运用在[a,b)上了。也就是说,先分别做好[2,sqrt(b))的表和[a,b)的表,然后从[2,sqrt(b))的表中筛得素数的同时,也将其倍数从[a,b)的表中划去,最后剩下的就是区间[a,b)内的素数了。

给定整数 a 和 b,请问区间 [a,b) 内有多少个素数?(a<b1012,ba106)

因为 b 以内合数的最小质因数一定不会超过 b,因为如果存在 d 是 n 的约数,那么 n/d 也是 n 的约数,由 n=d×n/d 可知 min(d,n/d)n

如果有 n 以内的素数表的话,就可以把埃式筛法用在 [a,b) 上了。也就是说,先分别做好 [2,b) 的表和 [a,b) 的表,然后从 [2,b) 的表中筛得素数的同时,也将其倍数从 [a,b) 的表中筛去,最后剩下的就是区间 [a,b) 内的素数了

假如要求 [1e9,1e9+2) 区间内的素数,难道我们要开 1e9+3 大小的数组吗?其实并不用,我们利用下标偏移,可以减少空间开销。即将 [1e9,1e9+2) 对应到[0,2),整体区间向左偏移 1e9

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int MAXN = 1e5;
bool is_prime[MAXN];
bool is_prime_small[MAXN];
ll prime[MAXN];
ll prime_num = 0;

//对区间[a,b)内的整数执行筛法,is_prime[i-a]=true <=> 表示i是素数(下标偏移了a) 
void segment_sieve(ll a, ll b) {
    for (ll i = 0; i * i < b; i++) //对[2,sqrt(b))的初始化全为质数
        is_prime_small[i] = true;
    for (ll i = 0; i < b - a; i++) //对下标偏移后的[a,b)进行初始化
        is_prime[i] = true;
        
    for (ll i = 2; i * i < b; i++) { //筛选[2,sqrt(b))
        if (is_prime_small[i]) {
            for (ll j = 2 * i; j * j < b; j += i) 
                is_prime_small[j] = false;
            //(a+i-1)/i 得到最接近a的i的倍数,最低是i的2倍,然后筛选
            for (ll j = max(2LL, (a + i - 1) / i) * i; j < b; j += i)
                is_prime[j - a] = false;
        }
    }
    for (ll i = 0; i < b - a; i++) //统计个数 
        if (is_prime[i])
            prime[prime_num++] = i + a;
}

int main() {
    ll a, b;
    while (~scanf("%lld%lld", &a, &b)) {
        prime_num = 0;
        memset(prime, 0, sizeof(prime));
        segment_sieve(a, b);
        printf("%lld
", prime_num);
    }
    return 0;
}

例一:

给定整数a和b,请问区间[a,b)内有多少个素数?

a< b<=10^12

b-a<=10^6

输入
22 37
输出
3
输入
22801763489 2280178297
输出
1000

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define LL long long

#define MAX_L 1000007
#define MAX_SORT_B 1000007

bool is_prime[MAX_L];
bool is_prime_small[MAX_SORT_B];

//对区间[a,b)内的整数执行筛法。isprime[i - a]=true <=> i是素数

void segment_sieve(LL a,LL b)
{
    for(int i=0; (LL)i*i < b; i++)is_prime_small[i]=true;
    for(int i=0; i<b-a; i++)is_prime[i]=true;
    for(int i=2; (LL)i * i<b; i++)
    {
        if(is_prime_small[i])
        {
            for(int j=2*i; (LL)j * j < b; j += i)
            {
                is_prime_small[j]=false;//筛[2,sqrt(b))
            }
            for(LL j=max(2LL, (a+i-1)/i)*i ; j<b; j+=i) //(a+i-1)/i为[a,b)区间内的第一个数至少为i的多少倍.
            {
                is_prime[j - a] =false;//筛[a,b)
            }
        }
    }
}

int main()
{
    long long a,b;
    while(~scanf("%lld %lld",&a,&b))
    {
        segment_sieve(a,b);
        int cnt=0;
        for(int j=0; j<b-a; j++)
        {
            if(is_prime[j])cnt++;
        }
        if(a==1)cnt--;
        printf("%d
",cnt);
    }
    return 0;
}

 

    例二:

Prime Distance

http://poj.org/problem?id=2689

数学的一个分支叫做数论,它是关于数字的性质的。千百年来,数论家们感兴趣的领域之一就是质数问题。质数是一个没有适当因数的数(它只能被1和它本身整除)。第一个质数是2 3 5 7但它们很快就变得不那么频繁了。一个有趣的问题是它们在不同范围内的密度。相邻质数是两个都是质数的数,但是相邻质数之间没有其他质数。例如,2,3是唯一的相邻质数也是相邻数。

你的程序有两个数字:L和U (1<=L<U<=2,147,483,647),你要找到两个相邻的素数C1和C2 (L<=C1<C2<=U),它们是最接近的(即C2-C1是最小的)。如果有其他的一对距离相同,就用第一对。你还需要找出两个相邻的素数D1和D2 (L<=D1< D2<=U)其中D1和D2的距离越远越好(如果有平手,同样选择第一对)。

输入

输入的每一行都包含两个正整数,L和U,并且L < U。L和U之间的差值不会超过1,000,000。

输出

对于每一个L和U,其输出要么是不存在相邻素数(因为两个给定的数之间小于两个素数),要么是一条给出两对相邻素数的直线。

Sample Input

2 17
14 17

Sample Output

2,3 are closest, 7,11 are most distant.
There are no adjacent primes.
解析
问题就是说在一个大区间中求相邻素数的最大最小值,
#include<iostream>
#include<algorithm>
#include<map>
#include <math.h> 
#include<memory.h>
using namespace std; 
typedef long long ll; 
inline int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
const int maxn=1e6+100;
const ll mod=1e9+7;
ll prime[maxn];//储存素数 
ll biaoji[maxn]={0};
ll cnt=0;
ll dabiao[maxn];
ll prime2[maxn];//大标记素数 
void primem(){
    for(int i=2;i<maxn;i++){
        if(!biaoji[i]){
            prime[++cnt]=i;
        }
        for(int j=1;j<=cnt&&i*prime[j]<maxn;j++){
            biaoji[i*prime[j]]=1;
            if(i%prime[j]==0){
                break;
            }
        }
    }
}
ll p=0;
void qujianshai(ll l,ll r){
    p=0;
    memset(dabiao,0,sizeof(dabiao));
    for(ll i=1;i<=cnt&&prime[i]*prime[i]<=r;i++){
        for(ll j=max(2LL,(l+prime[i]-1)/prime[i])*prime[i];j<=r;j+=prime[i]){
            if(j>=l){
                dabiao[j-l]=1;
            }
        }
//        ll x=l/prime[i]+(l%prime[i]>0);
//        if(x==1) x=2;
//        for(int j=x;j*prime[i]<=r;j++){
//            if(j*prime[i]>=l){
//                dabiao[j*prime[i]-l]=1;
//            }
//        }
    }
    for(int i=0;i<=r-l;i++){
        if(dabiao[i]==0){
                if((i+l)!=1) 
                    prime2[++p]=i+l;
        }
    }
}
int main(){
    primem();
    ll l,r,n,m;
    while(~scanf("%lld%lld",&l,&r)){
        if(l==1) l=2;    
        qujianshai(l,r);
        if(p<2){
            printf("There are no adjacent primes.
");
        }
        else{
            ll ma=0,mi=20*mod;
            ll x,y,a,b;
            for(int i=1;i<p;i++){
                if(prime2[i+1]-prime2[i]>ma){
                    ma=prime2[i+1]-prime2[i];
                    x=prime2[i],y=prime2[i+1];
                }
                if(prime2[i+1]-prime2[i]<mi){
                    mi=prime2[i+1]-prime2[i];
                    a=prime2[i],b=prime2[i+1];
                }
            }
            printf("%lld,%lld are closest, %lld,%lld are most distant.
",a,b,x,y);
        }
    }
    return 0;
} 


原文地址:https://www.cnblogs.com/lipu123/p/12405132.html