每天一道算法练习题--Day22 第一章 --算法专题 --- ----------最大公约数

news/2024/10/30 13:31:51/

关于最大公约数有专门的研究。 而在 LeetCode 中虽然没有直接让你求解最大公约数的题目。但是却有一些间接需要你求解最大公约数的题目。

在这里插入图片描述

如何求最大公约数?

定义法

def GCD(a: int, b: int) -> int:smaller = min(a, b)while smaller:if a % smaller == 0 and b % smaller == 0:return smallersmaller -= 1

复杂度分析

  • 时间复杂度:最好的情况是执行一次循环体,最坏的情况是循环到 smaller 为 1,因此总的时间复杂度为 O ( N ) O(N) O(N),其中 N 为 a 和 b 中较小的数。
  • 空间复杂度: O ( 1 ) O(1) O(1)

辗转相除法

如果我们需要计算 a 和 b 的最大公约数,运用辗转相除法的话。
首先,我们先计算出 a 除以 b 的余数 c,把问题转化成求出 b 和 c 的最大公约数;
然后计算出 b 除以 c 的余数 d,把问题转化成求出 c 和 d 的最大公约数;
再然后计算出 c 除以 d 的余数 e,把问题转化成求出 d 和 e 的最大公约数。

以此类推,逐渐把两个较大整数之间的运算转化为两个较小整数之间的运算,直到两个数可以整除为止。

def GCD(a: int, b: int) -> int:return a if b == 0 else GCD(b, a % b)

在这里插入图片描述

更相减损术

辗转相除法如果 a 和 b 都很大的时候,a % b 性能会较低。在中国,《九章算术》中提到了一种类似辗转相减法的 更相减损术。它的原理是:两个正整数 a 和 b(a>b),它们的最大公约数等于 a-b 的差值 c 和较小数 b 的最大公约数

def GCD(a: int, b: int) -> int:if a == b:return aif a < b:return GCD(b - a, a)return GCD(a - b, b)

上面的代码会报栈溢出。原因在于如果 a 和 b 相差比较大的话,递归次数会明显增加,要比辗转相除法递归深度增加很多,最坏时间复杂度为 O(max(a, b)))。这个时候我们可以将辗转相除法和更相减损术做一个结合,从而在各种情况都可以获得较好的性能。

形象化解释

下面我们对上面的过程进行一个表形象地讲解,实际上这也是教材里面的讲解方式,我只是照搬过来,增加一下自己的理解罢了。我们来通过一个例子来讲解:

假如我们有一块 1680 米 * 640 米 的土地,我们希望将其分成若干正方形的土地,且我们想让正方形土地的边长尽可能大,我们应该如何设计算法呢?

实际上这正是一个最大公约数的应用场景,我们的目标就是求解 1680 和 640 的最大公约数。

在这里插入图片描述
将 1680 米 * 640 米 的土地分割,相当于对将 400 米 * 640 米 的土地进行分割。 为什么呢? 假如 400 米 * 640 米分割的正方形边长为 x,那么有 640 % x == 0,那么肯定也满足剩下的两块 640 米 * 640 米的。

在这里插入图片描述
在这里插入图片描述

实例解析

题目描述

给你三个数字 a,b,c,你需要找到第 n 个(n 从 0 开始)有序序列的值,
这个有序序列是由 a,b,c 的整数倍构成的。比如:
n = 8
a = 2
b = 5
c = 7由于 257 构成的整数倍构成的有序序列为 [1, 2, 4, 5, 6, 7, 8, 10, 12, ...],因此我们需要返回 12。注意:我们约定,有序序列的第一个永远是 1

大家可以通过 这个网站 在线验证。
一个简单的思路是使用堆来做,唯一需要注意的是去重,我们可以使用一个哈希表来记录出现过的数字,以达到去重的目的。

代码:

ss Solution:def solve(self, n, a, b, c):seen = set()h = [(a, a, 1), (b, b, 1), (c, c, 1)]heapq.heapify(h)while True:cur, base, times = heapq.heappop(h)if cur not in seen:n -= 1seen.add(cur)if n == 0:return curheapq.heappush(h, (base * (times + 1), base, times + 1))

对于此解法不理解的可先看下我之前写的 几乎刷完了力扣所有的堆题,我发现了这些东西。。。(第二弹)

然而这种做法时间复杂度太高,有没有更好的做法呢?

实际上,我们可对搜索空间进行二分。首先思考一个问题,如果给定一个数字 x,那么有序序列中小于等于 x 的值有几个。
答案是 x // a + x // b + x // c 吗?

// 是地板除

可惜不是的。比如 a = 2, b = 4, n = 4,答案显然不是 4 // 2 + 4 // 4 = 3,而是 2。这里出错的原因在于 4 被计算了两次,一次是 2 ∗ 2 = 4 2 * 2 = 4 22=4,另一次是 4 ∗ 1 = 4 4 * 1 = 4 41=4

