【数据结构与算法】质数筛法:埃氏筛和欧拉筛
筛法是找出质数的方法。基本思路是维护一个表,初始化认为全都是质数,然后一步步划掉表里的合数,最终留下来的就是质数。
埃氏筛
朴素埃氏筛的思路很简单,遍历找到的质数p,然后划掉2p,3p,...
isPrimes = [True] * (n + 1)
isPrimes[0] = False
isPrimes[1] = False
for i in range(2, n + 1):
if isPrimes[i]:
k = 2
while k * i < n + 1:
isPrimes[k * i] = False
k += 1
这些循环的运算次数大概是 n/1 + n/2 + ...n/n= n * (1 + 1/2 + ...+n)。因此,这个朴素方法的时间复杂度是 O(nlgn)。
但是实际上这里有优化空间:
- 首先是外层循环不需要到n,只需要到 sqrt(n) 即可。因为对于一个合数,只需要用它更小的质数去标记即可。
- 对于某个质数i,只需要从 i * i 开始划掉合数即可。对于一个小于 i * i的合数,它的最小质因数一定比i小,所以它之前一定被这个更小的质因数划掉了。而对于大于i*i的合数,它的最小质因数可能就是i,因此不能保证它之前就被划掉了,因此要重新划。
isPrimes = [True] * (n + 1)
isPrimes[0] = False
isPrimes[1] = False
for i in range(2, int(sqrt(n + 1) + 1)):
if isPrimes[i]:
for j in range(i * i, n + 1, i):
isPrimes[j] = False
这个方法的运算次数大概是 n/1 + n/2 + ... + n/sqrt(n) = O(nlglgn)
但是这个方法的缺点是,无法保证每个合数只被筛一次。因为在大于 i*i的那些合数中,仍然可能存在最小质因数小于i的那些合数,那些合数其实已经被最小质因数划掉了,但是又被i划了多一次。
要保证合数只被划一次,使用欧拉筛
欧拉筛
欧拉筛的基本思想是,每个合数都只被自己的最小质因数划一次。
它遍历倍数i,把小于i的素数都记录下来,然后再从小到大遍历素数p。
- 如果 i % p == 0,那么p是合数 p*i 的最小质因数(原因同下一条),然后break出来
- 如果 i % p != 0,那么p就是合数 p*i 的最小质因数。因为如果有一个更小的质因数,那么它会在之前就被break掉,因此这里一定就是最小的质因数
isPrimes = [True] * (n + 1)
isPrimes[0] = False
isPrimes[1] = False
Primes = []
for i in range(2, n + 1):
if isPrimes[i]:
Primes.append(i)
for p in Primes:
if i * p >= n + 1: break
if isPrimes[p]:
if i % p != 0:
isPrimes[i * p] = False
else:
isPrimes[i * p] = False
break
由于对于每个合数就删掉一次,因此复杂度 O(n)
浙公网安备 33010602011771号