数学(二)-- LeetCode[204] 计数质数

news2024/11/27 16:51:30

1 计数质数

1.1 题目描述

        给定整数 n ,返回 所有小于非负整数 n 的质数的数量 。

示例 1:
输入:n = 10
输出:4
解释:小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7 。

示例 2:
输入:n = 0
输出:0

示例 3:
输入:n = 1
输出:0

        题目链接:https://leetcode.cn/problems/count-primes

1.2 思路分析

方法一:枚举法(计算超时)

        很直观的思路是我们枚举每个数判断其是不是质数。

        考虑质数的定义:在大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的自然数。因此对于每个数 x x x,我们可以从小到大枚举 [ 2 , − 1 ] [2, −1] [2,1] 中的每个数 y y y,判断 y y y 是否为 x x x 的因数。但这样判断一个数是否为质数的时间复杂度最差情况下会到 O ( n ) O(n) O(n),无法通过所有测试数据。

        考虑到如果 y y y x x x 的因数,那么 x y \frac{x}{y} yx 也必然是 x x x 的因数,因此我们只要校验 y y y 或者 x y \frac{x}{y} yx 即可。而如果我们每次选择校验两者中的较小数,则不难发现较小数一定落在 [ 2 , ( x ) ] [2, \sqrt(x)] [2,( x)] 的区间中,因此我们只需要枚举 [ 2 , ( x ) ] [2, \sqrt(x)] [2,( x)] 中的所有数即可,这样单次检查的时间复杂度从 O ( n ) O(n) O(n) 降低至了 O ( ( n ) ) O(\sqrt(n)) O(( n))

示例代码:

from math import sqrt
class Solution:
    def countPrimes(self, n: int) -> int:
        def is_prime(n):
            for i in range(2, int(n**0.5)+1):
                if n % i == 0:
                    return False
            return True

        count = 0 if n < 2 else 1
        for i in range(2, n):
            if is_prime(i):
                count += 1
        return count

复杂度分析:

  • 时间复杂度: O ( n ( n ) ) O(n\sqrt(n)) O(n( n))。单个数检查的时间复杂度为 O ( ( n ) ) O(\sqrt(n)) O(( n)),一共要检查 O ( n ) O(n) O(n) 个数,因此总时间复杂度为: O ( n ( n ) ) O(n\sqrt(n)) O(n( n))
  • 空间复杂度: O ( 1 ) O(1) O(1)

        做进一步的优化,首先素数的判断,没必要去除以 [ 2 , ( x ) ] [2, \sqrt(x)] [2,( x)] 之间的所有数,寻找质数时,质数里除了2以外都是奇数,只需要遍历小于 n n n 的奇数即可,虽然提高程序运行效率,但依然超时,毕竟复杂度的数量级没降下来。

from math import sqrt
class Solution:
    def countPrimes(self, n: int) -> int:
        def is_prime(number):
            """优化对素数的判断"""
            if number == 2 or number == 3:
                return True
            if number % 2 == 0 or number % 3 == 0:
                return False
            for i in range(6, int(sqrt(number))+2, 6):  
                if number % (i-1) == 0 or number % (i+1) == 0:
                    return False
            return True

        count = 0 if n < 2 else 1
        for i in range(3, n, 2):    # 偶数除了2肯定不能是质数,只判断奇数
            if is_prime(i):
                count += 1
        return count

方法二:厄拉多塞筛算法

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

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

算法流程图:

一个素数的各个倍数,是一个差为此素数本身的等差数列。此为这个筛法和试除法不同的关键之处,后者是以素数来测试每个待测数能否被整除。

        下面以所有不超过100的素数为例,因为小于等于10的所有素数为2、3、5、7,所以依次删除2、3、5、7的倍数。

代码实现:

from math import sqrt
class Solution:
    def countPrimes(self, n: int) -> int:
        if n < 2: return 0                  # 不存在小于 2 的素数
        flag_list = [1 for _ in range(n)]
        flag_list[:2] = [0, 0]
        for i in range(2, int(n**0.5)+1):   #  遍历 i=2 到 根号 n
            if flag_list[i]:                 # 筛去 i 的倍数
                for j in range(i*i, n, i):
                    flag_list[j] = 0
        return sum(flag_list)

        这里在筛去 i i i 的倍数的时候,第一个数是 i × i i \times i i×i 而不是 i i i,这是因为对于所有 k × i k\times i k×i k < i k<i k<i,都在前面被筛过,故可以跳过这些数。return sum(flag_list) 这里列表中每个质数位的值均为 1 ,其余合数位的值均为 0 ,sum(flag_list) 的结果就是 n 以内质数的个数。

        进一步优化,第二个for循环是可以直接用列表的索引把质数的所有倍数所在的位置赋值为0。

