素数筛选及区间素数

判断单个数是否为素数

时间复杂度 O ( n ) O(\sqrt{n}) O(n )

bool check(int n){
    int m=sqrt(n+0.5);
    for(int i=2;i<=m;i++){
		if(n%i==0) return false;
    return true;
}
埃氏筛法

算法复杂度 O ( n ∗ l o g ( l o g n ) ) O( n*log(log n) ) O(nlog(logn))

原理:从 2 2 2开始,每第一次出现且之前没有它的因数出现就一定是素数,然后再把它的所有倍数设为 t r u e true true。需要注意的点是,如果我们只需要判断 m a x n maxn maxn范围内的数是不是素数,那么外层循环设置为 s q r t ( m a x n + 0.5 ) sqrt(maxn+0.5) sqrt(maxn+0.5)即可。但是如果我们需要用数组保存 m a x n maxn maxn内的素数,那么外层循环也必须是 m a x n maxn maxn

const int maxn=?;
bool isprime[maxn];  //maxn即需要求多少范围里的素数
int prime[N];  //根据maxn的大小调节,该数组储存了maxn范围内的所有素数
int num;

void Prime(){
	memset(isprime,true,sizeof(isprime));
	int m=sqrt(maxn+0.5);  //实际上从maxn开始也不会太费时间
	for(int i=2;i<=m;i++){   //一个数的因数按其根号的值对称分布,因此只要能枚举到其因数便能筛选出其是否为素数
		if(isprime[i]){  //凡是在下面一层循环里被设置为false的一定不是素数
			//prime[num++]=i;  //保存素数的话外层循环也必须是maxn
			for(int j=2*i;j<maxn;j=j+i)  //如果从i*i开始,有可能爆int
				isprime[j]=false;  //这里实现了筛选,即素数的倍数一定是合数
		}
	}
}

可以将 b o o l bool bool数组替换为 b i t s e t bitset bitset降低空间复杂度

bitset<maxn> isprime;

void getPrime() {
    isprime.set();
    isprime[0] = isprime[1] = 0;
    int m = sqrt(maxn + 0.5);
    for (int i = 2; i <= m; i++)
        if (isprime[i]) {
            for (int j = i * i; j < maxn; j += i) isprime[j] = 0;
        }
 }

欧拉筛法

算法复杂度 O ( n ) O(n) O(n)

原理:

任意一个正整数 k k k,若 k ≥ 2 k \geq 2 k2,则k可以表示成若干个质数相乘的形式。埃氏筛法中,在枚举 k k k的每一个质因子时,都计算了一次 k k k,从而造成了冗余。因此在改进算法中,只利用 k k k的最小质因子去计算一次 k k k

与埃氏筛法不同的是,对于外层枚举 i i i,无论 i i i是质数,还是是合数,我们都会用 i i i的倍数去筛。但在枚举的时候,我们只枚举 i i i的质数倍

此外,在从小到大依次枚举质数 p p p来计算 i i i的倍数时,还需要检查 i i i是否能够整除 p p p。若 i i i能够整除 p p p,则停止枚举 。若 i i i能整除 p p p,因为我们是从小到大枚举素数的,那么又可以说 i i i已经被 p p p筛过了,所以 i i i乘其他质数的结果也一定是 p p p的倍数,后面会通过 p p p筛出而不需再筛,因此直接退出。

综上,欧拉筛法可以保证每个合数只会被枚举到一次,时间复杂度为 O ( n ) O(n) O(n)

vector<int> prime;
bitset<maxn> vis;

void init() {
    vis.reset();
    for (int i = 2; i < maxn; i++) {
        if (!vis[i]) prime.push_back(i);
        for (int j = 0; j < prime.size() && i * prime[j] < maxn; j++) {
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) break;
        }
    }
}
二次筛法

区间 [ l , r ] [l,r] [l,r]均小于 1 e 9 1e9 1e9 b − a < = 1 e 7 b-a<=1e7 ba<=1e7

因为一个非素数是被它的最小素数因子筛掉, 2147483647 2147483647 2147483647内的数要么是素数,要么是能被 s q r t ( 2147483647 ) sqrt(2147483647) sqrt(2147483647)内的素数整除的合数,也就是说, [ l , r ] [l,r] [l,r]区间的所有非素数的素数因子都在 s q r t ( 2147483647 ) sqrt(2147483647) sqrt(2147483647)内,预先将 s q r t ( 2147483647 ) sqrt(2147483647) sqrt(2147483647)内的所有素数找出来,然后用这些素数去筛掉指定区间的所有非素数

求出某个区间的素数且这些素数可能很大,需要进行下标偏移

const ll up = 2147483647;
const int maxn = 1e6 + 10;

vector<int> prime, ans;
bool is_prime[maxn], vis[maxn];

void euler() {
    int m = sqrt(up + 0.5);
    prime.clear();
    memset(is_prime, 1, sizeof(is_prime));
    is_prime[0] = is_prime[1] = 0;
    for (int i = 2; i <= m; i++) {
        if (is_prime[i]) prime.push_back(i);
        for (int j = 0; j < prime.size() && 1LL * i * prime[j] <= m; j++) {
            is_prime[i * prime[j]] = 0;
            if (i % prime[j] == 0) break;
        }
    }
}

void solve(ll l,ll r) {
    memset(vis, 1, sizeof vis);
    if (l == 1) l++;
    for (int i = 0; i < prime.size() && 2LL * prime[i] <= r; i++) {
        if (prime[i] < l) {
            ll x = l / prime[i], L;
            if (x * prime[i] < l) L = (x + 1) * prime[i];
            else L = x * prime[i];
            for (ll j = L; j <= r; j += prime[i]) vis[j - l] = 0;
        } else {
            for (ll j = 2LL * prime[i]; j <= r; j += prime[i]) vis[j - l] = 0;
        }
    }
    ans.clear();
    for (int i = 0; i <= r - l; i++)
        if (vis[i]) ans.push_back(i + l);
}
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值