gcd and exgcd

  • 数论入门基础
    • 最大公约数
      • 问题1
      • 解法1
      • 解法2
      • 求最大公约数的欧几里德算法
    • 扩展欧几里得
      • 问题
      • 解法

数论入门基础

最大公约数

问题1

给定平面上的两个格点

P1=(x1,y1)
P2=(x2,y2)
求线段P1P2上有几个格点
109<=x1,x2,y1,y2<=109

解法1

枚举所有的合法的

min(x1,x2)<=x<=max(x1,x2)

min(y1,y2)<=y<=max(y1,y2)

判断 (x,y) 是否在线段上

数据范围大,我们需要更快的算法

解法2

假设我们从(x1,y1)出发已经找到了离它最近的一个格点P0,那么从P0出发走相同长度肯定也是一个整数格点,就这样一路找下去,最终恰好能找到(x2, y2),中间的每一段都是等长的,你可以假设最终到的点不是(x2,y2),而是延伸出去的某个点,那么显然P0就不是离(x1, y1)最近的一个格点了。
所以我们证明了一个性质

线段上的所有的整数格点恰好都在这条线段的K等分点上

要满足等分点都是整数,所以K必须为X距离和Y距离的公约数,我们需要求最大的K,所以就是两个距离的最大公约数了

求最大公约数的欧几里德算法

  • 更相减损术
  • 辗转相除法

这两种方法是一样的,下面是上面的优化

int gcd(int a, int b) {
    while(a != b) {
        if (a > b) {
            a -= b;
        } else {
            b -= a;
        }
    }
    return a;
}
int gcd(int a, int b) {
    if(b == 0) return a;
    return gcd(b, a % b);
}

为什么这样子做是对的呢?
假设 g 为a b的某个公约数,那么对于任意一个g都有如下两个式子

a=k1g

b=k2g

a b更相减损,变的只是前面的k,无论怎么减g还是他们的约数,一直到最后两个数变成相同的,g还是他们的约数 这个时候他们相视一笑,发现原来你也在这里 也就是说

在更相减损的过程当中,所有的公约数信息都没有丢,也不会增加,跟原来一样,但是数据变小了哦,那么一直变小会发生什么呢?

对于任意一个公约数g都满足最后变成的两个相等的数为g的倍数。所以辗转相减之后的数也是最大公约数 gmx 的倍数,假设最后得到的两个数是

k1g
k2g

既然所有的公约数信息都没丢,这两个数的公约数也是原来a b的公约数,那么 k1k2 的值必定为1,不然就存在某个比g大的公约数了,与g是最大公约数矛盾。

其实上面的辗转相减法,每次就是最大公约数的两个系数k1, k2在辗转相减,最终变成1(他们的最大公约数是1),这个辗转相减的过程可以描述为

k1x+k2y=1

所以这个方程其实一定有解,因为刚才已经证明k1 k2辗转相减一定能得到他们的最大公约数1。

扩展欧几里得

问题

一个游戏
有向前向后无线延续的格子, 每个格子都写有整数。其中0号格子是起点, 1号格子是终点。 而骰子上只有a,b,-a,-b四个整数,所以根据a和b的值得不同,有可能无法到达终点。掷出四个整数各多少次可以到达终点?

解法

上述问题归结起来就是

ax+by=1;

如果
gcd(a,b)!=1
,无解。
实际上一定存在整数对(x, y)使得
ax+by=gcd(a,b)

假设已经求得了

1: bx+(a%b)y=gcd(a,b)

的整数解 (x,y)

a%b=a(a/b)b;

代入可得
bx+(a(a/b)b)y=gcd(a,b)

化简一下

2: ay+b(x(a/b)y)=gcd(a,b)

所以,解出了1式,就可以由2式得到原方程的解
特殊的,当b = 0时,

a1+b0=gcd(a,b)=a;

下面的代码解决 ax+by=gcd(a,b)

int extgcd(int a, int b, int &x, int &y) {
    int d = a;
    if(b != 0) {
        d = extgcd(b,  a % b,  x,  y);
        x -= (a / b) * y;
        std::swap(x,  y);
    } else {
        x = 1; y = 0; // g x+ 0 * y = g, y can be any number
    }
    return d;
}

递归算到最后y其实可以取任意整数。但是取的太大容易导致使得最后算出来的解爆int之类的事情
下面我们来看一下最终算出来的解 x0 y0 的绝对值大小情况
由x y的计算方法x -= (a / b) * y可以知道,x y始终是在max(|a|, |b|)的绝对值范围内的,因此a*x + b*y = gcd(a, b)最后算出的x y的绝对值大小跟a b是同一个级别的。
扩展欧几里德其实就是欧几里德,欧几里德其实就是辗转相减,全是一样的!

通解:

你可能感兴趣的:(math,math,gcd)