欧几里得与扩展欧几里得算法

欧几里得

它是我们求解整数 $a,b$ 的最大公约数的算法。

前置知识

$max(a,b)+min(a,b)=a+b$

$max(a,b)=a+b-min(a,b),min(a,b)=a+b-max(a,b)$

过程

不妨设 $a>b$

我们发现如果 $b$ 是 $a$ 的约数,那么 $b$ 就是 $a,b$ 的最大公约数,现在考虑 $b$ 不是 $a$ 的约数时的情况。

设 $a=b \times q+r$,其中 $r<b$,我们可以通过证明 $ gcd(a,b)=gcd(b,a\mod b)$。

此时显然有 $r=a \mod b$,则 $r=a-b \times q$,设 $d|a,d|b$,则 $\frac{r}{d}=\frac{a}{d}-\frac{b}{d}q$。

由右边式子可知 $\frac{r}{d}$ 为整数,即 $d|r$ ,所以对于 $a,b$ 的公约数,它也会是 $b,a \mod b$ 的公约数。

反过来也需证明,设 $d|a\mod b,d|b$,则 $\frac{a \mod b}{d}+\frac{b}{d}q=\frac{a}{d}$,由左边式子可知 $\frac{a}{d}$ 为整数,即 $d|a$,所以 $b,a \mod b$的公约数也会是 $a,b$ 的公约数。

既然两式的公约数相同,则两式的最大公约数也一定相同。

所以得到式子 $gcd(a,b)=gcd(b,a \mod b)$。

注:根据以上说明,可知两个数的最大公约数的约数一定包含两个数的公约数

代码

int gcd(int a, int b) {
  while (b != 0) {
    int tmp = a;
    a = b;
    b = tmp % b;
  }
  return a;
}

多个数的最大公约数

显然答案是每个数的约数,那么也一定等于每相邻两个数的约数,那么我们每次取出两个数求出它们的最大公约数,再放回去,然后删除这两个数,此时这个公约数就包含了两个数的所有约数。 直至剩下的数的个数为 $1$ ,就停止。

最小公倍数

两个数

根据算术基本定理,设 $a=p1^{ka_1}p2^{ka_2}...ps^{ka_s},b=p1^{kb_1}p2^{kb_2}...ps^{kb_s}$

我们发现两者的最大公因数为:

$p1^{min(ka_1,kb_1)}p2^{min(ka_2,kb_2)}...ps^{min(ka_s,kb_s)}$

最小公倍数为:

$p1^{max(ka_1,kb_1)}p2^{max(ka_2,kb_2)}...ps^{max(ka_s,kb_s)}$

由于 $k_a+k_b=max(k_a,k_b)+min(k_a,k_b)$。

所以得到的结论为 $gcd(a,b) \times lcm(a,b)=a \times b$。

根据结论,先求出两数的最大公因数,再求出两数的最小公倍数即可。

多个数

对于一个数列 $a_1,a_2,...,a_n$ ,所以 $a_1=p_1^{b1_1}p_2^{b1_2}...p_s^{b1_s},a_2=p_1^{b2_1}p_2^{b2_2}...p_s^{b2_s}...,a_n=p_1^{bn_1}p_2^{bn_2}...p_s^{bn_s}$ ,还是一样,假设现在处理到第$i$个数,前$i-1$个数的最小公倍数为 $m$ ,很显然 $m=p_1^{max(b1_1,b2_1,...,bi-1_1)}p_2^{max(b1_2,b2_2,...,bi-1_2)}...p_s^{max(b1_s,b2_s,...,bi-1_s)}$,$a_i=p_1^{bi_1}p_2^{bi_2}...p_s^{bi_s}$,所以前 $i$ 个数的最小公倍数应为 $m1=p_1^{max(b1_1,b2_1,...,bi_1)}p_2^{max(b1_2,b2_2,...,bi_2)}...p_s^{max(b1_s,b2_s,...,bi_s)}$ ,我们发现 $gcd(m,a_i)=p_1^{min(max(b1_1,b2_1,...,bi-1_1),bi_1)}p_2^{min(max(b1_2,b2_2,...,bi-1_2),bi_2)}...p_s^{min(max(b1_s,b2_s,...,bi-1_s),bi_s)}$

$m \times a_i=p_1^{max(b1_1,b2_1,...,bi-1_1)+bi_1}p_2^{max(b1_2,b2_2,...,bi-1_2)+bi_2}...p_s^{max(b1_s,b2_s,...,bi-1_s)+bi_s}$

则 $m \times a_i \div gcd(m,a_i)=p1^{max(b1_1,b2_1...,bi-1_1)+bi_1-min(max(b1_1,b2_1,...,bi-1_1),bi_1)}...ps^{max(b1_s,b2_s...,bi-1_s)+bi_s-min(max(b1_s,b2_s,...,bi-1_s),bi_s)}$

当 $max(b1_1,b2_1...bi-1_1) \leq bi_1$时,$max(b1_1,b2_1...,bi-1_1)+bi_1-min(max(b1_1,b2_1,...,bi-1_1),bi_1)=bi_1$,

