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


    普通的欧几里得算法

    欧几里得算法是用于求两个正整数最大公约数的算法,这个算法十分基础,99%的 OIer / ACMer 都会 XD
    如何求最大公约数?
    一种十分朴素的方法是枚举。(以下全部默认 $a>b$)若 $a equiv 0 (mod b)$,最大公约数为b,否则从 $frac{a}{2}$ 到 $1$ 枚举试除。然而显然这种做法超时概率为1。
    那么有没有快一点的做法呢?
    显然是有的,伟大的欧几里得在几千年前就提出了这种做法 orz。
    显然,$gcd(a, b)=gcd(b, amod 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·柯朗的《什么是数学?》中讲数论的那一部分,还是写得不错的。

  • 相关阅读:
    python学习笔记——拾
    python学习笔记——玖
    Python 实现栈与队列
    Vijos1774 机器翻译 [模拟]
    Vijos1788 第K大 [模拟]
    Python 序列求和
    HDU 2102 A计划 DFS与BFS两种写法 [搜索]
    Python 多组输入
    Python 文件读写
    HDU 2068 RPG错排 [错排公式]
  • 原文地址:https://www.cnblogs.com/InedibleKonjac/p/12654965.html
Copyright © 2020-2023  润新知