欧几里得算法/辗转相除法(证明)
/* "简单的东西, 往往包含深刻的道理" 回头一看, 发现简单的算法, 证明却不是很简单 前置知识 a|b代表a可以整除b d|a && d|b => d|(xa + yb) (a, b)表示a, b的公约数 gcd(a, b)表示a, b的最大公约数 证明d|a => id=a, d|b => jd=b, xa + yb == ixd + jyd == (ix + jy)d, (ix + jy)是整数 <==> d|(xa + yb) (其中x, y, i, j都为整数) 碾转相除法 中心思想gcd(a, b) == gcd(b, a % b); 设d为(a, b)的公约数, a % b == r, a - bk = r 那么d|a, d|b, 那么d|(a - bk) <==> d|r ; 所以对于(a, b)的所有公约数d都是(a, r)的公约数, 那么gcd(a, b) == gcd(a, r) == gcd(a, b % a) gcd(a, b) == gcd(b, a % b); 反过来, 设p是(b, a % b)的公约数, p|b, p|(a - kb) => p|(a - kb + kb) => p|a; 所以p是(a, b)的约数 所以(b, a % b)的所有公约数, 都是(a, b)的公约数, 那么(a, b) == (b, a % b) 那么gcd(b, a % b) == gcd(a, b); 碾转相除法 我们不断进行gcd(a, b) == gcd(b, a % b) 直到其中一个项为0, 出现了0说明什么?, a % b == 0 <==> a == kb <==> b|a 因为最大公约数是相同的, 所以最后最大公约数也是相同的 因为b|a那么此时gcd(b, a % b) == b, 那么前面所有数的最大公约数就是b (此时b为当前函数gcd(b, a % b)里的b) 这时候就求出了最大公约数 实现上我常采用递归的方式, 起始函数是gcd(a, b); 递归函数里面写gcd(b, a % b); 这样在进入下一次递归里面a1 == b, b1 == a % b; 再递归就是gcd(b1, a1 % b1), 相当于 gcd(a % b, b % (a % b)); 这样实现了自动交换位置碾转相除, 通过三目运算时可以压成一行 整个就是 int gcd(int a, int b) { return b ? gcd(b, a % b) : a; } */
更深的探索
在做题的时候发现的,对于我们的辗转相除法来说,在代码上,gcd(a, b)
是不区分a, b正负的,即都当成正数来算最大公约数,不过可能会出现负值,这时候取绝对值就行。
可以试试这几个
gcd(12, 8) gcd(-12, 8) gcd(12, -8) gcd(-12, -8)
可以发现的到的结果绝对值都是4
证明
设d = (a, b)
, a, b
都是整数
则d|a
,d|b
那么d|(a - b)
那么即使a - b
是负数,d
可以整除a - b
,也可以整除a
和b
那么(a, b) == (a - b, b)
因此在代码上,辗转相除法不区分正负号
[[扩展欧几里得(逆元)]]
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】