啊,耳熟能详。素数又称质数,一个大于1的自然数,除了1和它本身外,不能被其他自然数整除,换句话说就是该数除了1和它本身以外不再有其他的因数;否则称为合数。啊!1不是素数啊
啊,也耳熟能详了,暴力枚举一下除1和本身的自然数是否会被整除。
bool is_prime(int x) {
for(int i=2;i
啊,其实可以优化的,也耳熟能详(我真欠揍),我们只用从2到 x \sqrt x x 就可以了。
很容易发现这样一个事实:如果a是x的约数,那么 x a \frac{x}{a} ax也是x的约数。
得证。
代码:
bool is_prime(int x) {
for(int i=2;i<=sqrt(x);i++)
if(x%i==0) return 0;
return 1;
}
啊,其实还有更简单的判断方法:Miller-Rabin。耳熟能详个屁,自行学习吧。
这Eratosthenes一长串不是人读的是一个人的名字:埃拉托斯特尼。啊,耳熟能详。Eratosthenes 筛法的原理非常简单:一个素数的倍数一定不是素数。
代码:
void Eratosthenes(int n) {
for(int i=2;i<=n;i++) {
if(check[i]) continue;
prime[++cnt]=i;
for(int j=2;j<=n/2;j++)
check[i*j]=1;
}
return ;
}
过程如下:
正在处理的数 | 数组check的情况 |
---|---|
2 | 2,3, |
3 | 2,3, |
5 | 2,3, |
7 | 2,3, |
11 | 2,3, |
可以发现,2和3都会把6标记为合数。实际上,小于x2的x的倍数在之前就会被标记,所以我们可以优化为:
void Eratosthenes(int n) {
for(int i=2;i<=n;i++) {
if(check[i]) continue;
prime[++cnt]=i;
for(int j=i;j<=n/i;j++)
check[i*j]=1;
}
return ;
}
时间复杂度为 O ( n l o g l o g n ) O(n \ log \ log \ n) O(n log log n),显而易见。
埃式筛法也会重复标记,一个数的素数约数越多,重复标记越多。
线性筛法通过“从大到小累计质因子”的方式标记合数。
代码:
void yu(int n) {
check[1]=1;
for(int i=2;i<=n;i++) {
if(!check[i]) prime[++cnt]=i;
for(int j=1;j<=cnt && i*prime[j]<=n;j++) {
check[i*prime[j]]=1;
if(i%prime[j]==0)
break;
}
}
return ;
}
啊,简单。