普通素数筛法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//bitset 被置1说明是素数
public static void count(BitSet set){
//length() -> Returns the "logical size" of this BitSet
for(int i = 2; i < set.size(); i++){
//初始化,全部置1
set.set(i);
}
for(int i = 2; i * i < set.size(); i++){
if(set.get(i)){
for(int j = 2 * i; j < set.length(); j += i){
//素数的倍数全部标记为非素数
set.clear(j);
}
}
}
}

线性素数筛法

上面的筛选方法中,可以看出,有很多重复
比如30,会被2,3,5都标记,这样重复很多次
下面的方法所有数字只会被标记一次,是线性的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static void main(String[] args) {
final int N = 100000;
boolean[] isNot = new boolean[N];
int[] prime = new int[N];
int cnt = 0;
for(int i = 2; i < N; i++){
if(!isNot[i]){
prime[cnt++] = i;
}
//1.每个数字都会经过这循环
//a.如果是素数,就标记素数的素数倍数,即k=p1*p2,而p1,p2都是不相同的素数,一定不会重复筛选
//b.如果是合数,就只晒出该数字和最小素数的乘积,这样筛选才不会重复。
//因为一个合数是若干素数的乘积,因为筛选过程不重复,因此只会被最小素数和另一数字乘积时选住
for(int j = 0; j < cnt && prime[j] * i < N; j++){
isNot[i * prime[j]] = true;
// System.out.println("i * prime[j] = "+i+"*"+prime[j]+" = "+i * prime[j]);
//2.该合数是素数的倍数就退出筛选
if(i%prime[j] == 0){
break;
}
}
}
}

关键之处在于2,保证了每个数字筛选一次,因为只会被其最小的素数因子筛选到!
这里所有合数都可以表示成若干个素数的乘积