求两个数之间的最小公约数

目录

前言

方法:求两个数之间的最小公约数

1.欧几里得算法

2.枚举法

3.公共因子积

4.更相减损术

5.Stein算法

解题:在链表中插入最大公约数

总结


前言

今天刷每日一题:2807. 在链表中插入最大公约数 - 力扣(LeetCode),就在想怎么求两个数之间的最小公约数,然后发现求两个数的最大公约数(五种方法)-CSDN博客

这个博客总结的得很好但也有点自己的想法,于是记录下来,我也是真的超爱写博客了。


方法:求两个数之间的最小公约数

1.欧几里得算法

欧几里德算法是用来求两个正整数最大公约数的算法。是由古希腊数学家欧几里德在其著作《The Elements》中最早描述了这种算法,所以被命名为欧几里德算法。

大致过程如下:

1997 / 615 = 3 (余 152)
615 / 152 = 4 (余7)
152 / 7 = 21(余5)
7 / 5 = 1 (余2)
5 / 2 = 2 (余1)
2 / 1 = 2 (余0)
1997 % 615 = 152
615 % 152 = 7
152 % 7 = 5
7 % 5 = 2
5 % 2 = 1
2 % 1 = 0

至此,最大公约数为1。
以除数和余数反复做除法运算,当余数为 0 时,取当前算式除数为最大公约数,所以就得出了 1997 和 615 的最大公约数 1。

观察数就可以得出其算法实现是:

/*** 利用 欧几里得算法 求 m 和 n 的最大公约数** @param m m* @param n n* @return m 和 n 的最大公约数*/
public int gcd(int m, int n) {while (n != 0) {int temp = m % n;m = n;n = temp;}return m;
}

需要注意的是,在参考的博客说m>=n是此算法的必要条件,其实不然,因为就算m<n,经过一次计算后也会使得m>=n,这是算法使然,只是m<n时,这个算法的第一次会失效,重排序去了。因此,m,n可以任意输入

2.枚举法

给出 m 和 n,首先求出 m 和 n 的最小值赋值给临时变量 t,然后对 t 依次递减,如果 m 除以 t 的余数为 0,并且 n 除以 t 的余数为 0,此时 t 就是 m 和 n 的最大公约数。

这里依然以刚刚的1997和615为例,如果按照枚举法去计算,代码就从t=615依次执行到2,(615-2+1)次,显然效率极低

算法实现如下:

/*** 通过遍历的方式来求 m 和 n 的最大公约数** @param m m* @param n n* @return m 和 n 的最大公约数*/
public int gcd2(int m, int n) {// 第一步:将 min{m, n}的值赋值给 tint t = Math.min(m, n);for (; t >= 2; t--) {// 第二步和第三步,如果 m 除以 t 余数为 0 并且 n 除以 t 余数为 0,直接返回 tif (m % t == 0 && n % t == 0) {return t;}// 否则 t--,返回第二步和第三步}return 1;
}

3.公共因子积

计算两个数字的公共因子积。

第一步:找出 m 的全部质因数
第二步:找出 n 的全部质因数
第三步:从第一步和第二步求得的质因数分解式中找出所有的公因数(如果p是一个公因数,而且在m和n的质因数分解式分别出现过pm和pn 次,那么应该将p重复min{pm, pn}次).
第四步:将第三步中找到的质因数相乘,其结果作为给定数字的最大公约数.

