【数据结构与算法】质数筛法:埃氏筛和欧拉筛

筛法是找出质数的方法。基本思路是维护一个表,初始化认为全都是质数,然后一步步划掉表里的合数,最终留下来的就是质数。
埃氏筛
朴素埃氏筛的思路很简单,遍历找到的质数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)。
但是实际上这里有优化空间:

  1. 首先是外层循环不需要到n,只需要到 sqrt(n) 即可。因为对于一个合数,只需要用它更小的质数去标记即可。
  2. 对于某个质数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。

  1. 如果 i % p == 0,那么p是合数 p*i 的最小质因数(原因同下一条),然后break出来
  2. 如果 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)

posted @ 2026-10-11 17:47  陈冰语  阅读(4)  评论(0)    收藏  举报