为了解决这个问题,我们可以通过集合论的知识。

一点点集合知识:

  • 如果把有序序列中小于等于 x 的可以被 x 整除,且是 a 的倍数的值构成的集合为 SA,集合大小为 A
  • 如果把有序序列中小于等于 x 的可以被 x 整除,且是 b 的倍数的值构成的集合为 SB,集合大小为 B
  • 如果把有序序列中小于等于 x 的可以被 x 整除,且是 c 的倍数的值构成的集合为 SC,集合大小为 C

那么最终的答案就是 SA ,SB,SC 构成的大的集合(需要去重)的中的数字的个数,也就是:
在这里插入图片描述
问题转化为 A 和 B 集合交集的个数如何求?

实际上, SA 和 SB 的交集个数就是 x // lcm(A, B),其中 lcm 为 A 和 B 的最小公倍数。而最小公倍数则可以通过最大公约数计算出来:

def lcm(x, y):return x * y // gcd(x, y)

接下来就是二分套路了,二分部分看不懂的建议看下我的二分专题。

代码(Python3):

class Solution:def solve(self, n, a, b, c):def gcd(x, y):if y == 0:return xreturn gcd(y, x % y)def lcm(x, y):return x * y // gcd(x, y)def possible(mid):return (mid // a + mid // b + mid // c - mid // lcm(a, b) - mid // lcm(b, c) - mid // lcm(a, c) + mid // lcm(a, lcm(b, c))) >= nl, r = 1, n * max(a, b, c)while l <= r:mid = (l + r) // 2if possible(mid):r = mid - 1else:l = mid + 1return l

在这里插入图片描述


http://www.ppmy.cn/news/63017.html

相关文章

基于Linux系统在线安装RabbitMQ

一、前言 二、Erlang下载安装 三、RabbitMQ下载安装 三、RabbitMQ Web界面管理 一、前言 本次安装使用的操作系统是Linux centOS7。 二、Erlang下载安装 在确定了RabbitMQ版本号后&#xff0c;先下载安装Erlang环境。下面演示操作过程&#xff1a; Erlang下载链接&#…

centOS7忘记登录密码该如何重新修改登录密码

文章目录 前言一、重新修改登录密码1.1、第一步1.2、第二步1.3、第三步1.4、第四步1.5、第五步1.6、第六步1.7、第七步1.8、第八步 前言 忘记密码并不可怕&#xff0c;只要学会方法&#xff0c;密码随时都可以找回。 一、重新修改登录密码 1.1、第一步 当打开centOS7之后忘记…

OpenCV中的图像处理3.7-3.8(五)边缘检测、图像金字塔

目录 3.7 边缘检测目标理论OpenCV中的Canny边缘检测其他资源练习 3.8 图像金字塔目标理论使用金字塔进行图像混合其他资源 翻译及二次校对&#xff1a;cvtutorials.com 编辑者&#xff1a;廿瓶鲸&#xff08;和鲸社区Siby团队成员&#xff09; 3.7 边缘检测 目标 在本章中&a…

【C++从0到王者】第二站:类和对象(中)构造函数与析构函数

文章目录 一、C的六个默认成员函数二、构造函数和析构函数1.构造函数①构造函数的概念②构造函数的特性 2.析构函数①析构函数的概念②析构函数的特性 3.构造函数的其他特性4.构造函数总结5.一些不写构造函数的样例6.析构函数的其他特性 一、C的六个默认成员函数 如果一个类中什…

通过SSH隧道安全消费Kafka数据

一.背景 由于我们有个业务在阿里云部署了Kafka&#xff0c;但是想直接在本地IDC机房服务器直接通过公网消费Kafka进行业务处理。这个本来也不是什么难事&#xff0c;阿里云把9092默认端口打开运行访问即可&#xff0c;也不不值得再写这篇博客了。 这个事情让人特别关注的一个主…

ELK single deployment

版本信息: apache-zookeeper-3.7.1-bin.tar.gzkafka_2.12-2.8.2.tgzkibana-7.12.0-linux-x86_64.tar.gzelasticsearch-7.12.0-linux-x86_64.tar.gzlogstash-7.9.2.tar.gz 一. zookeeper配置启动2181 前置条件: 1.安装jdk 2.添加默认配置文件zoo.cfg 二. 启动kafka9092 …

基本的使用套路

public enum IdCardTypeEnum implements ValueObject<IdCardTypeEnum> {居民身份证("0", "居民身份证"),护照("1", "护照"),军官证("2", "军官证"),驾照("3", "驾照"),出生证明("…

JavaScript class和继承的原理

&#xff08;对于不屈不挠的人来说&#xff0c;没有失败这回事。——俾斯麦&#xff09; class 相关链接 MDN链接 有关类的详细描述 关于构造函数&#xff0c;原型和原型链的说明 类的概述 类是用于创建对象的模板。他们用代码封装数据以处理该数据。JS 中的类建立在原型上…