区间筛法找素数

区间筛法用于快速筛出a到b区间的全部素数,b-a<=1e6。

题目为nefu-2380,原题链接:Problem - 2380 (nefu.edu.cn)

 首先给出一个题解的错误代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6;
bool b[N + 5];
bool bprime[N + 5];
LL prime[N + 5];
LL isprime[N + 5];
LL p;
LL cnt;
void init(LL l, LL r)
{
	memset(b, 1, sizeof(b));
	memset(bprime, 1, sizeof(bprime));
	b[0] = b[1] = 0;
	for (LL i = 2; i * i < r; i++) {
		if (bprime[i]) {
			isprime[++cnt] = i;
			for (LL j = max(2LL, (l + i - 1) / i) * i; j <= r; j += i) {
				b[j - l + 1] = 0;
			}
		}
		for (int j = 1; j <= cnt && i * isprime[j] <= N; j++) {
			bprime[i * isprime[j]] = 0;
			if (i % isprime[j] == 0) break;
		}
	}
	for (LL i = 1; i <= r - l; i++) {
		if (b[i]) prime[++p] = i + l - 1;
	}
}
int main()
{
	LL l, r;
	while (cin >> l >> r) {
		p = 0;
		memset(prime, 0, sizeof(prime));
		//if (l == 1) l++;
		init(l, r + 1);
		if (p <= 1) cout << "There are no adjacent primes." << endl;
		else {
			LL dmin = 9999999999;
			LL dmax = 0;
			LL dx, dy;
			for (LL i = 2; i <= p; i++) {
				if (dmin > prime[i] - prime[i - 1]) {
					dmin = prime[i] - prime[i - 1];
					dx = i;
				}
				if (dmax < prime[i] - prime[i - 1]) {
					dmax = prime[i] - prime[i - 1];
					dy = i;
				}
			}
			printf("%lld,%lld are closest, %lld,%lld are most distant.\n", prime[dx - 1], prime[dx], prime[dy - 1], prime[dy]);
		}
	}
	return 0;
}

错误原因: 因为要解决当左端点为1时答案出错的情况,所有在init中赋值b[0]和b[1]导致在最后把l到r区间的素数放到prime数组中出现错误

欧拉筛写法(较繁琐):

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6;
bool b[N + 5];//a到b的素数判断
bool bprime[N + 5];//0到sqrt(b)的素数判断数组
LL prime[N + 5];//a到b的素数数组
LL isprime[N + 5];//0到sqrt(b)的素数数组
LL p;
LL cnt;
void init(LL l, LL r)
{
	memset(b, 1, sizeof(b));
	memset(bprime, 1, sizeof(bprime));
	for (LL i = 2; i * i < r; i++) {
		if (bprime[i]) {
			isprime[++cnt] = i;
			for (LL j = max(2LL, (l + i - 1) / i) * i; j <= r; j += i) {
				b[j - l + 1] = 0;
			//2LL是2的长整型形式
			// max用于把((a+i-1)/i)*i)和最小素数2进行比较,一般是((a+i-1)/i)*i),但是但a是1就是2了,它保证删除的都是素数的倍数
			// ((a+i-1)/i)*i)当a为1时,如果没有最大值判定,那么j就是1,会把所有数字删掉
			// 右移回到a,b区间进行判断,判断成功左移回来赋值false
			}
		}
		for (int j = 1; j <= cnt && i * isprime[j] <= N; j++) {//正常欧拉筛写法
			bprime[i * isprime[j]] = 0;
			if (i % isprime[j] == 0) break;
		}
	}
	for (LL i = 1; i <= r - l; i++) {//找到了所有a到b的素数,把素数放进素数表中
		if (b[i]) prime[++p] = i + l - 1;
	}
}
int main()
{
	LL l, r;
	while (cin >> l >> r) {
		p = 0;
		memset(prime, 0, sizeof(prime));
		if (l == 1) l++;
		init(l, r + 1);//r+1是因为区间筛是对[a,b)区间筛所以加一
		if (p <= 1) cout << "There are no adjacent primes." << endl;
		else {
			LL dmin = 9999999999;
			LL dmax = 0;
			LL dx, dy;
			for (LL i = 2; i <= p; i++) {
				if (dmin > prime[i] - prime[i - 1]) {
					dmin = prime[i] - prime[i - 1];
					dx = i;
				}
				if (dmax < prime[i] - prime[i - 1]) {
					dmax = prime[i] - prime[i - 1];
					dy = i;
				}
			}
			printf("%lld,%lld are closest, %lld,%lld are most distant.\n", prime[dx - 1], prime[dx], prime[dy - 1], prime[dy]);
		}
	}
	return 0;
}

埃氏筛写法(较简洁):只需要把init函数的欧拉筛相关全部换成埃氏筛即可

void init(LL l, LL r)
{
	memset(b, 1, sizeof(b));
	memset(bprime, 1, sizeof(bprime));
	for (LL i = 2; i * i < r; i++) {
		if (bprime[i]) {
			for (LL j = i * i; j * j <= r; j += i) {//这里压缩了一下
				bprime[j] = 0;
			}
			for (LL j = max(2LL, (l + i - 1) / i) * i; j <= r; j += i) {
				b[j - l + 1] = 0;
			}
		}
	}
	for (LL i = 1; i <= r - l; i++) {
		if (b[i]) prime[++p] = i + l - 1;
	}
}

个人推荐埃氏筛但是由于本人之前使用的的全是欧拉筛,所以第一次用区间筛使用的也是欧拉筛。但是写了一个欧拉筛之后,跟其他人写的对比发现确实麻烦了不少,所以自己又写了一个埃氏筛的方法以作对比。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值