class 041 最大公约数、同余原理

1. 辗转相除法
对下面的证明过程有什么问题和怀疑的直接随便找两个数字自己写一遍就行了.
1.1 利用辗转相除法计算最大公约数
直接记忆这段代码公式就行了(具体的证明过程直接去看左程云老师写的就行了).
public static long gcd(long a, long b) { // Greatest Common Divisor,GCD return b == 0 ? a : gcd(b, a % b); // 需要注意的是 a > b
}
这个的时间复杂度是:log(n) ^ 3. 已经足够好了.
1.2 利用辗转相除法计算最小公倍数
也是直接记忆就行, 证明更简单, 不用说
public static long lcm(long a, long b) { // Least Common Multiple,LCM return (long) a / gcd(a, b) * b;
}
2. 题目练习:第 n 个神奇的数字
2.1 题目描述
https://leetcode.cn/problems/nth-magical-number/

2.2 逻辑实现
这里我们会利用“二份答案法”和“容斥原理”, 但是都是非常简单的情况, 不用系统了解学习也能理解.
首先我们假设有 a = 2, b = 3 这两个数字, 我们要找第 100 个(n = 100)神奇的数字, 那么这个神奇的数字一定在 [1, n * min(a, b)] 之间, 因为:我们假设没有 b 这个数字, 那么第一百个神奇的数字肯定是 200, 此时有了 b = 3 这个数字了, 比如 1, 2, 3, 4, 5, 6, 7, 8, 9, 10. 原来 [1, 10] 之间只有 2, 4, 6, 8, 10 这 5 个神奇的数字, 那么有了 3 之后, 至少新增加了一个 3, 那么第一百个神奇的数字肯定在 [1, 200] 之间了.
神奇的数字怎么求解:a = 2, b = 3. 在 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 这几个数字中.
- 我们先找能整除
a的数字有几个:x / a个2, 4, 6, 8, 10. - 然后找能整除
b的数字有几个:x / b个3, 6, 9. - 肯定是有重复的值:比如说:
6. - 因为一个数字只能是一个神奇的数字, 所以重复的情况是:
x / lcm(a, b).(这个函数之前讲解过). - 最后得到的是神奇的数字的个数为:
x / a + x / b - x / lcm(a, b).
然后我们假设有一个 f(x) 函数, 这个函数能判断出来 <= x 的范围内, 有几个神奇的数字, 我们假设要在 [1, 200] 之间找第 n 个神奇的数字, 先是 100, 若是发现不够(< n), 就去右边找:150, 若是够了, 就在 [100, 150] 之间找到尽量小的的位置. 比如说到了 140 够了, 139 不够, 那么说明 140 肯定就是我们需要的第 n 个有效数字了.
具体例子:下面这个例子中, 答案是 8.

