当前位置: 首页 > news >正文

算法(三)——最大公约数、最小公倍数、同余原理

文章目录

  • 最大公约数、最小公倍数、同余原理
    • 最大公约数
    • 最小公倍数
    • 同余原理

最大公约数、最小公倍数、同余原理

最大公约数

最大公约数的计算可以采用 欧几里得算法(辗转相除法),基本思想是:两个整数的最大公约数等于其中较小的数和两数相除的余数的最大公约数。这个过程一直重复,直到余数为0,此时的除数就是最大公约数

代码样例:

    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 a * b / gcd(a, b);
    }

【经典例题】

原题链接:878. 第 N 个神奇数字 - 力扣(LeetCode)

在这里插入图片描述


根据题目要求,神奇数字是一个正整数,因此要从 1 开始考虑。以示例二为例,要求求出第4个神奇数字,从1开始,2能被2整除是第一个神奇数字,3能被3整除是第二个神奇数字,4能被2整除是第三个神奇数字,5不是神奇数字,6既能被2整除也能被3整除因此是第四个神奇数字,答案是6。

由于参数的随机性,我们每次都要从1开始考虑,范围太大了,因此我们考虑缩小范围:第 n 个神奇数字的范围:[1, min(a, b) * n],因为能被 a 整除 或者 能被 b 整除的数一定是神奇数字,所以范围的上界可以缩小到 a 和 b 较小值的 n 倍。

确定的范围是有序的,考虑二分加快效率,二分依据什么? 考虑[1, min(a, b) * n]区间内的一个数num,我们能否得到[1, num]范围内有几个神奇数字?如果能够求出,我们就可以在[1, min(a, b) * n]区间二分,每次判断中间值num(num其实就是每次二分求得的mid)[1, num]范围内有几个神奇的数字,与n对比:如果比n小,说明[1, num]范围内不会出现第n个神奇数字,要在(num, right]寻找;如果比n大或者等于n说明[1, num]中包含第n个神奇的数字。(注意:等于的情况下,num的值不一定是第n个神奇的数字,例如:当n = 3;a = 6;b = 4时,第n个神奇的数字应该是8,但是对于数字num = 9,也满足[1, num]范围内的神奇数字等于n ,我们必须找到大于或等于 n 的最小的 num)。

那么,如何求[1, num]范围内有几个神奇数字呢?

  • 数量count = num / a + num / b - num / a和b的最小公倍数,即范围内能被a整除的数量 + 范围内能被b整除的数量 - 重复计算的数量(范围内能被a和b的最小公倍数整除的数量)

【代码实现】

class Solution {
    public int nthMagicalNumber(int n, int a, int b) {
        long l = 1;
        long r = Math.min(a, b) * (long) n;
        long ret = 0;
        long lcmNum = lcm(a, b);
        while(l <= r) {
            long m = (l + r) / 2;
            long count = m / a + m / b - m / lcmNum;
            if(count >= n) {
                ret = m;
                r = m - 1;
            }else {
                l = m + 1;
            }
        }
        return (int) (ret % 1000000007);
    }

    private static long gcd(long a, long b) {
        return b == 0 ? a : gcd(b, a % b);
    }

    private static long lcm(long a, long b) {
        return a * b / gcd(a, b);
    }
}

  • 使用1000000007而非(long) (10e9 + 7),这会导致类型转换和精度问题。

同余原理

我们这里言简意赅,介绍一下算法中同余原理的常见应用场景以及常用结论:

  1. 应用场景

    • 避免大数运算:在算法竞赛中,经常需要处理非常大的数。直接对大数进行运算可能会导致溢出或计算效率低下。同余原理允许我们在计算过程中不断取模,将大数问题转化为小数问题,从而避免溢出并提高计算效率。
    • 数论问题:许多算法竞赛题目涉及数论,如最大公约数、最小公倍数、素数判定等。同余原理是解决这些数论问题的基础工具。
  2. 常用结论

    • 加法同余原理多个数的和对一个数取模得到的余数,等于每个数对相同的数取模得到的余数相加后再对这个数取模。

      (a + b) % mod = (a % mod + b % mod) % mod

    • 乘法同余原理多个数的乘积对一个数取模得到的余数,等于每个数对相同的数取模得到的余数的乘积再对这个数取模。

      (a * b) % mod = (a % mod * b % mod) % mod

    • 减法同余原理多个数的差对一个数取模得到的余数,等于每个数对相同的数取模得到的余数的差,再加上模数后再对这个数取模。

      (a - b) % mod = (a % mod - b % mod + mod) % mod


简单说明

  • 三种同余原理的右边部分再次% mod是为了确保最终的余数在0 ~ mod - 1的范围内

  • 对于减法同余原理,再次+ mod是为了避免结果为负数,因为题目一般要求得到非负结果

  • 注意两个操作数ab相加和相乘的结果可能溢出int,因此我们要强制类型转换为long,取模后再转换为int

    例如:int ret = (int) (((long) a * b) % mod)

  • 总之,如果要求返回一个复杂表达式的结果取模后的非负结果,并且表达式的操作数是大数,此时我们就可以利用同余定理化简计算。


相关文章:

  • LeetCode 873. Length of Longest Fibonacci Subsequence(2025/2/27每日一题)
  • 西门子S7-1200比较指令
  • @[TOC](Java实现 图书管理系统) # 一、菜单 该图书管理系统的菜单界面主要有以下两种: **管理员菜单:** **用户菜单:**
  • 线程池--深入一些
  • 从0-1学习Mysql第十二章: 常见问题与故障排查
  • 刷屏(0和1)
  • 模块七_面向对象
  • 解决android studio(ladybug版本) gradle的一些task突然消失了
  • iOS for...in 循环
  • Phpstudy中的MySQL无法正常启动或启动后自动暂停,以及sqlilab环境搭建出现的问题解决方法
  • ORM Bee V2.5.2.x 发布,支持 CQRS; sql 性能分析;更新 MongoDB ORM分片
  • 014存储期(时间)
  • Qt之QStateMachine等待
  • C++20 Lambda表达式新特性:包扩展与初始化捕获的强强联合
  • Is Noise Conditioning Necessary for Denoising Generative Models?论文阅读笔记
  • 开源向量数据库Milvus简介
  • RNN实现精神分裂症患者诊断(pytorch)
  • 在日常工作中,有一块新磁盘,如何扩容到vm中,具体命令是什么?
  • XSL 语言:XML 样式表的语言基础与应用
  • 初识uniApp
  • iis怎么创建网站/河南网站优化
  • wordpress翻页插件/广州优化公司哪家好
  • 做百度网站哪家公司好/深圳seo优化服务
  • 淮南模板网站建设费用/网址大全名称
  • 怎么做网站关键词搜索/上海百度seo
  • 建设产品网站/汕头网站建设方案外包