欧几里得算法及扩展欧几里得(含)

 

 

 

普通的欧几里得算法

欧几里得算法是用于求两个正整数最大公约数的算法,这个算法十分基础,99%的 OIer / ACMer 都会 XD
如何求最大公约数?
一种十分朴素的方法是枚举。(以下全部默认 $a>b$)若 $a \equiv 0 (\mod b)$,最大公约数为b,否则从 $\frac{a}{2}$ 到 $1$ 枚举试除。然而显然这种做法超时概率为1。
那么有没有快一点的做法呢?
显然是有的,伟大的欧几里得在几千年前就提出了这种做法 orz。
显然,$gcd(a, b)=gcd(b, a\mod b)$,于是只要这样不断递归,当$b=0$时,就可以求出最大公约数,即为a。
给出代码:

int gcd(int a, int b)
{
    return (b == 0) ? a : gcd(b, a % b);
}

证明留给读者自证。
(当然不会这样子啦QAQ)

证:
设 $a = kb + r$(a, b, k, r皆为正整数, 且r<b),则 $r = a \mod b$
设 $d$ 是 $a$ 与 $b$ 的一个公约数, 即$d |a$, $d|b$。
因为$r = a - kb$, 两边同时除以 $d$, r/d=a/d-kb/d=m,由等式右边可知m为整数,因此 $d|r$
因此$d$也是 $b,a mod b$的公约数
假设$d$是$b,a mod b$的公约数, 则$d|b,d|(a-k*b),k$是一个整数。
进而$d|a$.因此$d$也是$a,b$的公约数
因此$(a,b)$和$(b,a mod b)$的公约数是一样的,其最大公约数也必然相等。
得证。
这种算法还有个比较形象的名字叫辗转相除法,时间复杂度为 $O(\lg{b})$
这是如何得到的?

这里

适合高精度的改编版

如果a和b都特别大,需要高精度怎么办?
高精度除以高精度是非常棘手的,通常没人写那种东西。所以我们需要将它转移成别的运算或高精除以int。
代码:

int gcd(int m, int n)
{
    if (m == n)
          return m;
     if (m < n)
          return gcd(n, m);
     if (m & 1)
     {
          if (n & 1)
               return gcd(n, m - n);
          return gcd(m, n >> 1);
     }
     if (n & 1)
          return gcd(m >> 1, n);
     return gcd(m >> 1, n >> 1);
}

大致就是这个意思啦,我没写高精度,不过像除以2,判断奇偶这种东西是很容易实现的。这么做就很简单了。

扩展欧几里得算法

扩欧通常是用来求$ax+by=c$这类方程的整数解。比如方程 $x+2y=3$的整数解为$x=1-2t,y=1+t ( t∈Z)$。
需要注意的是,当$c$不为 gcd(a,b) 的倍数时,方程是没有整数解的(想想看为什么?)。
通过我们平时手算丢番图方程的过程,可以发现可以这么做:(下面的这段伪代码摘自《算法导论》的31.2)

EXTENDED-EUCLID(a,b)
    if b==0
        return(a,1,0)
    else(d',x',y')=EXTENDED-EUCLID(b,a mod b)
        (d,x,y)=(d',y',x'-[a/b]y')
        return(d,x,y)

这样可以求出一组解(然后就很容易得出通解)并且还顺便求出了gcd(a,b)的值——因为其实扩欧是普通辗转相除法的一点变形。

下面是exgcd的代码:

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

P.S.对于解丢番图方程、辗转相除法证,其他方法 可以看看 R·柯朗的《什么是数学?》中讲数论的那一部分,还是写得不错的。

posted @ 2018-10-28 15:44  no_proper_name_left  阅读(235)  评论(0编辑  收藏  举报