欧几里得算法及其扩展

欧几里得算法及其扩展

前言

整除:对于整数 a(a0)b,如果 qZ,使得 b=a×q,则称 a 能整除 b,记作 ab。否则,称 a 不能整除 b,记作 ab

最大公约数

公约数:如果一个整数是集合中任意一个元素的约数,则这个整数是该集合中的最大公约数。

最大公约数:集合的公约数中最大的一个,通常用 gcd 表示。

最大公约数经典的算法有:欧几里得算法(辗转相除法)、更相减损术。

欧几里得算法

“欧几里得算法”又名“辗转相除法”

过程

已知两个正整数 ab,其中 a>b

ba,则 b 就是二者的最大公约数

bagcd(a,b)=gcd(b,amodb)

“辗转相除法”与“更相减损术”都基于事实:

akb 时,gcd(a,b)=gcd(akb,b)=gcd(b,akb),其中 k 为正整数。
k 取最大值 ab,又可以写成 gcd(a,b)=gcd(amodb,b)=gcd(b,amodb)

证明:

ab 的最大公约数是 g,则 x,yZ 使得 a=xg,b=yg

由于 g 是最大公约数,所以 xy 中不存在大于 1 的公约数,即 xy 互质

现在要证: g 也是 akbb 的最大公约数
akb=xgkyg=(xky)g
即证 xkyy 互质

假设 xkyy 不互质,它们的最大公约数为 p,则 xky=up,y=up

x=up+yk=up+upk=(u+uk)p

此时 xy 不互质,与已证条件不符,因此 xkyy 互质

证毕

实现

/**
 * 通过递归实现的欧几里得算法求解最大公约数
 * @param a 一个正整数
 * @param b 一个正整数
 * @return a与b的最大公约数
 */
int gcd(int a, int b) {
    int rest = a % b;
    if (rest == 0) {
        return b;
    }
    return gcd(b, rest);
}

可以写成迭代的形式

/**
 * 通过迭代实现的欧几里得算法求解最大公约数
 *
 * @param a 一个正整数
 * @param b 一个正整数
 * @return a与b的最大公约数
 */
int gcd(int a, int b) {
    if (a < b) {
        return gcd(b, a);
    }
    while (true) {
        int rest = a % b;
        if (rest == 0) {
            return b;
        }
        a = b;
        b = rest;
    }
}

最小公倍数

公倍数:如果一个整数能被集合中任意一个数整除,则这个整数是该集合的公倍数。

最小公倍数:集合的公倍数中最小的一个,通常用 lcm 表示。

对于正整数 ab,由算术基本定理得:a=p1x1p2x2pnxnb=p1y1p2y2pnyn

则由最大公约数与最小公倍数的定义得:

gcd(a,b)=p1min(x1,y1)p2min(x2,y2)pnmin(xn,yn)lcm(a,b)=p1max(x1,y1)p2max(x2,y2)pnmax(xn,yn)

由于 xk+yk=min(xk,yk)+max(xk,yk)

所以 gcd(a,b)×lcm(a,b)=a×b

所以 lcm(a,b)=a×bgcd(a,b)

扩展欧几里得算法

常用于求 ax+by=gcd(a,b) 的一组可行解

过程

ax+by=gcd(a,b)=gcd(b,amodb)(1)=bx+(amodb)y(2)=bx+(abab)y(3)=ay+b(xaby)(4)

  1. 欧几里得定理
  2. gcd(a,b)=ax+byb代入aamodb带入b
  3. 正整数取模定义,amodb=aabb
  4. 合并同类项

因此,ax+by=ay+b(xaby)

对比系数得:

