/*
"简单的东西, 往往包含深刻的道理"
回头一看, 发现简单的算法, 证明却不是很简单
前置知识
a|b代表a可以整除b
d|a && d|b => d|(xa + yb)
证明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;
}
不压是这样, 易于扩展
int gcd(int a, int b)
{
if (b == 0)
{
return a;
}
int d = gcd(b, a % b);
return d;
}
*/
欢迎指出错误
[[扩展欧几里得(逆元)]]