赞
踩
\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; } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。