生成素数的三大法宝
埃氏筛
理论
埃拉托斯特尼筛法,简称埃氏筛或爱氏筛,是一种由希腊数学家埃拉托斯特尼所提出的一种简单检定素数的算法。要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的倍数剔除,剩下的就是素数。
过程:
要得到自然数n以内的全部素数,必须把不大于 的所有素数的倍数剔除,剩下的就是素数。
给出要筛数值的范围n,找出以内的素数。先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个质数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个质数5筛,把5留下,把5的倍数剔除掉;不断重复下去…。
Java实现
public int countPrimes(int n) {
int[] nums = new int[n+1];
for (int i=2;i<=n;i++) nums[i]=i;
for (int i=2;i<Math.sqrt(n);i++){
if (nums[i]!=0){
int p = i*i;
while (i<=n){
nums[p] = 0;
p = p+i;
}
}
}
int count = 0;
for (int i=2;i<=n;i++){
if (nums[i]!=0) count++;
}
return count;
}
线性筛
理论
在埃氏筛的遍历过程中,会出现重复标记的情况。例如30既是2的倍数也是3的倍速还是5的倍数,那么在遍历2的所有倍数时会被标记一次,在遍历3的倍数时又会被标记一次,在遍历5的倍数时又会被标记一次。可以优化埃氏筛,但还是会有被重复标记的数,而线性筛可以实现完全无重复的解法。
每一个合数都可以以唯一形式被写成质数的乘积。也就是说,一个合数可以表示成一个质数和一个数的乘积。
Java实现
public int countPrimes(int n) {
int[] nums = new int[n+1];
int[] prime = new int[n+1];
int count = 0;
for (int i=2;i<=n;i++){
if (nums[i]==0) prime[count++] = i;
for (int j=0;j<count&&i*prime[j]<n;j++){
nums[prime[j]*i] = 1;
if(i % prime[j] == 0) break;
}
}
return count;
}
奇数筛
理论
已知偶数不一定是质数,而质数一定是奇数,所以只需要在奇数范围内标记出合数,剩下的奇数便都是质数了。
根据上述条件,可以在已经有的埃氏筛的基础上进行优化。即,先从所有数中划分出奇数,再从奇数中使用埃氏筛划分出质数。
Java实现
public int countPrimes(int n) {
int[] prime = new int[n+1];
int count = 0;
if (n>2) {
prime[2] = 1;
}
for (int i=3;i<n;i++){
if (i%2==1) prime[i] = 1;
else prime[i] = 0;
}
for (int i=2;i<n;i++){
if(prime[i]==1){
count++;
for (int j=2*i;j<n;j+=i) prime[j] = 0;
}
}
return count;
}