2.3 代码实例
该有的注释我都写到了代码中.
Code02_NthMagicalNumber { public static void main(String[] args) { int n = 3; int a = 4; int b = 6; System.out.println(nthMagicalNumber(3, 4, 6)); } public static int nthMagicalNumber(int n, int a, int b) { long lcm = lcm(a, b); // 先找到a, b的最小公倍数. long ans = 0; // l = 0 // r = (long) n * Math.min(a, b) // l......r long l = 0, r = (long) n * Math.min(a, b), m = 0; while (l <= r) { m = (l + r) / 2; // 1....m // 这样写的原因是有可能在其中一个比较大的范围上也是和答案一样的. // 比如上面n = 3, a = 4, b = 6 的情况下, // 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12. // 在这几个数字的范围中, 8 是第三个神奇的数字, 但是 9 不是一个神奇数字, 但是当 m == 9 的时候 // m / a + m / b - m / lcm == n, 所以最后会返回 9, 这是不对的. // 所以要继续往下走. // 不能修改为这样的代码:
// if (m / a + m / b - m / lcm > n) {
// r = m - 1;
// } else if (m / a + m / b - m / lcm < n){
// l = m + 1;
// } else {
// ans = m; // 此时遇到对应的值就返回了, 按照上面的例子, 最后返回的是 9, 但是正确答案是 8,
// break;
// }if (m / a + m / b - m / lcm >= n) { ans = m; r = m - 1; } else { l = m + 1; } } return (int) (ans % 1000000007); } public static long gcd(long a, long b) { return b == 0 ? a : gcd(b, a % b); } public static long lcm(long a, long b) { return (long) a / gcd(a, b) * b; } }
3. 同余原理 (加法, 减法, 乘法)
假设我们现在面临一个情况:我们得到的值已经远远超出 long 类型能表示的范围, 若是用这个数字进行运算, 这样会大大增加时间复杂度. 那么我们如何规避这样的情况, 这就用到了同余原理.\
同余原理解决两个问题:
真实结果 % mod == ?如何保证?是对的.- 保证
+, -, * /运算的常数时间.
注意:我们平时在进行加减乘除运算的时候, 无论是 int(32位)还是long(64位) 类型的, 都默认的时间复杂度是 O(1).
但是若是 k位 数字的 +, - 时间复杂度是:O(k), * / 的时间复杂度是:O(k^2).
除法的同余原理需要用到逆元, 比较麻烦, 会放到后面.
3.1 加法的同余原理
比如 (a + b) + (c + d) = ans,我们假设 a + b == ans1, c + d = ans2, ans1 + ans2 == ans3.
那么:ans % mod == ans3 % mod, ans1 % mod + ans2 % mod == ans % mod.
只需要记住就行了, 自己可以尝试带入几个具体的案例
3.2 乘法的同余原理
乘法的同余原理和加法是一样的.
3.3 减法的同余原理
首先, 我们需要先明确一个前提, 我们接受的余数只能是非负数.
举一个例子:(75 - 17) % 7 == 58 % 7 == 2 那么利用同余原理:75 % 7 == 5, 17 % 7 == 3, 所以 (5 - 3) % 7 == 2, 这样最后的结果是对的, 但是其实会有问题, 有可能余数为负数.
比如:(72 - 18) % 7 == 5, 72 % 7 == 2, 18 % 7 == 4, 这样 (2 - 4) % 7 == -2, 我们刚刚说过, 是不能有负数的, 所以我们需要修改一下, 修改为:(2 - 4 + 7) % 7 == 5, 这样的最后结果才是正确的,
所以第一个例子中, 我们应该让其加上 mod 再取模:(5 - 3 + 7) % 7 == 2, 最后的结果还是正确的, 所以正确的方式应该是 (a - b) % mod == (a % mod - b % mod + m) % m.
3.4 验证同余原理的正确性(没有除法的同余原理)
// 加法、减法、乘法的同余原理
// 不包括除法,因为除法必须求逆元,后续课讲述
public class Code03_SameMod { // 为了测试 public static long random() { return (long) (Math.random() * Long.MAX_VALUE); } // 计算 ((a + b) * (c - d) + (a * c - b * d)) % mod 的非负结果 public static int f1(long a, long b, long c, long d, int mod) { BigInteger o1 = new BigInteger(String.valueOf(a)); // a BigInteger o2 = new BigInteger(String.valueOf(b)); // b BigInteger o3 = new BigInteger(String.valueOf(c)); // c BigInteger o4 = new BigInteger(String.valueOf(d)); // d BigInteger o5 = o1.add(o2); // a + b BigInteger o6 = o3.subtract(o4); // c - d BigInteger o7 = o1.multiply(o3); // a * c BigInteger o8 = o2.multiply(o4); // b * d BigInteger o9 = o5.multiply(o6); // (a + b) * (c - d) BigInteger o10 = o7.subtract(o8); // (a * c - b * d) BigInteger o11 = o9.add(o10); // ((a + b) * (c - d) + (a * c - b * d)) // ((a + b) * (c - d) + (a * c - b * d)) % mod BigInteger o12 = o11.mod(new BigInteger(String.valueOf(mod))); if (o12.signum() == -1) { // 如果是负数那么+mod返回 return o12.add(new BigInteger(String.valueOf(mod))).intValue(); } else { // 如果不是负数直接返回 return o12.intValue(); } } // 计算 ((a + b) * (c - d) + (a * c - b * d)) % mod 的非负结果 public static int f2(long a, long b, long c, long d, int mod) { int o1 = (int) (a % mod); // a int o2 = (int) (b % mod); // b int o3 = (int) (c % mod); // c int o4 = (int) (d % mod); // d int o5 = (o1 + o2) % mod; // a + b int o6 = (o3 - o4 + mod) % mod; // c - d int o7 = (int) (((long) o1 * o3) % mod); // a * c int o8 = (int) (((long) o2 * o4) % mod); // b * d int o9 = (int) (((long) o5 * o6) % mod); // (a + b) * (c - d) int o10 = (o7 - o8 + mod) % mod; // (a * c - b * d) int ans = (o9 + o10) % mod; // ((a + b) * (c - d) + (a * c - b * d)) % mod return ans; } public static void main(String[] args) { System.out.println("测试开始"); int testTime = 100000; int mod = 1000000007; for (int i = 0; i < testTime; i++) { long a = random(); long b = random(); long c = random(); long d = random(); if (f1(a, b, c, d, mod) != f2(a, b, c, d, mod)) { System.out.println("出错了!"); } } System.out.println("测试结束"); System.out.println("==="); long a = random(); long b = random(); long c = random(); long d = random(); System.out.println("a : " + a); System.out.println("b : " + b); System.out.println("c : " + c); System.out.println("d : " + d); System.out.println("==="); System.out.println("f1 : " + f1(a, b, c, d, mod)); System.out.println("f2 : " + f2(a, b, c, d, mod)); } }
相关文章:
class 041 最大公约数、同余原理
1. 辗转相除法 对下面的证明过程有什么问题和怀疑的直接随便找两个数字自己写一遍就行了. 1.1 利用辗转相除法计算最大公约数 直接记忆这段代码公式就行了(具体的证明过程直接去看左程云老师写的就行了). public static long gcd(long a, long b) { // Greatest Common Di…...
token的创建与解析,并配合拦截器使用
场景: 进行web应用开发时,往往需要对当前用户进行身份判断,此时可以使用token技术 1.导入依赖 <dependency><groupId>io.jsonwebtoken</groupId><artifactId>jjwt-impl</artifactId><scope>runtime<…...
Oracle 数据库历史备份数据恢复验证
Oracle ASM 管理的数据库历史备份数据恢复至单实例数据库 简介: 验证 ASM 管理的数据库的历史备份恢复至单实例数据库(主要目的在于验证历史备份是否可用的一次恢复演练) 一、恢复演练系统选择 根据数据库情况选择恢复测试的环境。 此次恢…...
【网络面积篇】TCP断开连接(笔记)
目录 一. 四次挥手 (1)过程描述 (2)为什么是四次挥手? 二、相关问题 1. 第一次挥手丢失了,会发生什么? 2. 第二次挥手丢失了,会发生什么? 补充:close …...
下跌多少才能涨回来?
文章目录 上涨下跌函数关系函数图形数学分析 上涨下跌函数关系 最近炒股很热,对于股票来说,有个很重要的参数涨跌幅,那么下跌多少才能涨回来?这个不需要太深的知识就可以计算出来,下跌和上涨不是等价的,下跌…...
【AAOS】【源码分析】CarSystemUI -- CarSystemBar
CarSystemBar不像Android手机那样固定的顶部“状态栏”和底部“导航栏”,而是将StatusBar和NavigationBar都统称为SystemBar,可以通过如下配置为每侧最多配置一个“系统栏”。 packages/apps/Car/SystemUI/res/values/config.xml<!-- Configure which system bars should …...
[供应链] 邀请招标
1.邀请招标定义 邀请招标(Invitation to Bid by Request) 也称为有限竞争性招标(limited Competitive Bidding)或选择性招标(Selected Bidding) 邀请招标的采购方式下,采购人(如政府机构、企业或其他组织)不是公开发布招标信息,而是根据供应商或承包商…...
VS2017+Qt5.12.9+CMake3.30.2编译VTK 9.2.0
一.准备工作 vs2017,QT,Cmake自行下载准备, VTK下载地址 1.官网下载 2.github下载 二.编译VTK源码 1.个人习惯创建以下目录,一个源码目录,Build为vs解决方案输出目录和编译输出以及中间生成文件目录 2.cmake基础…...
Java线程CPU占用过高如何排查?
使用ps命令查看java进程详细信息: ps aux | grep java使用top命令查看系统进程占用情况 top使用jstack命令导出Java进程的堆栈信息 jstack pid | grep tid -A 10 "java.lang.Thread.State" > gc.log找出占用cpu最高的线程id: top -Hp -d 1 …...
uniapp推送配置流程
Dcloud Dcloud注册账号 个推 了解即可 注册个推账号 ios配置流程 需配置含有推送的描述文件以及p8证书 配置推送证书 ios证书配置报技术错误(参数错误) TeamID-苹果开发者账号唯一的ID 安卓需配置多厂商 小米手机需要配置小米厂商 华为手机则需…...
qt QPicture详解
1、概述 QPicture类是Qt框架中的一个重要图形类,它主要用于记录和回放QPainter的绘图指令。这个类能够跨平台、无分辨率依赖地绘制图形,非常适合用于实现打印预览和图像操作等场景。QPicture可以将绘图操作序列化为一种独立于平台的格式,保存…...
ScheduledFuture Source Code Analysis
ScheduledFuture Overview is a delayed result-bearing action, 可以被cancel.通常是在ScheduledExecutorService里面schedule一个task, 然后ScheduledFuture是其task执行接受后的返回结果。 Code Analysis 继承于两个接口: extends Delayed, Future一些继承ch…...
【CSS】CSS 样式重置 (normalize.css 和 reset.css) 和通用样式配置
一般来说,每一个项目初始化阶段都需要样式重置和样式定制化。样式重置最常用的就是 normalize.css 和 reset.css 这两个文件。 他们的区别: Normalize.css更加注重保留有用的浏览器默认样式,仅修复浏览器之间的不一致性,适用于需…...
自动化机器学习(AutoML)详解
自动化机器学习(AutoML)详解 引言 在数据驱动的时代,将庞大的数据集转化为有价值的洞察和预测模型是众多组织的首要任务。然而,传统的机器学习流程复杂且耗时,包括数据预处理、特征选择、模型选择、调参以及模型评估…...
Linux: network:erspan0
文章目录 问题介绍生成时间:代码Linux引入后面NONE是怎么生成的问题 最近看到一个网卡是erspan0,不知道是做什么用的: # ip -d link show erspan0 7: erspan0@NONE: <BROADCAST,MULTICAST> mtu 1450 qdisc noop state DOWN mode DEFAULT group default qlen 10000...
第11课 计算思维
从二级考试开始,计算思维基本上以编程题的形式考察。为了避免一看就会,一写就废的情况,需要我们加强编程练习,把学到的知识,通过实战练习,变成自己的本领。 同一道题,一般会有多种解决方法&…...
ACL, ACL Workshop, ACL Findings 解释
ACL(Annual Conference of the Association for Computational Linguistics)是自然语言处理(NLP)领域的顶级会议之一,但确实有多个与ACL相关的会议和出版物,具体如下: ACL Main Conference&…...
《使用Gin框架构建分布式应用》阅读笔记:p272-p306
《用Gin框架构建分布式应用》学习第15天,p272-p306总结,总35页。 一、技术总结 1.TDD(test-driven development) 虽然经常看到TDD这个属于,从本人的工作经历看,实际开发中用得相对较少。 2.unitest(单元测试) go语言开发中&a…...
【搜索引擎】俄罗斯搜索引擎yandex
俄罗斯搜索引擎yandex 1997年,俄罗斯搜索引擎Yandex(俄语意为:语言目录)首次上线,已发展成为全球第四大搜索引擎和第二大非英语搜索引擎 https://yandex.com/...
加密源代码|html代码如何加密保护?3分钟学会4种源代码加密妙招,代码人必看
你是否曾担心过自己的源代码被轻易复制或篡改? 在这个开源和共享盛行的时代,如何加密源代码,成为了每个开发者不得不面对的问题。 古人云:“工欲善其事,必先利其器。”今天,我们就来探讨一下如何加密保护你…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻
在如今就业市场竞争日益激烈的背景下,越来越多的求职者将目光投向了日本及中日双语岗位。但是,一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧?面对生疏的日语交流环境,即便提前恶补了…...
R语言AI模型部署方案:精准离线运行详解
R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...
【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
聊聊 Pulsar:Producer 源码解析
一、前言 Apache Pulsar 是一个企业级的开源分布式消息传递平台,以其高性能、可扩展性和存储计算分离架构在消息队列和流处理领域独树一帜。在 Pulsar 的核心架构中,Producer(生产者) 是连接客户端应用与消息队列的第一步。生产者…...
让AI看见世界:MCP协议与服务器的工作原理
让AI看见世界:MCP协议与服务器的工作原理 MCP(Model Context Protocol)是一种创新的通信协议,旨在让大型语言模型能够安全、高效地与外部资源进行交互。在AI技术快速发展的今天,MCP正成为连接AI与现实世界的重要桥梁。…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
MinIO Docker 部署:仅开放一个端口
MinIO Docker 部署:仅开放一个端口 在实际的服务器部署中,出于安全和管理的考虑,我们可能只能开放一个端口。MinIO 是一个高性能的对象存储服务,支持 Docker 部署,但默认情况下它需要两个端口:一个是 API 端口(用于存储和访问数据),另一个是控制台端口(用于管理界面…...
