扩展欧几里得算法及其推广应用

1.扩展欧几里得算法

贝祖定理若a,b是整数,且gcd(a,b)=d,那么对于任意的整数x,y,ax+by=m中的m一定是d的倍数。(特别地,如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。)

那么贝祖定理的一个直接应用就是:如果ax+by=1有解,那么gcd(a,b)=1(将原公式两边同时除以gcd(a,b))。

扩展欧几里得算法用来解决这样一个问题:给定两个非零的整数a和b,求一组整数解(x,y)使得ax+by = gcd(a,b)成立,其中gcd(a,b)表示a和b的最大公约数。(其中我们通过前面的贝祖定理可知解一定存在)

回忆我们知道的欧几里得算法,它总是把gcd(a,b)转化为求解gcd(b,a%b),而当b变为0时返回a,此时的a就等于gcd。也就是说,欧几里得算法结束时变量a中存放的是gcd,变量b中存放的是0,因此此时显然有 a × 1 + b × 0 = g c d a\times 1+b\times 0=gcd 成立,此时有x=1、y=0成立。

我们不妨利用上面的欧几里得算法的过程来计算x和y。目前已知的是递归边界成立时为x=1、y=0,需要想办法反推出最初始的x和y。

当计算gcd(a,b)时,有 a x 1 + b x 1 = g c d ax_1+bx_1=gcd 成立;

而在下一步计算gcd(b,a%b)时,又有 b x 2 + ( a % b ) y 2 = g c d bx_2+(a \%b)y_2=gcd 成立。

因此 a x 1 + b y 1 = b x 2 + ( a % b ) y 2 ax_1+by_1=bx_2+(a\%b)y_2 成立。

又考虑到有关系 a % b = a ( a / b ) × b a\%b=a-(a/b)\times b 成立(此处除法为整除),因此 a x 1 + b y 1 = a y 1 + b ( x 2 ( a / b ) y 2 ) ax_1+by_1=ay_1+b(x_2-(a/b)y_2)

对比等号左右两边可以马上得到下面的递推公式:

