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

目录

前言

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

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}的值赋值给 t
    int t = Math.min(m, n);
    for (; t >= 2; t--) {
        // 第二步和第三步,如果 m 除以 t 余数为 0 并且 n 除以 t 余数为 0,直接返回 t
        if (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);

        // 处理 marr
        for (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;
        }

        // 处理 narr
        for (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()) {
            // 取出 value
            int 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算法,此算法只有整数的移位和加减法。

加油加油,今天熬熬夜。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

jenkins安装报错:No such plugin: cloudbees-folder

jenkins安装报错&#xff1a;No such plugin: cloudbees-folder 原因是缺少cloudbees-folder.hpi插件 解决&#xff1a; 一&#xff0c;重新启动 http://xxx:8800/restart 二&#xff0c;跳到重启界面时&#xff0c;点击系统设置 三&#xff0c;找到安装插件&#xff0c;然…

Python基础-07(for循环、range()函数)

文章目录 前言一、for循环1.for循环结构2.参数 end&#xff08;使其输出时变为横向&#xff09; 二、range()函数1.range(常数)2.range(起始值&#xff0c;结束值)3.range(起始值&#xff0c;结束值&#xff0c;步长)4.例子 总结 前言 此章介绍循环结构中最常用的循环&#xf…

Go (一) 基础部分5 -- 单元测试,协程(goroutine),管道(channel)

一、单元测试 Go自带一个轻量级的"测试框架testing"和自带的"go test"命令来实现单元测试和性能测试。 1.确保每个函数时可运行&#xff0c;并且运行结果是正确的。 2.确保写出来的代码性能是好的。 3.单元测试能及时的发现程序设计或实现的逻辑错误&#…

mysql基础-数据操作之增删改

目录 1.新增数据 1.1单条数据新增 1.2多条数据新增 1.3查询数据新增 2.更新 2.1单值更新 2.2多值更新 2.3批量更新 2.3.1 批量-单条件更新 2.3.2批量-多条件更新 2.4 插入或更新 2.5 联表更新 3.删除 本次分享一下数据库的DML操作语言。 操作表的数据结构&#xf…

Spark回归分析与特征工程

回归分析是统计学和机器学习中的一个重要分支&#xff0c;用于建立因变量与自变量之间的关系模型。在大数据领域&#xff0c;Apache Spark为回归分析提供了强大的工具和库&#xff0c;以处理大规模数据集。本文将深入探讨如何使用Spark进行回归分析以及如何进行特征工程&#x…

使用qtquick调用python程序,pytorch

一. 内容简介 使用qtquick调用python程序 二. 软件环境 2.1vsCode 2.2Anaconda version: conda 22.9.0 2.3pytorch 安装pytorch(http://t.csdnimg.cn/GVP23) 2.4QT 5.14.1 新版QT6.4,&#xff0c;6.5在线安装经常失败&#xff0c;而5.9版本又无法编译64位程序&#xf…

第1章 初识JavaScript

学习目标 了解JavaScript基本概念&#xff0c;能够说出JavaScript的作用、由来、组成和特点 熟悉常见浏览器的特点&#xff0c;能够说出浏览器的组成以及作用 掌握下载和安装Visual Studio Code编辑器&#xff0c;能够独立完成编辑器的下载和安装 掌握JavaScript代码引入方式…

Windows电脑无法睡眠解决办法

原因 电脑无法休眠的原因&#xff0c;是打开离开模式策略后&#xff0c;windows内核会持续调用CPU资源&#xff0c;导致系统一直在运行而无法关闭。关闭后就好了。 解决步骤 修改注册表 操作步骤如下: 按winR&#xff0c;输入regedit&#xff0c;打开注册表编辑页面。输入如下…

第11章 GUI Page462~476 步骤二十三,二十四,二十五 Undo/Redo ③实现“Undo/Redo”菜单项

工程六 添加“编辑”菜单和子菜单 菜单ID分别为 idMenuEditUndo 和 idMenuEditRedo 热键&#xff08;快捷键&#xff09;分别为CtrlZ 和 CtrlShiftZ 变量名分别为 MenuItemEditUndo 和 MenuItemEditRedo 分别添加事件 ActionLink类增加成员函数 运行效果&#xff1a;“添加…

Docker安装WebRTC下TURN服务

详细实现方式以及代码下载请前往 https://www.passerma.com/article/90 实现效果 一、手动构建镜像 1.新建Dockerfile文件 文件用于编译镜像 以alpine为基础镜像 添加coturn需要的依赖库 获取coturn并进行编译 通过start.sh启动turnserver服务 Dockerfile FROM alpineRUN ap…

AI与5G、IDC等成为数字经济的重要基础设施

AI与5G、IDC等已经成为数字经济的重要基础设施&#xff0c;它们的影响和作用不容忽视。随着技术的迅速发展&#xff0c;AI在各行各业都得到了广泛应用&#xff0c;并成为数字经济的核心驱动力之一。 首先&#xff0c;AI的兴起为数字经济带来了巨大的机遇。AI技术可以帮助企业从…

Vue2商品规格选择

Vue2Element-ui Vu2仿写拼多多商家后台规则选择&#xff0c;为什么用Vue2呢&#xff0c;因为公司用的Vue2... 样式不是很好看&#xff0c;自己调一下就行。 <template><div ref"inputContainer"><div>{{ combinationsResult }}</div><…

案例099:基于微信小程序的外卖小程序的研究与开发

文末获取源码 开发语言&#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 小程序…

在Gitee上维护Erpnext源

在Gitee上维护Erpnext源 官方的frappe和erpnext地址: GitHub - frappe/frappe: Low code web framework for real world applications, in Python and Javascript GitHub - frappe/erpnext: Free and Open Source Enterprise Resource Planning (ERP) 1, 仓库地址输入frappe的官…

R 批量对多个变量进行单因素方差分析 批量计算均值±标准差

多个变量批量进行单因素方差 R实现 文章目录 一、批量生成均值标准差 P值二、添加协变量单因素方差分析&#xff0c;生成校正P值三、在分层情况下进行单因素方差分析四、添加协变量和交互项的单因素方差分析&#xff0c;生成交互项的P值 一、批量生成均值标准差 P值 数据结构如…

pyfolio工具结合backtrader分析量化策略组合,附源码+问题分析

pyfolio可以分析backtrader的策略&#xff0c;并生成一系列好看的图表&#xff0c;但是由于pyfolio直接install的稳定版有缺陷&#xff0c;开发版也存在诸多问题&#xff0c;使用的依赖版本都偏低&#xff0c;试用了一下之后还是更推荐quantstats。 1、安装依赖 pip install …

【STM32】STM32学习笔记-TIM输出比较(15)

00. 目录 文章目录 00. 目录01. 输出比较简介02. PWM简介03. 输出比较通道(高级)04. 输出比较通道(通用)05. 输出比较模式06. PWM基本结构07. PWM参数计算08. 舵机简介09. 舵机硬件电路10. 直流电机及驱动简介11. 直流电机硬件电路12. 附录 01. 输出比较简介 OC&#xff08;Ou…

ASP.NET可视化流程设计器源码

源码介绍: ASP.NET可视化流程设计器源码已应用于众多大型企事业单位。拥有全浏览器兼容的可视化流程设计器、表单设计器、基于角色的权限管理等系统开发必须功能&#xff0c;大大为您节省开发时间&#xff0c;是您开发OA.CRM、HR等企事业各种应用管理系统和工作流系统的最佳基…

Redis高级特性和应用(慢查询、Pipeline、事务、Lua)

Redis的慢查询 许多存储系统(例如 MySQL)提供慢查询日志帮助开发和运维人员定位系统存在的慢操作。所谓慢查询日志就是系统在命令执行前后计算每条命令的执行时间,当超过预设阀值,就将这条命令的相关信息(例如:发生时间,耗时,命令的详细信息)记录下来,Redis也提供了类似…

【开源】基于JAVA语言的服装店库存管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 角色管理模块2.3 服装档案模块2.4 服装入库模块2.5 服装出库模块 三、系统设计3.1 用例设计3.2 数据库设计3.2.1 角色表3.2.2 服装档案表3.2.3 服装入库表3.2.4 服装出库表 四、系统展示五、核心代码5.…