反之有$max(b1_1,b2_1...,bi-1_1)+bi_1-min(max(b1_1,b2_1,...,bi-1_1),bi_1)=max(b1_1,b2_1...,bi-1_1)$ 。

所以最终结果均满足是最大值,故得证。

方法二:

$m \times a_i \div gcd(m,a_i)=p1^{max(b1_1,b2_1...,bi-1_1)+bi_1-min(max(b1_1,b2_1,...,bi-1_1),bi_1)}...ps^{max(b1_s,b2_s...,bi-1_s)+bi_s-min(max(b1_s,b2_s,...,bi-1_s),bi_s)}$

将 $max(b1_1,b2_1...,bi-1_1)+bi_1-min(max(b1_1,b2_1,...,bi-1_1),bi_1)$ 的 $max(b1_1,b2_1...,bi-1_1)$ 看作 $A$ ,$bi_1$ 看作 $B$ ,则原式化为 $A+B-min(A,B)$ 根据前置知识可知 $max(A,B)=A+B-min(A,B)$ 。

故得证。

代码如下:

int gcd(int a,int b)
{
    if(!b)return a;
    return gcd(b,a%b);
}
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
int m=a[1];
for(int i=2;i<=n;i++)
    m=m*a[i]/gcd(m,a[i]);

裴蜀定理

对于任意正 整数 $a$ ,$b$,存在一对整数 $x,y$ ,满足 $ax+by=gcd(a,b)$

感性理解一下,我们发现因为 $x,y$ 为整数,所以 $ax+by$ 为 $gcd(a,b)$ 的倍数,所以一定存在一组 $x',y'$ 使得 $ax+by=gcd(a,b)$。

那么感性理解完毕,我们开始证明。

设 $d=gcd(a,b)$,则 $ax+by=\frac{a}{d}\times d\times x+\frac{b}{d}\times d\times y=\frac{c}{d}\times d$。

两边去掉 $d$ 得:$\frac{a}{d}\times x+\frac{b}{d}\times y=\frac{c}{d}$,因为 $\frac{a}{d}$ 为整数,$\frac{b}{d}$ 也为整数, $x,y$ 也为整数,根据“整数 $\times$ 整数 $=$ 整数,整数 $+$ 整数 $=$ 整数”的封闭法则得 $\frac{c}{d}$ 一定为整数,也就是 $d|a$。

故得证。

扩展欧几里得

它是一般我们求解形如 $ax+by=c$ 的方程时的算法。

根据裴蜀定理,我们可以很轻松地知道 $gcd(a,b)|c$ 时才会有整数解。

设 $ax_1+by_1=gcd(a,b)$

$bx_2+(a \mod b)y_2=gcd(b,a\mod b)$

由欧几里得定理可知 $gcd(a,b)=gcd(b,a \mod b)$

所以 $ax_1+by_1=bx_2+(a \mod b)y_2$

又因为 $a \mod b=a- \lfloor \frac{a}{b} \rfloor\times b$,

所以上式化为: $ax_1+by_1=bx_2+(a- \lfloor \frac{a}{b} \rfloor\times b)y_2$

所以:$ax_1+by_1=bx_2+ay_2- \lfloor \frac{a}{b} \rfloor\times b\times y_2=ay_2+b\times(x_2-\lfloor \frac{a}{b}\rfloor \times y_2)$

假设我们已经先求出了 $x_2,y_2$,因为 $a=a$,$b=b$,则 $x_1=y_2$,$y_1=(x_2-\lfloor \frac{a}{b}\rfloor \times y_2)$.

所以求解这种问题就可以转化成求解最大公约数的过程了。

注:边界情况当 $b=0$ 时,所以 $gcd(a,b)=a$,$ax'+by'=gcd(a,b)=a$。

则原式化为:$ax'=a$,则 $x'=1,y'$ 取任意实数,但一般如果你不想爆 $long~~long$ 的话,最好是 $y'=0$ 。

代码

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

例题:青蛙的约会

显然,青蛙$A$走的路程为$x+km$,青蛙$B$走的路程为$y+kn$,则题意要求$x+km\mod L=y+kn \mod L$,即$x+km \equiv y+kn(\mod L)$。

将上式化为一般方程为:$k(m-n)+zL=y-x$,因为$m,n,L,y,x$都是已知量,所以这个方程就相当于关于$k,z$的二元一次不定方程。

$Code$

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
int exgcd(int a,int b,LL &x,LL &y){
    if(b==0){
        x=1,y=0;
        return a;
    }
    int d=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return d;
}
int main(){
    int a,b,m,n,l,f;
    //cin>>f;
    //while(f--){
        cin>>a>>b>>m>>n>>l;
        LL x=0,y=0;
        int d=exgcd(m-n,l,x,y);
        if((b-a)%d!=0){
            cout<<"Impossible"<<endl;
        }else{
            int t3=b-a;
            x=x*t3/d;
            y=y*t3/d;
            int t=abs(l/d);
            cout<<(x+t)%t<<endl;
        }
    //}
    return 0;
}
posted @ 2023-12-22 23:33  wangyuanbo  阅读(20)  评论(0)    收藏  举报  来源