{ x 1 = y 2 y 1 = x 2 ( a / b ) y 2 \begin{cases}x_1=y_2 \\ y_1=x_2-(a/b)y_2 \end{cases}

由此我们便可以通过 x 2 x_2 y 2 y_2 来进行反推出 x 1 x_1 y 1 y_1 了。代码如下:

int exGcd(int a, int b, int &x, int &y)   //x和y使用引用
{
    if(b == 0)
    {
        x = 1;
        y = 0;
        return a;
    }
    int g = exGcd(b, a%b, x, y);    //递归计算exGcd(b,a%b)
    int temp = x;             //存放x的值
    x = y;
    y = temp - (a/b)*y;       //更新y = x(old) - a/b*y(old)
    return g;                 //g是gcd
}

由于这里我们使用了引用,因此当exGcd函数结束时x和y就是所求的解。

显然,在得到这样一组解之后,就可以通过下面的式子得到全部解(其中K为任意整数):

{ x = x + b g c d × K y = y a g c d × K \begin{cases}x'=x+\frac{b}{gcd}\times K \\ y'=y-\frac{a}{gcd}\times K \end{cases}

简单证明一下:

假设新的解为 x + s 1 x+s_1 y s 2 y-s_2 ,即有 a ( x + s 1 ) + b ( y s 2 ) = g c d a*(x+s_1)+b*(y-s_2)=gcd 成立,

通过代入 a x + b y = g c d ax+by=gcd 可以得到 a s 1 = b s 2 as_1=bs_2

于是 s 1 s 2 = b a \frac{s_1}{s_2}=\frac{b}{a} 成立。

为了让和 s 1 s_1 s 2 s_2 尽可能小,可以让分子和分母同时除以一个尽可能大的数,同时保证它们仍然是整数。显然,由于 b g c d \frac{b}{gcd} a g c d \frac{a}{gcd} 互质,因此gcd是允许作为除数的最大数,于是 s 1 s 2 = b a = b / g c d a / g c d \frac{s_1}{s_2}=\frac{b}{a}=\frac{b/gcd}{a/gcd}

s 1 s_1 s 2 s_2 的最小取值就是 b g c d \frac{b}{gcd} a g c d \frac{a}{gcd}

证毕

也就是说,x和y的所有解分别以 b g c d \frac{b}{gcd} a g c d \frac{a}{gcd} 为周期。

那么其中x的最小非负整数解是什么呢?直观来说就是 x % b g c d x\%\frac{b}{gcd} 。但是由于通过exGcd函数计算出来的x、y可以为负数,因此实际上 x % b g c d x\%\frac{b}{gcd} 会得到一个负数,例如 ( 15 ) (-15)%4=-3 。考虑到即便x是负数, x % b g c d x\%\frac{b}{gcd} 的范围也是在( b g c d -\frac{b}{gcd} ,0),因此对于任意整数来说, ( x % b g c d + b g c d ) % b g c d (x\%\frac{b}{gcd}+\frac{b}{gcd})\%\frac{b}{gcd} 才是对应的最小非负整数解。

如果gcd=1,即ax+by=1时,全部解的公式简化为下式,且x的最小非负整数解也可以简化为 ( x % b + b ) % b (x\%b+b)\%b

{ x = x + b × K y = y a × K \begin{cases}x'=x+b\times K \\ y'=y-a\times K \end{cases}

2.方程ax+by=c的求解

当我们知道了ax+by=gcd的解后,最主要的应用就是:求解ax+by=c,其中c为任意整数。

首先,假设ax+by=gcd有一组解 ( x 0 , y 0 ) (x_0,y_0) ,现在在其等号的左右两边同时乘以 c g c d \frac{c}{gcd}

即有 a c x 0 g c d + b c y 0 g c d = c a\frac{cx_0}{gcd}+b\frac{cy_0}{gcd}=c 成立,因此 ( x , y ) = ( c x 0 g c d , c y 0 g c d ) (x,y)=(\frac{cx_0}{gcd},\frac{cy_0}{gcd}) 是ax+by=c的一组解。

但是显然这样做的充要条件是c%gcd==0(因为: a x g c d + b y g c d = c g c d a\frac{x}{gcd}+b\frac{y}{gcd}=\frac{c}{gcd} ,其中左边为整数因此右边也为整数)

否则第一步将无法做到。

为了获取全部解的公式,我们可以模仿前面的做法,假设新的解为 x + s 1 x+s_1 y s 2 y-s_2 ,然后将 a ( x + s 1 ) + b ( y s 2 ) = c a*(x+s_1)+b*(y-s_2)=c a x + b y = c ax+by=c 联立,发现同样可得 s 1 s 2 = b a \frac{s_1}{s_2}=\frac{b}{a} 成立。于是因为同样的原因, s 1 s_1 s 2 s_2 的最小取值仍然是 b g c d \frac{b}{gcd} a g c d \frac{a}{gcd} 。因此ax+by=c的全部解的公式为:

{ x = x + b g c d K = c x 0 g c d + b g c d K y = y a g c d K = c y 0 g c d a g c d K \begin{cases}x'=x+\frac{b}{gcd}*K=\frac{cx_0}{gcd}+\frac{b}{gcd}*K \\ y'=y-\frac{a}{gcd}*K=\frac{cy_0}{gcd}-\frac{a}{gcd}*K \end{cases}

我们发现,这与ax+by=gcd全部解的公式是一样的,唯一不同的是初始解(x,y)不同。因此,对于ax+by=c来说,其解同样分别以 b g c d \frac{b}{gcd} a g c d \frac{a}{gcd} 为周期。

但是我们要注意的是:在ax+by=gcd的全部解的基础上都乘以 c g c d \frac{c}{gcd} 只能获取一部分解。

这是因为:

由于 c g c d c\geq gcd (根据c%gcd==0得到),因此ax+by=gcd的全部解乘以 c g c d \frac{c}{gcd} 会导致周期放大为原先的 c g c d \frac{c}{gcd} 倍(x和y的周期会边为 b c g c d 2 \frac{bc}{gcd^2} a c g c d 2 \frac{ac}{gcd^2} ),所以导致漏解。

3.同余式 a x c ( m o d   m ) ax\equiv c(mod\ m) 的解

既然已经解决了ax+by=c,不得不提的就是同余式 a x c ( m o d   m ) ax\equiv c(mod\ m) 的求解。

什么式同余式:对于a、b、m来说,如果m整除a-b(即(a-b)%m=0),那么就说a与b模m同余,对应的同余式为 a x c ( m o d   m ) ax\equiv c(mod\ m) ,m称为同余式的模。

那么我们解决同余式 a x c ( m o d   m ) ax\equiv c(mod\ m) 的解呢?

根据同余式的定义,有 ( a x c ) % m = 0 (ax-c)\%m=0 成立

因此存在整数y,使 a x c = m y ax-c=my 成立

移项并令y=-y后即得 a x + m y = c ax+my=c

由前面的结论我们知道,当 c % g c d ( a , m ) = = 0 c\%gcd(a,m)==0 时方程才有解,且解的形式如下,

其中(x,y)是ax+my=c的其中一组解,可以先通过求解ax+my=gcd(a,m)得到 ( x 0 , y 0 ) (x_0,y_0) ,然后由公式 ( x , y ) = ( c x 0 g c d ( a , m ) , c y 0 g c d ( a , m ) ) (x,y)=(\frac{cx_0}{gcd(a,m)},\frac{cy_0}{gcd(a,m)}) 直接得到。

{ x = x + m g c d ( a , m ) × K y = y a g c d ( a , m ) × K K \begin{cases}x'=x+\frac{m}{gcd(a,m)}\times K \\ y'=y-\frac{a}{gcd(a,m)}\times K \end{cases} (K为任意整数)

虽然对方程ax+my=c来说,K可以取任意整数,但是对同余式来说会有很多解在模m意义下是相同的(由于只关心x,因此下面只考虑x)。对同余式来说没只需要找出那些在模m意义下不同的解。因此考虑 x = x + m g c d ( a , m ) K x'=x+\frac{m}{gcd(a,m)}*K ,会发现当K分别取0、1、2、…、gcd(a,m)-1时,所得到的解在模m意义下是不同的,而其他解都可以对应到K取这gcd(a,m)个数值之一。由此可得到结论:

设a,c,m是整数,其中 m 1 m\geq 1 ,则

①若 c % g c d ( a , m ) 0 c\%gcd(a,m)\neq0 ,则同余式方程 a x c ( m o d   m ) ax\equiv c(mod\ m) 无解。

②若 c % g c d ( a , m ) = 0 c\%gcd(a,m)=0 ,则同余式方程 a x c ( m o d   m ) ax\equiv c(mod\ m) 恰好由gcd(a,m)个模m意义下不同的解,且解的形式为:

x = x + m g c d ( a , m ) K x'=x+\frac{m}{gcd(a,m)}*K

4.逆元的求解以及(b/a)%m的计算

接着我们来解决最后一个问题,假设a、m是整数,求a模m的逆元

什么是逆元(此处特指乘法的逆元):

假设a、b、m是整数,m>1,且有 a b 1 ( m o d   m ) ab\equiv1(mod\ m) 成立,

那么就说a和b互为模m的逆元,一般记作 a 1 b a\equiv \frac{1}{b} b 1 a b\equiv \frac{1}{a}

通俗的讲就是,如果两个整数的乘积模m后等于1,那么就称它们互为m的逆元

那么逆元有什么用处呢?

对乘法来说有 ( b a ) % m = ( ( b % m ) ( a % m ) ) % m (b*a)\%m=((b\%m)*(a\%m))\%m 成立,但是对除法来说, ( b / a ) % m = ( ( b % m ) / ( a % m ) ) % m (b/a)\%m=((b\%m)/(a\%m))\%m 却不成立, ( b / a ) % m = ( ( b % m ) / a ) % m (b/a)\%m=((b\%m)/a)\%m 也不成立,

例如:如果要对12/4对2取模,采用((12%2)/4)%2会得到错误的结果0,而实际上结果是1。

这时就需要逆元来计算(b/a)%m:

通过找到a模m的逆元x,就有(b/a)%m=(b*x)%m成立(只考虑整数取模,也即假设b%a=0,即b是a的整数倍),于是就把除法取模转化为乘法取模,这对于解决被除数b非常大(使得b已经取过模,不是原始值)的问题来说是非常实用的。

由定义知,求a模m的逆元,就是求解同余式 a x 1 ( m o d   m ) ax\equiv1(mod\ m) ,并且在实际使用中,一般把x的最小正整数解称为a模m的逆元,因此下文中提到的逆元都默认为x的最小正整数解。显然,同余式 a x 1 ( m o d   m ) ax\equiv1(mod\ m) 是否有解取决于1%gcd(a,m)是否为0,而这等价于gcd(a,m)是否为1:

①如果 g c d ( a , m ) 1 gcd(a,m)\neq1 ,那么同余式 a x 1 ( m o d   m ) ax\equiv1(mod\ m) 无解,a不存在模m的逆元。

②如果 g c d ( a , m ) = 1 gcd(a,m)=1 ,那么同余式 a x 1 ( m o d   m ) ax\equiv1(mod\ m) 在(0,m)上有唯一解,可以通过求解ax+my=1得到。

注意:由于gcd(a,m)=1,因此ax+my=1=gcd(a,m),直接使用扩展欧几里得算法解出x之后就可以用 ( x % m + m ) (x\%m+m)%m 得到(0,m)范围内的解,也就是所需要的逆元。

下面代码使用了扩展欧几里得算法来求解a模m的逆元,使用条件是gcd(a,m)=1,当然如果m是素数,就肯定成立了,可以放心使用。

int inverse(int a,int m)
{
	int x, y;
	int g = exGcd(a, m, x, y);   //求解ax+my=1
    return (x%m+m)%m;            //a模m的逆元为(x%m+m)%m
}

补充说明:

如果m是素数,且a不是m的倍数,则还可以直接使用费马小定理来得到逆元,这种做法不需要使用扩展欧几里得算法。

费马小定理:设m是素数,a是任意整数且a不能整除m,则 a m 1 1 ( m o d m ) a^{m-1}\equiv1\pmod m

使用费马小定理来推到逆元的方法非常简单:由 a m 1 1 ( m o d m ) a^{m-1}\equiv1\pmod m 可知 a a m 2 1 ( m o d m ) a*a^{m-2}\equiv1\pmod m ,直接由逆元的定义便可以知道, a m 2 % m a^{m-2}\%m 就是a模m的逆元,而这可以通过快速幂的方法来求解。

发布了119 篇原创文章 · 获赞 22 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/lovecyr/article/details/105372427
今日推荐