{x=yy=xaby

b=0 时, a=gcd(a,b),由 ax+by=gcd(a,b)gcdx+0y=gcd,则 x=1,yZ此时 (a,b)的 一个整数解

作为人类,通常会选择令最后一层的 y=0

因为 y 在回代时会增长很快,无论正负

再从下至上递归求出 最开始的 ax+by=gcd(a,b) 的一组整数解 (x0,y0)

此时可以得到方程的通解为 x=x0+bgcd(a,b)ty=y0agcd(a,b)t,其中 tZ

若要求得 x 的最小正整数解,可以通过 (x0%b+b)%b 求得

值域分析

由于 ax+by=gcd(a,b) 的解有无数个,那求出的解如果趋近于无穷呢?

b0,扩展欧几里得算法求出的可行解必有 |x|b,|y|a

详见:值域分析 - OI Wiki

实现

int x = 0, y = 0;
int exgcd(int a, int b) {
    if (b == 0) {
        x = 1;
        y = 0;
        return a;
    }
    int gcd = exgcd(b, a % b), t = x;
    x = y;
    y = t - a / b * y;
    return gcd;
}

迭代方法

欧几里得算法可以通过迭代的方式求得最大公约数,那扩展欧几里得是否也能迭代呢?

答案是肯定的。

x,y 对应当前层,x,y 对应下一层

初始情况,当 x=1y=0x=0y=1时,则 ax+by=aax+by=b,方程成立。

gcd(a,b)=gcd(b,aabb),存储两层,顺次向下一层递推

ax+by=a(1)ax+by=b(2)

(1)ab×(2)(3)a(xabx)+b(yaby)=aabb

接下来 (2)(1)(3)(2),继续递推,直至求出 gcd,即 b=0

int x = 0, y = 0;
int exgcd(int a, int b) {
    x = 1;
    y = 0;
    int nx = 0, ny = 1, q, t;
    while (b != 0) {
        q = a / b;
        
        x -= q * nx;
        t = x; x = nx; nx = t;
        
        y -= q * ny;
        t = y; y = ny; ny = t;
        
        a -= q * b;
        t = b; b = a; a = t;
    }
    // 结束后
    // a 是 gcd(a,b)
    // 如果 gcd(a,b)!=1 则 无整数解
    // 否则 x,y 是 ax+by=gcd(a,b) 的一组整数解
    return a;
}
int x, y;
int exgcd(int a, int b) {
    x = 1, y = 0;
    int nx = 0, ny = 1, q;
    while (b != 0) {
        q = a / b;
        std::swap(x -= q * nx, nx);
        std::swap(y -= q * ny, ny);
        std::swap(a -= q * b, b);
    }
    // 结束后
    // a 是 gcd(a,b)
    // 如果 gcd(a,b)!=1 则 无整数解
    // 否则 x,y 是 ax+by=gcd(a,b) 的一组整数解
    return a;
}

二元一次不定方程

关于 x,y 的形如 ax+by=c 的方程是二元一次不定方程

裴蜀定理

对于 a,bZ, x,yZ 使得 ax+by=gcd(a,b)

证明(By other):

首先,显然有 gcd(a,b)|agcd(a,b)|b 。由整除的性质可知 x,yZ,gcd(a,b)(ax+by)。设 sax+by 的最小正值,故有 gcd(a,b)s
k=as,r=amods ,则有 r=ak(ax+by)=a(1kx)+b(ky) ,发现 r 也为 (a,b) 的线性组合,而 r 又在模 s 剩余系中,故 r[0,s1],又由于 s 为最小正值,故 r=0
所以 sa,同理 sb,根据两个数的公约数必为这两个数的最大公约数的约数,有 sgcd(a,b),而上文已证明 gcd(a,b)s,故 s=gcd(a,b)
进而得证,故 ax+by=gcd(a,b) 这个方程一定有解

判断方程有整数解的条件

方程 ax+by=c 有整数解的充要条件是 gcd(a,b)c

证明(By other):

考虑将 ax+by=gcd(a,b) 变为 ax+by=c
为了区分这两个方程,我们将前者改为 ax+by=gcd(a,b)
k=gcd(a,b),方程两边同除 k,得: akx+bky=1

方程两边同乘c得:ackx+bcky=c

只要通过换元,即令 a=ack,b=bck,即可将方程转化为 ax+by=c 的形式
考虑到 gcd(ack,bck)=c=gcd(a,b),且对于一般的方程: atx+bty=c(c=ct,tZ)
故只有 cgcd(a,b) 倍数时原方程有解,即 gcd(a,b)c,从而得证。

求解二元一次不定方程

对于解方程 ax+by=c,可以先解方程 ax+by=gcd(a,b)

当我们得到了 ax+by=gcd(a,b) 的一组解 x0,y0,即 ax0+by0=gcd(a,b)

两边同时除以 gcd(a,b),再乘以 c,即 ac×x0gcd(a,b)+bc×y0gcd(a,b)=c

x0=c×x0gcd(a,b), y0=c×y0gcd(a,b)ax+by=c 的一组特解

此时可以得到方程的通解为 x=x0+bgcd(a,b)ty=y0agcd(a,b)t,其中 tZ

若要求得 x 的最小正整数解,可以通过 (x0%b+b)%b 求得

线性同余方程

关于 x 的形如 axc(mod b) 的方程被称为线性同余方程。

与 二元一次不定方程 等价

对于方程 axc(mod b),等价于 ax+by=c,其中 y=cbaxb

证明:

axc(mod b)ax mod c=b mod c(1)axb×axb=cb×cb(2)ax+(cbaxb)b=c(3)

  1. 同余号转等号
  2. 取模定义 a mod b=ab×ab
  3. 合并同类项

求解线性同余方程

转化为 二元一次不定方程,求解该不定方程,即是求解线性同余方程

参考资料

最大公约数 - OI Wiki

求解一类方程的方法 - Nickel_Angel's nest

posted @   Cattle_Horse  阅读(109)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)
点击右上角即可分享
微信分享提示