Leetcode 计算质数 -- 埃氏筛、线性筛解析

2023-11-10

0 题目描述

leetcode原题链接:204. 计数质数
在这里插入图片描述

1 埃氏筛

很直观的思路是我们枚举每个数判断其是不是质数,枚举没有考虑到数与数的关联性,因此难以再继续优化时间复杂度。介绍一个常见的算法, 该算法由希腊数学家厄拉多塞(Eratosthenes ) 提出,称为厄拉多塞筛法,简称埃氏筛。
我们考虑这样一个事实:如果 x x x 是质数,那么大于 x x x x x x 的倍数 2 x , 3 x , … 2 x, 3 x, \ldots 2x,3x, 一定不是质数,因此我们可以从 这里入手。
我们设 i s Prime ⁡ [ i ] i s \operatorname{Prime}[i] isPrime[i] 表示数 i i i 是不是质数,如果是质数则为 1 1 1,否则为 0 0 0。 从小到大遍历每个数,如果这个数为质数,则将其所有的倍数都标记为合数(除了该质数本身),即 0 0 0,这样在运行结束的时候我们即能知道质数的个数。
这里还可以继续优化, 对于一个质数 x , x, x, 如果前面说的我们从 2 x 2 x 2x 开始标记其实是冗余的, 应该直接从 x ⋅ x x \cdot x xx 开始标记 , , , 因为 2 x , 3 x , … 2 x, 3 x, \ldots 2x,3x, 这些数一定在 x x x 之前就被其他数的倍数标记过了,例如 2 的所有倍数,3 的所有倍数等。
注意,0和1不是质数。

class Solution:
    # 埃氏筛
    def countPrimes(self, n: int) -> int:
        if n <= 0: return 0
        isPrime = [0]*2 + [1] *(n-2)
        res = 0
        for i in range(2, n, 1):
            if isPrime[i]:
                res += 1
                if i*i < n:
                    for j in range(i*i, n, i):
                        isPrime[j] = 0
        return res

复杂度分析
时间复杂度: O ( n log ⁡ n ) O(n\log n) O(nlogn)
空间复杂度: O ( n ) O(n) O(n),需要 O ( n ) O(n) O(n)的空间记录每个数是否为质数。

2 线性筛

埃氏筛其实还是存在冗余的标记操作,比如对于 15 这个数,它会同时被 3,5 两个数标记为合数,因此我们优化的目标是让每个合数只被标记一次,这样时间复杂度即能保证为 O ( n ) O(n) O(n),这就是接下来要介绍的线性筛。
相较于埃氏筛,需要多维护一个 p r i m e s pr i m e s primes 数组表示当前得到的质数集合。我们从小到大遍历,如果当前的数 x x x 是质数,就将其加入 p r i m e s primes primes数组。
另一点与埃氏筛不同的是, 「标记过程」不再仅当 x x x 为质数时才进行,而是对每个整数 x x x 都进行。对于整数 x x x,我们不再标记其所有的倍数 x ⋅ x , x ⋅ ( x + 1 ) , … , x \cdot x, x \cdot(x+1), \ldots, xx,x(x+1),, 而是只标记质数集合中的数与 x x x 相乘的数,即
primes 0 ∗ x , _{0} * x, 0x, primes 1 ∗ x , … , _{1} * x, \ldots, 1x,, 且在发现 x x x mod primes i = 0 _{i}=0 i=0 的时候结束当前标记。
核心点在于:如果 x x x 可以被 primes i i i 整除,那么对于合数 y = x ⋅ y=x \cdot y=x primes i + 1 i+1 i+1 而言,它一定在后面遍历到 x p r i m e s i ⋅ p r i m e s i + 1 \frac{x}{p r i m e s_{i}} \cdot p r i m e s_{i+1} primesixprimesi+1 这个数的时候会被标记,其他同理,这保证了每个合数只会被其「最小的质因数」筛去,即每个合数被标记一次。

class Solution:
    # 线性筛
    def countPrimes(self, n: int) -> int:
        if n <= 0: return 0
        isPrime = [0]*2 + [1] *(n-2)
        primes = []
        for i in range(2, n, 1):
            if isPrime[i]:
                primes.append(i)
            for _, prime in enumerate(primes):
                if i*prime >= n:
                    break
                isPrime[i*prime] = 0
                if i % prime == 0:
                    break
        return len(primes)

复杂度分析
时间复杂度: O ( n ) O(n) O(n),线性筛保证每个合数只被其最小质因子筛掉一次,所以理论上算法复杂度是 O ( n ) O(n) O(n)线性时间复杂度。
空间复杂度: O ( n ) O(n) O(n),需要 O ( n ) O(n) O(n)的空间记录每个数是否为质数。

参考资料

计算质数

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Leetcode 计算质数 -- 埃氏筛、线性筛解析 的相关文章