from math import sqrt
class Solution:
    def countPrimes(self, n: int) -> int:
        if n < 2: return 0
        flag_list = [1 for _ in range(n)]
        flag_list[:2] = [0, 0]
        for i in range(2, int(n**0.5)+1):
            if flag_list[i]:
                flag_list[i*i:n:i] = [0] * ((n - i * i - 1) // i + 1)
        return sum(flag_list)

         flag_list[i*i:n:i] = [0] * ((n - i * i - 1) // i + 1) 指定步长参数,进行列表切片赋值,这里要计算好列表切片取出多少个值。

         温馨提示: 使用 python 时间和空间效率都较低,对于标记素数,可以采用 c++ 的 bitset,bitset 是以比特为单位标记的,会极大降低存储消耗。可以参考:运用比特表(Bitmap)算法对筛法进行内存优化

        小结: 厄拉多塞筛算法的核心就是要得到自然数 n 以内的全部质数,必须把不大于 根号n 的所有质数的倍数剔除,剩下的就是质数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/364515.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis学习之持久化(六)

这里写目录标题一、持久化简介1.1 持久化1.2 Redis持久化的两种形式二、RDB2.1 RDB概念2.2 save指令手动执行一次保存配置相关参数2.3 bgsave指令2.4 save配置自动执行2.5 RDB三种启动方式对比三、AOF3.1 AOF概念3.2 AOF执行策略3.3 AOF重写四、RDB和AOF区别2.1 RDB与AOF对比&a…

LQB04 蜂鸣器和继电器的操作。和代码

硬件图 编程实现 图中 &#xff0c;用Y5C控制ULN2003芯片。 所以要选通Y5C ; ULN2003芯片是个反相放大&#xff0c;IN口是1&#xff0c;OUT口出来是0&#xff1b;IN口是0&#xff0c;出来是1&#xff1b; 蜂鸣器和继电器&#xff0c;都是0点亮&#xff0c;发声&#xff1b;那…

macOS搭建Nexus 3.x为Maven的资源仓库服务器 Maven私服搭建

1 下载 Nexus 服务器安装包 目前的版本有 2.X 和 3.X &#xff0c;2.X 对 Maven 的支持更友好一点&#xff0c;3.X 的支持范围更广&#xff0c;支持 ruby 和 docker。但是 3.X 要求 JDK 的版本是1.8&#xff0c;而且貌似还需要做特殊配制。 官网地址&#xff1a;https://www.…

JVM笔记(7)—— Java对象创建的过程

一、对象创建的六种方式 1. new关键字 直接通过new关键字调用类的构造器创建 2. Class的newInstance()方法 通过类对象的newInstance()方法利用反射创建对象&#xff0c;只能调用权限为public的空参构造器&#xff0c;若对应类没有此构造器则会抛出编译时异常ClassNotFoundE…

redis基本入门| 怎么安装redis?什么的是redis?怎么使用?

目录 一、Redis下载与安装 二、基本概念 1.什么是Redis? 2.Redis端口多少&#xff1f; 3.Redis是单线程还是多线程&#xff1f; 4.Redis为什么单线程还这么快&#xff1f; 三、Redis的基本操作 四、Redis的五个基本类型 1.Redis-key 2.字符串 string 3.列表 list …

七大软件架构设计原则-读书笔记

7大原则 开闭原则&#xff08;Open-Closed Principle&#xff0c;OCP&#xff09; 指一个软件实体如类、模块和函数应该对扩展开放&#xff0c;对修改关闭。强调的是用抽象构建框架&#xff0c;用实现扩展细节&#xff0c;可以提高软件系统的可复用性及可维护性。开闭原则是面…

浅谈Redisson实现分布式锁对原理

1.Redisson简介 Redis 是最流行的 NoSQL 数据库解决方案之一&#xff0c;而 Java 是世界上最流行&#xff08;注意&#xff0c;我没有说“最好”&#xff09;的编程语言之一。虽然两者看起来很自然地在一起“工作”&#xff0c;但是要知道&#xff0c;Redis 其实并没有对 Java…

Win10开机输入密码后1分钟左右就蓝屏,一天出现10个不同蓝屏代码,如何解决

环境: DELL3480 Win10 专业版 问题描述: Win10 电脑开机输入密码后1分钟左右突然就蓝屏,一天出现10个不同蓝屏代码 1.BAD_SYSTEM_CONFIG_INFO bug 检查的值为 0x00000074。 此 bug 检查指示注册表中出现错误。 2.PAGE_FAULT_IN_NONPAGED_AREA bug 检查的值为 0x000…

修改redis的配置文件使得windows的图形界面客户端可以连接redis服务器

1. 安装 Redis 依赖 Redis 是基于 C语言编写的&#xff0c;因此首先需要安装 Redis 所需要的 gcc 依赖&#xff1a; yum install -y gcc tcl 2、上传安装文件 将下载好的 redis-6.2.7.tar.gz 安装包上传到虚拟机的任意目录&#xff08;一般推荐上传到 /usr/local/src目录&…

linux集群技术(三)--七层负载均衡-nginx

nginx特点nginx优势、缺点生产架构nginx 7层负载均衡语法示例nginx负载均衡算法测试案例生产案例 1.nginx特点 1. 功能强大,性能卓越,运行稳定。 2. 配置简单灵活。 3. 能够自动剔除工作不正常的后端服务器。 4. 上传文件使用异步模式。client---nginx---web1 web2 web3 lvs同…

uniapp: 基础开发官网文档

1、uniapp官网文档&#xff1a;https://uniapp.dcloud.net.cn/component/2、uView跨端UI组件库&#xff1a;http://v1.uviewui.com/components/intro.html3、lunch-request&#xff08;类似axios的请求库&#xff09;&#xff1a;https://www.quanzhan.co/luch-request/handboo…

图神经网络基础 Graph 图以及python实现

摘要&#xff1a; 本文将介绍图的基本知识、无向图、有向图、邻接矩阵 python实现&#xff1a;度、连通分量、强连通图、弱连通图、图直径、度中心性、特征向量中心性、中介中心性、连接中心性等基本概念。 python计算代码&#xff1a; 先安装依赖&#xff1a; pip install n…

Java-模块化

模块的基本使用 模块使用步骤 创建模块&#xff08;创建模块&#xff0c;创建包&#xff0c;创建类&#xff0c;定义方法&#xff09; -创建两个模块myOne,myTwo在模块的src目录下创建module-info.java的描述性文件&#xff0c;该文件专门定义模块名&#xff0c;访问权限&#…

一文读懂函数编程及其工作原理

微软MVP实验室研究员 马洪喜-微软 MVP 19年研发经验 云计算咨询顾问专家 容器云及基础架构云技术专家 DevOps 及微服务咨询专家 什么是函数编程 我先用通俗的大白话给大家解释一下函数(Functions, Function as a Service, FaaS)的几个要点&#xff0c;这样看后面示例时才不…

跑步带的耳机选择啥样的好、推荐几款跑步专用耳机

我是个比较喜欢运动的人&#xff0c;每天下班都会在小区湖边跑步健身&#xff0c;每次跑步要是少了耳机&#xff0c;那可没什么兴趣跑了&#xff0c;喜欢跑步的时候对着音乐的节奏跑&#xff0c;所以我的耳机基本上是用的比较紧实不易掉落的无线耳机。接下来我来为大家介绍下我…

ChatGPT 可收费的那种产品该如何实现?一点尝试

导读|时隔两个月&#xff0c;勇哥终于把chatGPT生成SQL的功能发布上线了&#xff0c;支持统计分析查询、创建表、数据生成等多种全面的SQL DDL生成能力&#xff0c;本文就和大家聊聊相关功能的使用和背后实现逻辑&#xff0c;并希望相关功能能帮助大家在工作中提升一定的工作效…

手把手搭建springboot项目05-springboot整合Redis及其业务场景

目录前言一、食用步骤1.1 安装步骤1.1.1 客户端安装1.2 添加依赖1.3 修改配置1.4 项目使用1.5 序列化二、应用场景2.1 缓存2.2.分布式锁2.2.1 redis实现2.2.2 使用Redisson 作为分布式锁2.3 全局ID、计数器、限流2.4 购物车2.5 消息队列 (List)2.6 点赞、签到、打卡 (Set)2.7 筛…

如何弄小程序?公司企业可以这样做小程序

公司企业现在对于小程序的需求已经是刚需了&#xff0c;即使已经有官网的情况下&#xff0c;也会考虑再弄一个小程序来做小程序官网。那么公司企业如何弄小程序呢&#xff1f;下面跟大家说说方法。 流程一、找小程序服务商 由于一些公司企业并不像现在的互联网公司企业那样有…

考试系统 (springboot+vue前后端分离)

系统图片 下载链接 地址&#xff1a; http://www.gxcode.top/code 介绍 一款多角色在线培训考试系统&#xff0c;系统集成了用户管理、角色管理、部门管理、题库管理、试题管理、试题导入导出、考试管理、在线考试、错题训练等功能&#xff0c;考试流程完善。 技术栈 Spr…

Java8中@Contended和伪共享

Java8引入了Contented这个新的注解来减少伪共享(False Sharing)的发生。 sun.misc.Contended注解是被设计用来解决伪共享问题的 文章目录1.缓存行2.伪共享(False Sharing)2.1 CPU的缓存机制3.填充(Padding)4.Contended方式4.总结1.缓存行 CPU读取内存数据时并非一次只读一个字…