素数判断及其筛法

一、试除法判断素数

素数的定义是只能被常数1或自己整除,不能被其他整数整除的正整数。即不能被[2, n-1]区间内的整数整除的都是素数(2,3除外),所以可以从2开始试除,直到n-1,如果都不能整除,那就是素数,否则就是合数。当然这个可以优化,只需要试除到 n \sqrt{n} n 就可以了,一个数如果被小于 n \sqrt{n} n 的数整除,必然被另一个大于 n \sqrt{n} n 的数整数。以下是试除法的代码。

bool isPrime(int n){
	if(n <= 1) return false;
	for(int i = 2; i <= n/i; i++){
		if(n % i == 0) return false;
	}
	return true;
}

二、埃式筛法

如果是求一个范围内的所有素数,用试除法一个一个去判断就太慢了,埃氏筛法可以很好解决这个问题,它的时间复杂度为 O ( n l o g ( n ) ) O( nlog(n) ) O(nlog(n)),可以快速找出[2, n-1]中所有素数,它的做法是输出最小素数,并筛掉它的倍数,例:对于数列{2,3,4,5,6,7,8,9,10,11,12}进行如下操作:

  1. 取出最小素数2,并筛去2的倍数,剩下{3,5,7,9,11}。
  2. 取出最小素数3,并筛去3的倍数,剩下{5,7,11}。
  3. 取出最小素数5,并筛去5的倍数,剩下{7,11}。
  4. 取出最小素数7,并筛去7的倍数,剩下{11}。
  5. 取出最小素数11,并筛去11的倍数,数列为空,结束操作。

代码如下:

const int maxn = 1e7;
int Prime[maxn];
bool visit[maxn];

int EratSieve(int n){
	int len = 0;
	for(int i = 0; i <= n; i++){
		visit[i] = false;
	}
	for(int i = 2; i <= n/i; i++){
		if(!visit[i]){
			for(int j = i*i; j <= n; j += i){
				visit[j] = true;
			}
		}
	}
	for(int i = 2; i <= n; i++){
		if(!visit[i]) prime[len++] = i;
	}
	return len;
}

三、欧拉筛法

上面讲的埃氏筛法虽然可以快速筛去区间[2, n] 内的素数,但是有些数也重复筛了,如12被 2 和 3 筛了 2 次,下面介绍的欧拉筛法很好地解决了这个问题,它的时间复杂度仅为 O ( n ) O(n) O(n)。代码如下:

const int maxn = 1e7;
int Prime[maxn];
bool visit[maxn];

int Euler(int n){
	int len = 0;
	for(int i = 0; i <= n; i++){
		visit[i] = false;
	}
	for(int i = 2;i <= n; i++){
		if(!visit[i]){
			prime[len++] = i;
		}
		for(int j = 0; j < len; j++){
			if(i*prime[j] > n) break;
			visit[i*prime[j]] = true;
			if(i%prime[j] == 0) break;
		}
	}
	return len;
}

第一个for循环的i即做筛选的数,也做筛选的倍数。做筛选的数时,从2开始筛选,直到n,标记为false的即为素数;做筛选的倍数时,每轮筛选都遍历素数列表,把素数列表的i倍置为true,但是如果倍数为合数,可能会中途退出,以n=12为例,有以下流程:

  1. 2为false,是素数,加入素数队列,同时筛掉2的2倍,即筛掉4。
  2. 3为false,是素数,加入素数列表,同时筛掉2,3的3倍,即筛掉6,9。
  3. 4为true,不是素数,筛掉2的4倍,4能被2整除,退出循环,即筛掉8。
  4. 5为false,是素数,加入素数列表,同时筛掉2,3,5的5倍,但15超过12,即筛掉10。
  5. 6为true,不是素数,筛掉2,3,5的6倍,但18超过12即筛掉12。
  6. 7为false,是素数,加入素数队列,但素数列表的7倍都超过12,不筛选。
  7. 8为true,不是素数,素数列表的8倍都超过8,不筛选。
    以此类推…

从上面可以看出来,用欧拉筛法不会重复筛同一个数,当数据很大时,可以大大降低时间复杂度。

有问题欢迎私信我!

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leslie_Hollow

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值