这个太太太繁琐了,完全没必要。看看就得了。

    public int gcd3(int m, int n) {Instant start = Instant.now();int[] marr = factorArr(m);int[] narr = factorArr(n);// ---------------------------------------------------------------------// 处理两个数组的公共元素// ---------------------------------------------------------------------// 求出 marr 和 narr 的最大值Map<Integer, Integer> mMap = new HashMap<>(marr.length);Map<Integer, Integer> nMap = new HashMap<>(narr.length);// 处理 marrfor (int i = 0; i < marr.length; ) {int index = i;int count = 0;while (index < marr.length && marr[index] == marr[i]) {count++;index++;}mMap.put(marr[i], count);i = index;}// 处理 narrfor (int i = 0; i < narr.length; ) {int index = i;int count = 0;while (index < narr.length && narr[index] == narr[i]) {count++;index++;}nMap.put(narr[i], count);i = index;}int sum = 1;// 可以遍历任意一个 map ,来找出公共元素的个数for (Map.Entry<Integer, Integer> entry : mMap.entrySet()) {// 取出 valueint value = entry.getKey();// 取出个数int count = entry.getValue();// 取出另外一个集合中对应 value 值出现的次数int anotherCount = nMap.get(value) == null ? 0 : nMap.get(value);// 两个因子数组相同因子出现次数的较小值int minCount = Math.min(count, anotherCount);sum *= minCount * value == 0 ? 1 : Math.pow(value, minCount);}return sum;}/*** 返回 value 的全部因子,以数组的形式返回** @param value value 值* @return value 的全部因子,以数组的形式返回*/private int[] factorArr(int value) {List<Integer> list = new ArrayList<>();for (int i = 2; i <= Math.sqrt(value); i++) {if (value % i == 0) {list.add(i);value /= i;i--;}}return list.stream().mapToInt(Integer::valueOf).toArray();}

4.更相减损术

  • 第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
  • 第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
  • 则第一步中约掉的若干个2的积与第二步中等数的乘积就是所求的最大公约数。
/*** 使用更相减损法求 m 和 n 的最大公约数** @param m 数字 m* @param n 数字 n* @return m 和 n 的最大公约数*/
public int gcd4(int m, int n) {// 两个数字不相等时,继续进行运算,while (m != n) {if (m > n) m -= n;else n -= m;}return m;
}

这个也很简洁,但也没有取余来得高效。

5.Stein算法

欧几里德算法是计算两个数最大公约数的传统算法,无论从理论还是从实际效率上都是很好的。但是却有一个致命的缺陷,这个缺陷在素数比较小的时候一般是感觉不到的,只有在大素数时才会显现出来:一般实际应用中的整数很少会超过64位(当然现在已经允许128位了),对于这样的整数,计算两个数之间的模是很简单的。对于字长为32位的平台,计算两个不超过32位的整数的模,只需要一个指令周期,而计算64位以下的整数模,也不过几个周期而已。但是对于更大的素数,这样的计算过程就不得不由用户来设计,为了计算两个超过64位的整数的模,用户也许不得不采用类似于多位数除法手算过程中的试商法,这个过程不但复杂,而且消耗了很多CPU时间。对于现代密码算法,要求计算128位以上的素数的情况比比皆是,比如说RSA加密算法至少要求500bit密钥长度,设计这样的程序迫切希望能够抛弃除法和取模。
Stein算法很好的解决了欧几里德算法中的这个缺陷,Stein算法只有整数的移位和加减法。

讲实话,这个我还没搞得太懂,需要之后好好看看,对于较大数字用这个。

递归:

/*** 求两个正整数的最大公因数* <p>* 结合辗转相除法和更相减损法的优势以及移位运算* * 结合辗转相除法和更相减损法的优势以及移位运算* 对 m 和 n 分四种情况* 如果 m 为偶数 n 为偶数, gcd(m, n) = gcd(m >> 1, n >> 1) << 1;* 如果 m 为偶数 n 为奇数, gcd(m, n) = gcd(m >> 1, n);* 如果 m 为奇数 n 为偶数, gcd(m, n) = gcd(m, n >> 1);* 如果 m 为奇数 n 为奇数, gcd(m, n) = gcd(n, m - n);** @param m 数字 m* @param n 数字 n* @return 返回 m 和 n 的最大公因数*/
public int gcd5(int m, int n) {// 这个地方也是利用到更相减损术if (m == n) {return m;}// 为了保证较大的数始终在前面,减少了代码if (n > m) {return gcd5(n, m);} else {if (((m & 1) == 0) && ((n & 1) == 0)) {// 两数都是偶数return gcd5(m >> 1, n >> 1) << 1;} else if ((m & 1) == 0 && (n & 1) != 0) {// m为偶数,n为奇数return gcd5(m >> 1, n);} else if ((m & 1) != 0 && (n & 1) == 0) {// m为奇数,n为偶数return gcd5(m, n >> 1);} else {// 当两个数都为奇数时,应用更相减损法// 这个位置利用到了更相减损术return gcd5(n, m - n);}}
}

非递归:

/*** Stein 算法的非递归实现* * @param m m* @param n n* @return  m 和 n 的最大公因子*/
public int steinGCD(int m, int n) {int count = 0;if (m < n) return steinGCD(n , m);while ((m & 1) == 0 && (n & 1) == 0) {count++;m >>= 1;n >>= 1;}while (m != n) {while ((m & 1) == 0) m >>= 1;while ((n & 1) == 0) n >>= 1;if (m < n) {m ^= n;n ^= m;m ^= n;}// 进行一次更相减损术int temp = m - n;m = n;n = temp;}return m << count;
}

解题:在链表中插入最大公约数

 这里链表插入删除的逻辑还是很好做的,要注意的是这个while的条件:current != null && current.next != null

这里的gcd函数就是用来求最小公约数的(刚说的几种都可试试)。

/*** Definition for singly-linked list.* public class ListNode {*     int val;*     ListNode next;*     ListNode() {}*     ListNode(int val) { this.val = val; }*     ListNode(int val, ListNode next) { this.val = val; this.next = next; }* }*/
class Solution {public ListNode insertGreatestCommonDivisors(ListNode head) {ListNode current = head;while (current != null && current.next != null) {ListNode next = current.next;int gcdValue = gcd(current.val, next.val);// 在相邻节点之间插入新节点ListNode newNode = new ListNode(gcdValue);newNode.next = next;current.next = newNode;// 更新 current 指针到下一个相邻节点current = next;}return head;}/*** 计算两个数的最大公约数** @param a 第一个数* @param b 第二个数* @return 最大公约数*/private int gcd(int a, int b) {while (b != 0) {int temp = a % b;a = b;b = temp;}return a;}
}


总结

 当数较小时(不超过64位),用欧几里得算法(取余)或者更相减损术;当数太大时,用stein算法,此算法只有整数的移位和加减法。

加油加油,今天熬熬夜。

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

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

相关文章

基于X86的助力智慧船载监控系统

船载综合监控系统结合雷达、AIS、CCTV、GPS等探测技术&#xff0c;以及高度融合的实时态势与认知技术&#xff0c;实现对本船以及范围内船舶的有效监控&#xff0c;延伸岸基监控中心监管范围&#xff0c;保障行船安全&#xff0c;为船舶安全管理部门实现岸基可控的数据通信和动…

第 121 场 LeetCode 双周赛题解

A 大于等于顺序前缀和的最小缺失整数 模拟&#xff1a;先求最长顺序前缀的和 s s s &#xff0c;然后从 s s s 开始找没有出现在 n u m s nums nums 中的最小整数 class Solution { public:int missingInteger(vector<int> &nums) {unordered_set<int> vis(…

如何批量自定义视频画面尺寸

在视频制作和编辑过程中&#xff0c;对于视频画面尺寸的调整是一项常见的需求。有时候&#xff0c;为了适应不同的播放平台或满足特定的展示需求&#xff0c;我们需要对视频尺寸进行批量调整。那么&#xff0c;如何实现批量自定义视频画面尺寸呢&#xff1f;本文将为您揭示这一…

LLM之RAG实战(十三)| 利用MongoDB矢量搜索实现RAG高级检索

想象一下&#xff0c;你是一名侦探&#xff0c;身处庞大的信息世界&#xff0c;试图在堆积如山的数据中找到隐藏的一条重要线索&#xff0c;这就是检索增强生成&#xff08;RAG&#xff09;发挥作用的地方&#xff0c;它就像你在人工智能和语言模型世界中的可靠助手。但即使是最…

小心JDK20 ZipOutputStream

Oracle 團隊竟然這麽粗心&#xff0c;編譯JDK 20 時ZipOutputStream沒有編譯成功就發佈了。 所以這個20版本不可以使用ZipOutputStream。 GZIPInputStream 只能做最後的壓縮&#xff0c;不能添加多個附件ZipEntry。 下一個版本21不存在這個問題。 try(var zipOut new ZipOu…

数据分析——火车信息

任务目标 任务 1、整理火车发车信息数据&#xff0c;结果的表格形式为&#xff1a; 2、并输出最终的发车信息表 难点 1、多文件 一个文件夹&#xff0c;多个月的发车信息&#xff0c;一个excel&#xff0c;放一天的发车情况 2、数据表的格式特殊 如何分析表是一个难点 数…

案例102:基于微信小程序的旅游社交管理系统设计与实现

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;SSM JDK版本&#xff1a;JDK1.8 数据库&#xff1a;mysql 5.7 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.5.4 小程序框架&#xff1a;uniapp 小程序开发软件&#xff1a;HBuilder X 小程序…

解决VMware 虚拟机 ubuntu 20.04 异常关闭导致虚拟网卡 ens33 无法工作问题

问题描述 由于经常使用 SSH 远程链接 VMware 中的虚拟机 ubuntu&#xff0c;每次关闭都是挂起&#xff0c;时间久了&#xff0c;虚拟机运行有些卡顿了&#xff0c;此时可以通过 Linux 命令重启或者关闭 ubuntu&#xff0c;也可以之间使用 VMWare 中的【虚拟机】-- 【电源】-&g…

SiC电机控制器(逆变器)发展概况及技术方向

SiC电机控制器&#xff08;逆变器&#xff09;发展概况及技术方向 1.概述2.电动汽车动力系统设计趋势3.栅极驱动器和驱动电源配置4.结论 tips&#xff1a;资料来自网上搜集&#xff0c;仅供学习使用。 1.概述 2022年到2023年&#xff0c;第三代半导体碳化硅被推上了新的热潮。…

前端uniapp的tab选项卡for循环切换、开通VIP实战案例【带源码/最新】

目录 效果图图1图2 源码最后 这个案例是uniapp&#xff0c;同样也适用Vue项目&#xff0c;语法一样for循环&#xff0c;点击切换 效果图 图1 图2 源码 直接代码复制查看效果 <template><view class"my-helper-service-pass"><view class"tab…

第14课 利用openCV快速数豆豆

除了检测运动&#xff0c;openCV还能做许多有趣且实用的事情。其实openCV和FFmpeg一样都是宝藏开源项目&#xff0c;貌似简单的几行代码功能实现背后其实是复杂的算法在支撑。有志于深入学习的同学可以在入门后进一步研究算法的实现&#xff0c;一定会受益匪浅。 这节课&#…

(Python + Selenium4)Web自动化测试自学Day1

目录 文章声明⭐⭐⭐让我们开始今天的学习吧&#xff01;自动打开Chrome浏览器实现自动搜索元素定位常用的元素定位方式By.IDBy.CLASS_NAMEBy.TAG_NAMEBy.NAMEBy.LINK_TEXTBy.PARTIAL_LINK_TEXTBy.CSS_SELECTOR根据id定位根据class定位根据属性定位组合定位 By.XPATH 文章声明⭐…

#error 在C语言中的作用

1、#error命令是C/C语言的预处理命令之一 #error 是C语言中的预处理指令之一&#xff0c;用于在编译时生成一个错误消息。当编译器遇到 #error 指令时&#xff0c;会立即停止编译&#xff0c;并将指定的错误消息输出到编译器的错误信息中。 在给定的代码中&#xff0c;#error…

玩转Mysql 二(MySQL的目录结构与表结构)

一路走来,所有遇到的人,帮助过我的、伤害过我的都是朋友,没有一个是敌人。 一、MYSQL目录结构及命令存放路径 1、查看MYSQL数据文件存放路径 mysql> show variables like datadir; 注意:生成环境要提前规划好数据存放目录,存储一般以T为单位闪盘。 2、MYSQL命令存放…

Note: A Woman Doctor Lina

A woman doctor Lina 女医生丽娜 Born in a pigs’ nest, Lina led a poor life in her childhood. 出生在猪圈里&#xff0c;丽娜过着贫穷的童年生活。 led nest She was looked down upon by the children of her generation. 她被她同时代的孩子瞧不起。 generation look…

私有云平台搭建openstack和ceph结合搭建手册

OpenStack与云计算 什么是云&#xff1f; 如何正确理解云&#xff0c;可以从以下几个方面。 云的构成。 用户&#xff1a;对用户而言是透明无感知的&#xff0c;不用关心底层构成&#xff0c;只需要知道利用云完成自己任务即可。 云提供商&#xff1a;对云资产管理和运维。 云…

CentOS安装JDK

目录 一、文件准备 1、目录创建 2、导入jdk压缩文件 3、解压压缩包 二、配置 1.配置环境变量 2.使用source命令生效环境变量 3、测试 总结 一、文件准备 1、目录创建 在/usr/local下创建java 目录 2、导入jdk压缩文件 3、解压压缩包 tar -zxf jdk-8u201-linux-x64.t…

[C#]使用onnxruntime部署yolov8-onnx印章检测

【官方框架地址】 https://github.com/ultralytics/ultralytics.git 【算法介绍】 YOLOv8是目标检测领域中的一种先进算法&#xff0c;它是YOLO&#xff08;You Only Look Once&#xff09;系列算法的最新发展。YOLO算法以其高效和实时的性能而著名&#xff0c;而YOLOv8则进一…

计算机毕业设计----ssm大学生兼职论坛

项目介绍 该项目是一个大学生校园兼职平台&#xff0c;分为前台和后台功能&#xff0c;主要用户有三种角色&#xff1a;分别是商家、学生、管理员。商家可以发布兼职信息&#xff0c;发布后&#xff0c;管理员进入后台确认信息是否有效&#xff0c;并且审核该兼职信息。学生登…

gRPC - gRPC 整合 SpringBoot(全代码 + 避坑!)

目录 一、gRPC 整合 SpringBoot 1.1、创建项目 1.2、天坑&#xff08;看前须知&#xff09;&#xff01; 1.2.1、天坑背景 1.2.2、解决天坑 1.3、api 开发 1.4、server 开发 1.5、client 开发 1.6、演示效果 一、gRPC 整合 SpringBoot 1.1、创建项目 api&#xff1a;编…