求1e11以内的素数
有两种做法,一种是打表,另一种是直接求。
打表
将1e11每隔len(len=2000w)个数字统计一下该区间内素数的个数,比如cnt[1] 表示[1,len]以内有多少个素数,cnt[2]表示[len+1,2*len】以内有多少个素数,依次类推。
然后维护一下前缀和,sum[i] = cnt[1] + ....+ cnt[i]
那么给定一个数字n,求[1,n]以内有多少个素数, 那么只要统计一下sum[n/len],然后再统计一下区间[n/len*len+1, n/len*len + n%len],由于这个内最多只有2000w个,那么只要对该区间内的数字进行筛法求素数,然后统计该区间内素数的个数就可以了。
任意区间内素数的个数
所以关键是如果求任意区间内素数的个数, 例如要求区间[a, b]内有多少个数字, 因为该区间内任意合数字的最大最小质因数不会超过sqrt(b),所以只要先求出区间[2,sqrt(b)]内的素数表,那么就可以用该素数表去筛去区间[a,b]内的所有合数。
#include <stdio.h> #include <string.h> #include <iostream> #include <algorithm> #include <vector> #include <queue> #include <set> #include <map> #include <string> #include <math.h> #include <stdlib.h> #include <time.h> using namespace std; typedef long long LL; /* * 要求区间[a,b]以内的素数, 那么该区间内所有合数的最小质因数绝对不超过sqrt(b), * 所以只要求出[2,sqrt(b)]以内的所有素数,然后用这些素数筛去区间[a,b]内的所有合数即可 * 要开的数组的大小 * M > b - a * N > sqrt(b) */ const int N = 1000000; const int M = 10000000; bool is_prime[N]; int prime[N], cnt; void get_prime(){ for(int i=3; i<N; ++i) is_prime[i] = true; cnt = 0; prime[cnt++] = 2; for(LL i=3; i<N; i+=2){ if(is_prime[i]){ prime[cnt++] = i; for(LL j=i*i; j<N; j+=2*i){ is_prime[j] = false; } } } } bool is_prime2[M]; int get_prime2(LL l, LL r){ for(LL i=0; i<=r-l; ++i) is_prime2[i] = true; for(LL i=0; i<cnt && (LL)prime[i]*prime[i]<=r; ++i){ /* (l+prime[i]-1)/prime[i]*prime[i] 得到最接近l的prime[i]的倍数是多少 */ for(LL j=max(2LL, (l+prime[i]-1)/prime[i])*prime[i]; j<=r; j+=prime[i]){ is_prime2[j-l] = false; } } int res = 0; //会把0和1当做素数,所以要减去 if(l==0) res -= 2; if(l==1) res -= 1; for(LL i=0; i<=r-l; ++i){ res += is_prime2[i]; /* printf("%lld %d\n", i+l, is_prime2[i]); */ } return res; } int main() { /* freopen("in.txt","r",stdin); */ /* freopen("out.txt","w",stdout); */ get_prime(); cout << get_prime2(1, 10000000) << endl; return 0; }