浅谈拓展欧几里得算法

拓展欧几里得是基于欧几里得算法的,在数论中算比较基础的一块了,那么今天就来讲一讲它。

前置技能


  1. 欧几里得算法

    就是GCD,辗转相除法求最大公约数,数论中没有比这个更简单的了。很多人都会想到,就像高斯消元一样,这么叫不过是因为他们出生比较早,当然,死的也比较早。

    如果还有不懂,自行百度。

  2. 裴蜀定理

    裴蜀定理是一个关于最大公约数的定理,说明了对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性丢番图方程看不懂就当我没说,大概意思如下:

    ax + by = m

    有解当且仅当m是d的倍数 (d为a、b最大公约数总看得懂了吧)

基本事实


所求

拓展欧几里得求的是:已知 ax + by = m ,求一组整数解(x,y)(a,b,c均为整数 )。

简化

由裴蜀定理可得:令 ax + by = m简化为 ax + by = c (反正m为c倍数)。

还可以得到另外一个结论,也挺有用 :ax + by = 1有整数解当且仅当GCD(a,b)=1(即a与b互质)

推导求解


  1. 对(a,b)做辗转相除法直到(c,0) 很显然,不是吗
  2. ​ 由于c + 0 = c,可以得到此时必有一解(x,y) = (1,0) (事实上y是任意的,只不过为了防止造数据的无脑…你懂的)
  3. 假设现在我们已知bx0 + (a mod b)y0 = c,如何求出ax + by = c的解?(为什么要这么写?参考GCD的过程)
  4. 因为a mod b比较难操作,所以要先将上式改写为bx0 + (a – [a/b] * b)y0 = c(形如a mod b的式子,一般来说在推导过程中都要化为a – [a/b] * b)
  5. 在做GCD时递归即可

上代码


#include<cstdio>
using namespace std;
int a,b;
struct ANS{
    int x,y;
}ans;
void gcd(int x,int y){
    if(!y) {ans.x=1;ans.y=0;}
    else{
        gcd(y,x%y);
        int t=ans.x;
        ans.x=ans.y;
        ans.y=t-((int)(x/y))*ans.y;
    }
}
int main(){
    scanf("%d%d",&a,&b);
    gcd(a,b);
    printf("%d %d\n",ans.x,ans.y);
    return 0;
}

顺便给一个Python版的代码

class ANS:
    x=0
    y=0
    def __init__(self):
        self.x=0
        self.y=0


def gcd(x,y):
    global ans
    if not y:
        ans.x=1
        ans.y=0
    else:
        gcd(y,x%y)
        t=ans.x
        ans.x=ans.y
        ans.y=t-(int(x/y))*ans.y


ans=ANS()
a=0
b=0
a,b = [int(j) for j in raw_input().split()]
gcd(a,b)
print str(ans.x)+' '+str(ans.y)

猜你喜欢

转载自blog.csdn.net/weixin_41811117/article/details/80968315