求小于n的素数,除了用暴力筛选以外,还可以使用素数筛法,效率更高。
Eratosthenes 筛法
先假设2-n中所有的数都是素数。从2开始,依次将,...设为合数,之后依次筛选3,5,7...等素数的倍数,直到超过n。以n=20为例子,先假设所有的数都是合数,再将素数的倍数转为素数:
第一次选取2的倍数,筛选结果:4,6,8,10,12,14,16,18
第二次选取3的倍数,筛选结果:9,12,15,18
第三次选取5的倍数,筛选结果:10,15
第四次选取7的倍数,筛选结果:14
...
以此类推,最后筛选结果的合集为:4,6,8,9,10,12,14,15,16,18
即素数为剩下的2,3,5,7,11,13,17,19
代码实现如下:
func eratosthenes(n int) []int {
res := make([]int, 0)
//isPrime是否是素数,0-是,1-否
isPrime := make([]int, n)
for i := 2; i < n; i++ {
if isPrime[i] == 0 {
res = append(res, i)
for j := 2; j <= n; j++ {
if i*j >= n {
break
}
isPrime[i*j] = 1
}
}
}
return res
}
时间复杂度为。
不足之处:同一个数,可能会被多次筛选,例如,第一次筛选的时候,筛选了6,第二次筛选的时候,也筛选了6。可以用euler筛法处理这个问题。
Elur筛法
增加一个素数数组,用来保存已经确认了的素数。
第一次筛选(各个素数的2倍):4,此时3还没有进入素数数组(即还没有确认为素数)
第二次筛选(各个素数的3倍):6,9
第三次筛选(各个素数的4倍):8,因为4 % 2 == 0, break,因为4是2的倍数,那么4*3=12也一定是2的倍数,在接下来的筛选中,还要计算2的其他倍数,一定能覆盖这个数,因此可以break。
第四次筛选(各个素数的5倍):10,15
第五次筛选(各个素数的6倍):12,因为6 % 2 == 0,break,进行下一次筛选
第六次筛选(各个素数的7倍):14
第七次筛选(各个素数的8倍):16,因为8 % 2 == 0,break,进行下一次筛选
依此类推...
代码如下:
func euler(n int) []int {
count := 0
primes := make([]int, 0)
//isPrime是否是素数,0-是,1-否
isPrime := make([]int, n)
for i := 2; i < n; i++ {
if isPrime[i] == 0 {
primes = append(primes, i)
count += 1
}
for j := 0; j < count; j++ {
if i*primes[j] >= n {
break
}
isPrime[i*primes[j]] = 1
//保证每个数只筛选一次
if i%primes[j] == 0 {
break
}
}
}
return primes
}
对于18-20行if i%primes[j] == 0 { break }的解释为:如果i是primes[j]的倍数,那么i的倍数一定也是primes[j]的倍数,那么在接下来的筛选中,一定可以找到这个primes[j]的倍数,参考上文第三次筛选的结果。