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种源代码加密妙招,代码人必看
你是否曾担心过自己的源代码被轻易复制或篡改? 在这个开源和共享盛行的时代,如何加密源代码,成为了每个开发者不得不面对的问题。 古人云:“工欲善其事,必先利其器。”今天,我们就来探讨一下如何加密保护你…...
用了大半年的免费云服务器,分享真实体验
最近一直在用阿贝云的免费云服务器和免费虚拟主机,整体体验非常不错。服务器性能稳定,响应速度快,完全能满足个人建站、学习测试的需求,而且操作简单,新手也能快速上手。免费虚拟主机的空间足够,搭建个人博…...
基于西门子S7-200 PLC与组态王技术的变频恒压供水控制系统设计与实物制作——软硬件设计详解
基于西门子S7-200 PLC和组态王小区变频恒压供水控制系统的设计,可制作对应实物,软硬件设计今天,我决定深入研究一个自动化控制领域中的典型项目:基于西门子S7-200 PLC和组态王软件的小区变频恒压供水控制系统。这个项目听起来有点…...
Linux内核设计哲学:你我承载力的艺术(续)
第七部:设备驱动——与不完美的世界和解7.1 你不是主人,你是仆人设备驱动是内核中最“卑微”的组件。它不和用户直接打交道,不参与核心决策,甚至不拥有任何资源。它只是硬件的翻译官——把内核的标准请求翻译成硬件能懂的指令&…...
本科论文知网AI率高的原因和解决方法全在这里
知网AIGC检测出来AI率高,很多同学第一反应是"我没有全程用AI写啊,为什么这么高?"这个问题确实需要好好解释一下——知网检测到的AI率高,未必是因为你完全靠AI写的。 知网AIGC检测是怎么工作的 知网的AIGC检测系统会分…...
Spring AI 助力 Java 开发者构建全功能 AI 智能体
【导语:随着人工智能的迅速发展,Java 开发者在将 AI 能力集成到基于 Spring 的应用程序方面选择有限。Spring AI 的出现改变了这一局面,本文详细介绍了如何使用 Spring AI 构建基于 Java 的全功能 AI 智能体。】Spring AI 打破 Java 集成 AI …...
2025最权威的降重复率方案实际效果
Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 从多个方面着手,才能降低文本的AIGC检测率。最先要留意语言自然度,使…...
揭秘AI教材写作:掌握这些技巧,用AI写教材低查重不是梦
编写教材的过程,总是让我踩到“慢节奏”的不少雷区。尽管框架和材料已经准备齐全,却在内容创作上遭遇阻碍——有时候一句话反复修改半个小时,心里始终觉得没说到点子上;而章节之间的衔接,绞尽脑汁也难以找到合适的表达…...
当HTTPS上传太慢时,我是如何用Minio Java SDK在后端搞定大文件分片上传的
HTTPS环境下大文件上传性能优化:基于Minio Java SDK的后端分片方案实战 最近在重构一个医疗影像存储系统时,我们遇到了一个典型的技术瓶颈:当用户通过HTTPS协议上传平均500MB的DICOM文件时,上传成功率不足60%,平均耗时…...
告别手动压缩!用Python的shutil.make_archive()自动备份你的项目文件
告别手动压缩!用Python的shutil.make_archive()自动备份你的项目文件 深夜赶项目时,你是否经历过这样的崩溃瞬间——修改了三天的重要代码突然消失,而上次备份还是一周前的手动压缩包?作为开发者,我们常陷入"明天…...
重构macOS滚动体验:Scroll Reverser的跨设备解决方案
重构macOS滚动体验:Scroll Reverser的跨设备解决方案 【免费下载链接】Scroll-Reverser Per-device scrolling prefs on macOS. 项目地址: https://gitcode.com/gh_mirrors/sc/Scroll-Reverser 破解多设备滚动的混乱困局 当设计师小李同时连接数位板和鼠标工…...
