欧几里得算法/辗转相除法(证明)
/*
"简单的东西, 往往包含深刻的道理"
回头一看, 发现简单的算法, 证明却不是很简单
前置知识
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)
因此在代码上,辗转相除法不区分正负号
[[扩展欧几里得(逆元)]]