厄拉多塞筛法

算法介绍

厄拉多塞筛算法(Eratosthenes Sieve)是一种求素数的方法,由古希腊数学家厄拉多塞提出。它的原理是,给定一个数 n,从 2 开始依次将 \(\sqrt{n}\) 以内的素数的倍数标记为合数,标记完成后,剩余未被标记的数为素数(从 2 开始)。如此可省去检查每个数的步骤,使筛选素数的过程更加简单。厄拉多塞筛算法具体步骤如下:

  1. 读取输入的数 n,将 2 到 n 的所有整数记录在表中
  2. 从 2 开始,划去表中所有 2 的倍数
  3. 由小到大寻找表中下一个未被划去的整数,再划去表中所有该整数的倍数
  4. 重复第(3)步,直到找到的整数大于 \(\sqrt{n}\) 为止
  5. 表中所有未被划去的整数均为素数

算法流程图

seive

具体代码(python)

def sieve_prime(n: int) -> list:
    """厄拉托塞筛法"""
    if n < 2:  # 不存在小于 2 的素数
        return []
    is_prime = [True for _ in range(n + 1)]
    # 遍历 i=2 到 根号 n
    for i in range(2, int(pow(n, 0.5)) + 1):
        if is_prime[i]:  # 筛去 i 的倍数
            for j in range(i * i, n + 1, i):
                is_prime[j] = False
    # 返回 2 到 n 中未被筛去的数
    return [i for i in range(2, n + 1) if is_prime[i]]

在筛去 i 的倍数的时候,第一个数是 \(i \times i\) 而不是 i,这是因为对于所有 \(k \times i , \; k < i\),都在前面被筛过,故可以跳过这些数

测试样例和结果

  • 102 以内的素数共 25 个,最后两个为 89, 97
  • 103 以内的素数共 168 个,最后两个为 991, 997
  • 104 以内的素数共 1229 个,最后两个为 9967, 9973
  • 105 以内的素数共 9592 个,最后两个为 99989, 99991
  • 106 以内的素数共 78498 个,最后两个为 999979, 999983
  • 107 以内的素数共 664579 个,最后两个为 9999973, 9999991

一些改进

这个算法很大的一个问题是对空间很不友好,需要存储很大的一个素数表
除了 2 之外,所有的素数都是奇数,那么在筛的时候只需要考虑奇数即可
在构建表格的时候,先前将下标 i 对应整数 i,现在可以将下标 i 对应整数 2i+3,节约一半的存储空间

def sieve_prime(n: int) -> list:
    """厄拉托塞筛法"""
    if n < 2:  # 不存在小于 2 的素数
        return []
    elif n == 2:
        return [2]
    end = (n - 3) // 2
    is_prime = [True for _ in range(end + 1)]
    # 遍历 i=3 到 根号 n
    for i in range(3, int(pow(n, 0.5)) + 1, 2):
        k = (i - 3) // 2  # 将 i 映射回下标 k
        if is_prime[k]:  # 筛去 i 的倍数
            for j in range(i * i, n + 1, 2 * i):
                is_prime[(j - 3) // 2] = False
    # 返回 2 到 n 中未被筛去的数
    return [2] + [2 * i + 3 for i in range(end + 1) if is_prime[i]]

最后

除了厄拉多塞筛法之外,还有欧拉筛等筛法

使用 python 时间和空间效率都较低,对于标记素数,可以采用 c++ 的 bitset,bitset 是以比特为单位标记的,会极大降低存储消耗

参考资料:《密码学实验教程》

posted @ 2021-07-07 16:57  kentle  阅读(1753)  评论(0编辑  收藏  举报