//count统计素数个数
public static int bf(int n){
int count=0;
for (int i = 2; i < n; i++) {
count+= isPrime(i)? 1:0;
}
return count;
}
//isPrime方法是进行素数运算,暴力算法
private static boolean isPrime(int x) {
//为什么不是i<x而是i*i<=x呢,因为2*6=6*2,
//所以只用i<根号下x就行了,然后得出 i *i<= x,不能省去等于号
for (int i = 2; i *i<= x; i++) {
if(x%i==0){
return false;
}
}
return true;
}
暴力算法如上图所示理解理解就行
//埃氏筛选
public static int eratosthenes(int n){
//默认值布尔数组都为false,所以false代表素数
boolean[] isPrime=new boolean[n];
//count统计个数
int count=0;
for (int i = 2; i < n; i++) {
if(!isPrime[i]){
count++;
//将标记位一律设置为true。
// for (int j =2*i; j <n ; j+=i)
for (int j =i*i; j <n ; j+=i) {
isPrime[j]=true;
}
}
}
return count;
}
每一行代码都非常的精髓。
我们可以从非素数入手,非素数又称为合数。我们不难发现4=2*2,6=2*3。所以我们可以把所有的合数进行标记为true在for循环中跳过,减少遍历的时间。
在二重j循环的,j+=i是十分的巧妙,它在内部将i的系数进行了递增。
比如i=2时;
j =i*i; j <n ; j+=i
循环第二次变成了2*3<n ,以此类推
可能有人会考虑一开始的 if(!isPrime[i])是不是会把合数标记成素数,其实这个担忧是不存在的,100以内所有的偶数一开始都会被2*i进行标记,标记速度远远快于i增长的速度。不妨我们挑一个数,i=9时早就已经被第二重循环i=3的时候遍历了标记为true,所以根本不用担心。
for (int j =2*i; j <n ; j+=i) { isPrime[j]=true; }
这一行代码我们也是可以优化的,因为i=4时,2*4,3*4都被之前i=2和i=3的时候标记过,所以可以直接从4*4开始省去重复标记。