质数(C++)

质数

定义

\qquad 质数,又称素数,指在大于1的自然数中,除了1和它本身以外不再有其他因数的自然数。

判断素数

思路:

试除法

\qquad 枚举区间 [ 2 , n ) [2,n) [2,n) 内的每个自然数,若其能被 n n n 整除,则 n n n 为合数。若均不能被 n n n 整除,则 n n n 为素数。

\qquad 我们知道,除完全平方数外,一个自然数的因数一定是成对出现的,分别位于 n \sqrt{n} n 的两侧。而一个完全平方数,除 n \sqrt{n} n 外,其因数也是成对出现,同样分列其两侧。由此,我们可以得出结论:枚举一个自然数 n n n 可能存在的因数,只需要枚举 [ 2 , ⌊ n ⌋ ) [2,\lfloor \sqrt{n} \rfloor ) [2,n ⌋) 之间的整数即可。 ($\lfloor x \rfloor $ 表示向下取整)

代码:

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

小小优化:

\qquad 注意到,我的代码for循环中并没有写 i <= sqrt(x); ,这是因为求平方根的函数的运算速度很慢。而且,我也没有写 i * i <= x; ,这是因为,如果 x x x 很大,接近 int 能存储整数大小的极限时,i * i 就有可能会超出int范围,计算出负数,使得程序出现难以发现的错误。

\qquad 我用 i <= x / i; 这种写法能够成功避免以上问题,即使在C++中做除法确实比做乘法慢得多。

素数筛

\qquad 当我们需要判断大量数字是否为素数时,上面的逐个判断的效率就很低下了。因此我们就需要学习更加迅速的算法来解决这类问题:筛法。以下 是两种常用的素数筛法:埃氏筛法欧氏筛法

法一:朴素筛(埃氏筛法)

思路:

\qquad 此算法由古希腊数学家埃拉托斯特尼发明,故称作埃氏筛法。

\qquad 埃氏筛法的思路是用每个现有素数删去其倍数。这是因为每个合数的因数一定小于它本身,也就是说它一定会被比它小的素数筛掉。这样的话,最终剩下没有筛掉的数字就都是素数了。

时间复杂度:

\qquad 首先有 n n n 个数字,这 n n n 个数字里是 2 2 2 的倍数的有 n 2 \frac{n}{2} 2n 个,是 3 3 3 的倍数的有 n 3 \frac{n}{3} 3n 个,以此类推,每次都需要筛掉 n n n 以内每个质数的倍数。设 n n n 以内最大的质数为 p p p ,总计算量为 n 2 + n 3 + n 5 + ⋯ + n p \frac{n}{2}+\frac{n}{3}+\frac{n}{5}+\cdots+\frac{n}{p} 2n+3n+5n++pn,它的数量级为:

\qquad O ( n l o g l o g n ) O(nloglogn) O(nloglogn)

代码:
const int N = 1e5 + 10;
bool s[N];										//s[i]表示i是否为合数(非素数)
void prime() {
	int i, j;
	s[0] = s[1] = true;							//不是素数
	for (i = 2; i <= N; i++)
		if (!s[i]) {							//是素数才筛
			for (j = i + i; j <= N; j += i)		//删去素数的倍数
				s[j] = true;
}
缺点:

\qquad 一个数会被它所有的因数都删一次,存在重复计算。

\qquad 举个例子:当 i = 2 i=2 i=2 时,30 会被筛掉( s[30]=true );当 i = 3 i=3 i=3 时,30会被再筛一次( s[30]=true );当 i = 5 i=5 i=5 时,30 会被叒筛一次( s[30]=true ),这个语句也就被重复做了 3 次。总的来说:每一个合数都会被它的所有质因数筛一次,它有几个不同的因数,它就会重复做同样的语句多少次。因此这种筛法也存在一定的改进余地。

\qquad 顺理成章地,我们便引入了下一种筛法:线性筛(欧氏筛法),它可以完美避免这样的重复操作。

法二:线性筛(欧氏筛法)

思路:

\qquad n只会被它的最小质因子筛掉。

\qquad 1. i   %   p [ j ] = 0 i\space \%\space p[j]=0 i % p[j]=0 时, p [ j ] p[j] p[j] 一定是 i i i 的最小质因子,也是 i   ∗   p [ j ] i\space *\space p[j] i  p[j] 的最小质因子。

\qquad 2. i   %   p [ j ]   ≠ 0 i\space \%\space p[j]\space \ne 0 i % p[j] =0 时, p [ j ] p[j] p[j] 一定小于 i i i 的所有质因子,也一定是 i   ∗   p [ j ] i\space *\space p[j] i  p[j] 的最小质因子。

\qquad 综上,无论什么情况,任何一个合数,一定会被其最小质因子筛掉。

时间复杂度:

\qquad O ( n ) O(n) O(n)

代码:
#include <iostream>
using namespace std;
const int N = 1e7;
int p[N], t = 0;
bool s[N];			//0表示是质数
void prime(int n) {
	int i, j;
	s[0] = s[1] = 1;						//不是素数
	for (i = 2; i <= n; i++) {
		if (!s[i])	p[++t] = i;				//下一行用除法是防止 爆int
		for (j = 1; p[j] <= n / i; j++) {	//一个数会被它最小的因数删掉,线性筛
			s[i * p[j]] = 1;
			if (i % p[j] == 0)	break;
		}
	}
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值