随机推荐

  • 在Windows Server2016中安装SQL Server2016

    SQL Server2016安装硬 软件条件 点击打开链接 WinServer2016的安装参见 在虚拟机中安装Windows Server2016 1 SQL Server2016下载地址 1 SQL Server2016安装包 2016
  • SuperPunch - unity3D拳击小游戏项目源码

    SuperPunch是一个完整的项目 准备发布并且适合移动设备 它包含构建顶头拳击游戏的所有必要内容 特征 移动友好的纹理 分层的 包括 SVG 文件 包括 PNG文件 包括 C 脚本 包括文档 包括6架战斗机 包括战士动画 闲置 拳击 受
  • QChart入门教程-绘制正弦曲线

    1 创建界面 将widget作为容器进行绘图 并将widget提升为QChartView类 1 1 单击widget 右键中选择 提升 提升的类名称中填写 QChartView 会自动生成头文件名 选择 添加 将类和头文件添加进要提升的类中
  • ElasticSearch第十八讲 ES-Master节点职责和ES是如何做到数据实时性的

    Elasticsearch Master 节点的职责 由主节点负责ping 所有其他节点 判断是否有节点已经挂掉 创建或删除索引 决定分片在节点之间的分配 稳定的主节点对集群的健康是非常重要的 虽然主节点也可以协调节点 路由搜索和从客户端新
  • 6.84 C++ 遍历数组的几种方式

    1 计算出数组长度进行遍历 数组类型确定 数组中每个元素本身的字节大小就已经确定 利用 sizeof 函数可以计算出数组长度 而后利用 for 循序进行数组的遍历 2 使用类似 foreach 的方式进行遍历 C 中也可使用类似 forea
  • AVI文件与WAV文件格式

    AVI 与WAV文件都属于RIFF文件 因此都遵循RIFF文件的格式要求 先看看RIFF文件的格式 第一 RIFF 大小 AVI WAV 数据 第二 RIFF 文件中实际的数据通常采用列表 list 和块 Chunk 的形式表示 列表结构为
  • 智能门锁电路图_蓝牙门锁原理图一览 蓝牙智能门锁工作原理介绍

    蓝牙智能门锁工作原理是什么 蓝牙门锁原理图步骤详解 蓝牙智能门锁主要是通过手机开锁的方式来解锁 相比传统门锁 蓝牙门锁更加便捷 此外 还可以通过手机APP来实现门锁实时管理 访客管理 是符合智能家居时代对门锁要求的电子产品 那么蓝牙智能门锁
  • tesseract api C++使用例子

    转自 https code google com p tesseract ocr wiki APIExample APIExample API examples Updated Aug 12 2014 by theraysm gmail c
  • unsigned int 和 signed int 的区别

    unsigned int 和 signed int 的区别 对于 int 类型 默认是带有正负号的 也就是说 int 等同于 signed int signed int 等同于int 都能表示正负数 1 signed int 可以表示正整数
  • SQL优化之LIMIT语法, limit n,m 和 limit n有什么区别?

    在某些面试题中会遇到这样的问答或笔试题 limit 0 1 和 limit 1有什么区别 要准确回答这个问题就等深入明白limit一个参数和两个参数的本质区别 limit n m 中的第一次参数n表示的游标的偏移量 初始值为0 第二个参数m
  • Codeforces Round #367 (Div. 2)【贪心、差分、DP、字典树、二维链表】

    Codeforces Round 367 Div 2 A Beru taxi 就是问 我们知道一个点 从其他点到它的最少花费的时间是多少 include
  • 三次握手,四次挥手白话文

    三次握手和四次挥手是TCP协议中用于建立和断开连接的过程 三次握手 Three way Handshake 客户端向服务器发送一个SYN 同步 包 其中包含一个随机的初始序列号 表示客户端请求建立连接 服务器收到客户端的SYN包后 向客户端
  • 2022互联网精英副业指南,看到程序员的我笑了~

    不得不说 互联网人收入高 如果你以为互联网人收入高是因为工资高 年终奖丰厚 那你就错了 其实 还有一个原因是他们搞起了副业 副业千万条 闲鱼第一条 万万没想到的是 互联网人在闲鱼上赚钱也与众不同 甚至都一个比一个拼 https mmbiz
  • 【Java 集合 & 数据结构】优先队列 PriorityQueue

    优先队列 PriorityQueue 一 概述 二 结构 三 解析 1 核心属性 2 核心方法 offer 方法 入队列 poll 方法 出队列 peek 方法 队头元素 最小元 四 特点 优点 缺点 一 概述 优先队列 PriorityQ
  • Android String字符串截取方法总结

    Android String字符串截取方法总结 指定字符 截取字符串 返回字符串数组 String str abcd efg 123456 hijk 345 String strs str split 指定索引号 截取字符串 将字符串从索引
  • 服务器上创建Python虚拟环境

    应用场景 不同的项目 或者同一项目的不同版本 需要安装不同的Python解释器和依赖库 对于有python版本依赖的程序来说 为了安全可靠的管理环境 需要创建不同版本的 独立 隔离 的虚拟环境 virtualenv 是一个创建隔绝的Pyth
  • Java设计与实现“秒杀”活动之抢粽子【完整版】

    五月榴花妖艳烘 绿杨带雨垂垂重 五月新丝缠角粽 金盘送 生绡画扇盘双凤 正是浴兰时节动 正值端午佳节 实习公司也是例行放假三天以及给每一位员工发放了节日小礼品 过完端午又将迎来618活动专场 秒杀抢单活动也是此起彼伏 从而产生刺激性消费 由
  • 使用HiBurn烧录鸿蒙.bin文件到Hi3861开发板

    使用HiBurn烧录鸿蒙 bin文件到Hi3861开发板 鸿蒙官方文档的 Hi3861开发板第一个示例程序 中描述了 如何使用DevEco Device Tool工具烧录二进制文件到Hi3861开发板 本文将介绍如何使用HiBurn工具烧录
  • 网站服务器放本地还是云上,服务器放本地还是云上安全

    服务器放本地还是云上安全 内容精选 换一换 在弹性云服务器上安装完成后输入公网IP 无法连接目的虚拟机 端口无法访问工具 源端网络未连通目的端 目的端安全组未开放8084端口 目的端网络ACL禁用了8084端口 登录源端服务器后 在源端服务
  • Leetcode 计算质数 -- 埃氏筛、线性筛解析

    0 题目描述 leetcode原题链接 204 计数质数 1 埃氏筛 很直观的思路是我们枚举每个数判断其是不是质数 枚举没有考虑到数与数的关联性 因此难以再继续优化时间复杂度 介绍一个常见的算法 该算法由希腊数学家厄拉多塞 Eratosth