欧几里得算法/辗转相除法(证明)

/*
    "简单的东西, 往往包含深刻的道理"
    回头一看, 发现简单的算法, 证明却不是很简单
    
    前置知识
    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,也可以整除ab
那么(a, b) == (a - b, b)
因此在代码上,辗转相除法不区分正负号

[[扩展欧几里得(逆元)]]

posted @ 2024-03-26 15:26  blind5883  阅读(16)  评论(0编辑  收藏  举报