一种高效的计算素数的方法:埃拉托斯特尼筛法(Sieve of Eratosthenes)

素数筛

一、基本思想

通过标记非素数(即合数),从而得到所有素数。

二、算法步骤
  1. 初始化:

    • 创建一个布尔型列表 sieve,表示从 0 到 n 每个数是否是素数。初始时,我们假设所有数都是素数,因此将列表中所有的元素都设置为 True,但 sieve[0]sieve[1] 设为 False,因为 0 和 1 不是素数。
  2. 筛选过程:

    • 从 2 开始,遍历到 sqrt(n),⭐因为如果一个合数 a 可以被分解成两个因数 bc,则必定存在 bc 小于等于 sqrt(a)。因此,我们只需要检查小于等于 sqrt(n) 的数,来筛选出所有的合数。
    • 对于每一个被标记为素数的数 i,我们将 i 的倍数(即从 i*i 开始的所有 i 的倍数)标记为合数,即将 sieve[j] 设置为 False
  3. 收集素数:

    • 遍历 sieve 列表,将其中值为 True 的索引(即素数)收集到一个新列表 primes 中。

⭐为什么从 i * i 开始标记?

  1. 标记合数的过程: 在算法中,i 是当前的素数。如果 i 是素数,那么我们从 i * i 开始标记,因为:

    • 小于 i 的倍数(即 2i, 3i, …)已经在之前的素数处理中标记过了。
    • i * i 开始标记,可以确保我们不会重复标记已经处理过的合数,而且可以避免不必要的操作。
  2. 避免重复计算:

    • 如果 i 是素数,那么 i 的倍数 2i, 3i, 4i, ... 已经在之前的步骤中被标记过了(因为这些倍数的因数已经小于 i,且我们在处理更小的素数时已经将它们标记为合数)。
    • 因此,开始从 i * i 标记是为了跳过那些已经被处理过的倍数,避免重复操作,从而提高效率。
三、例子:

假设我们计算素数 n = 30,我们要找出小于等于 30 的所有素数。

  1. i = 2 开始,2 是素数,标记 2 的倍数:4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30
  2. 然后 i = 33 也是素数,标记 3 的倍数:9, 12, 15, 18, 21, 24, 27, 30
  3. 接下来,i = 4sieve[4] 已经是 False(在处理 2 时就已经标记过了),跳过。
  4. 然后 i = 55 是素数,标记 5 的倍数:25, 30
    • 注意到 5 * 5 = 25,这是第一次标记 5 的倍数,而更小的倍数(如 5 * 2 = 10)已经在前面标记过了。

通过这种方式,我们避免了重复的计算,提升了效率。

四、核心代码
def sieve_of_eratosthenes(n):
    # 初始化布尔数组,假设所有数都是素数
    sieve = [True] * (n + 1)  # 创建大小为 n+1 的布尔数组
    sieve[0] = sieve[1] = False  # 0 和 1 不是素数

    # 从 2 开始筛选
    for i in range(2, int(n ** 0.5) + 1):  # 只需到 sqrt(n)⭐转换为int类型
        if sieve[i]:  # 如果 i 是素数
            for j in range(i * i, n + 1, i):  # 将 i 的倍数标记为合数
                sieve[j] = False
    
    # 返回所有值为 True 的索引,这些索引对应的数字是素数
    primes = [i for i in range(2, n + 1) if sieve[i]]
    return primes
代码执行
print(sieve_of_eratosthenes(30))

输出:

[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]

PS:留意标注⭐的地方

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值