贝祖定理与扩展欧几里得算法详解

贝祖定理

  • 定义

    给定两个整数 a a a b b b,一定存在整数 x x x y y y,使得 a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)

    换句话说,若 a x + b y = m ax+by=m ax+by=m有整数解当且仅当 m m m g c d ( a , b ) gcd(a,b) gcd(a,b)的倍数。

  • 证明

    以下 d ∣ a d|a da代表 d d d a a a的因子

    对于 a x + b y = c ax+by=c ax+by=c,设 d = g c d ( a , b ) d=gcd(a,b) d=gcd(a,b),则 d ∣ a , d ∣ b d|a,d|b da,db。所以我们可以得出: ∀ x , y ∈ Z \forall x,y\in Z x,yZ d ∣ a x , d ∣ b y d|ax,d|by dax,dby,显然要使得之前的式子成立,则必须满足 c c c a a a b b b的公约数的倍数,又因为 x , y ∈ Z x,y\in Z x,yZ,所以 c c c必然是 a , b a,b a,b最大公约数的倍数,故此定理成立。

  • 升级->新定义/重要推论

    我们由定义可知, a x + b y = g c d ( a , b ) ax+by=gcd(a,b) ax+by=gcd(a,b)那么如果我们左右两边同时除以最大公约数 g c d ( a , b ) gcd(a,b) gcd(a,b),那么就得到了 p x + q y = 1 px+qy=1 px+qy=1,其中 p , q p,q p,q互质,而我们又知道一定存在一组解使得这个等式满足,所以我们可以得到:

    如果两个整数 a , b a,b a,b是互质的,那么 a x + b y = 1 ax+by=1 ax+by=1一定有整数解。

  • 推广

    对于方程 a x + b y + c z + . . . . + n m = f ax+by+cz+....+nm=f ax+by+cz+....+nm=f(其中 a , b , c . . . n , f a,b,c...n,f a,b,c...n,f为整数)有解的充要条件就是 f f f g c d ( a , b , c . . . n ) gcd(a,b,c...n) gcd(a,b,c...n)的整数倍。

  • 应用

    由推广我想我们就已经知道了,即若给定我们一个不定式,需要我们求解 ∑ i = 0 n a i x i \sum_{i=0}^n a_ix_i i=0naixi的值最小,其中最小值必须为正数,那么我们就可以直接根据贝祖定理求解得到。即 g c d ( a 1 . . . . a n ) gcd(a_1....a_n) gcd(a1....an)

  • 贝祖定理模板例题—洛谷P4529

    • 原题链接

    • AC代码

      /**
        *@filename:贝祖定理
        *@author: pursuit
        *@CSDNBlog:unique_pursuit
        *@email: 2825841950@qq.com
        *@created: 2021-03-28 20:54
      **/
      #include <bits/stdc++.h>
      
      using namespace std;
      
      typedef long long ll;
      const int maxn = 100000 + 5;
      const int mod = 1e9+7;
      
      int n;
      int a[maxn];
      int gcd(int n,int m){
          return m?gcd(m,n%m):n;
      }
      void solve(){
          int result=0;
          for(int i=0;i<n;i++){
              result=gcd(result,a[i]);
          }
          cout<<result<<endl;
      }
      int main() {
          while(cin>>n){
              int temp;
              for(int i=0;i<n;i++){
                  //将负数全都更改为整数。
                  cin>>a[i];
                  if(a[i]<0){
                      a[i]*=-1;
                  }
              }
              solve();
          }
          return 0;
      }
      

扩展欧几里得算法

由上我们已知了贝祖定理的定义,那么这个算法其实就是为了解决贝祖定理,求解出整数解 ( x , y ) (x,y) (x,y)。我们知道对于欧几里得算法,它总是把 g c d ( a , b ) gcd(a,b) gcd(a,b)转化为求解 g c d ( b , a m o d    b ) gcd(b,a\mod b) gcd(b,amodb),而当b变为0时返回a,此时的 a a a就等于 g c d gcd gcd。也就是说,欧几里得算法结束时变量 a a a中存放的是 g c d gcd gcd,变量 b b b中存放的是 0 0 0,因此此时显然有 a × 1 + b × 0 = g c d a × 1 + b × 0 = g c d a×1+b×0=gcd成立,此时有 x = 1 、 y = 0 x=1、y=0 x=1y=0成立。我们先看一下证明。

  • 证明

    x = x 1 , y = y 1 x=x_1,y=y_1 x=x1,y=y1是该方程的一个解,那么
    a x 1 + b y 1 = g c d ( a , b )   ( 1 ) ax_1+by_1=gcd(a,b)\space (1) ax1+by1=gcd(a,b) (1)

    再设 x = x 2 , y = y 2 x=x_2,y=y_2 x=x2,y=y2是方程 b x + ( a m o d    b ) y = g c d ( b , a m o d    b ) bx+(a\mod b)y=gcd(b,a\mod b) bx+(amodb)y=gcd(b,amodb)的一个解。即:
    b x 2 + ( a m o d    b ) y 2 = g c d ( b , a m o d    b )   ( 2 ) bx_2+(a\mod b)y_2=gcd(b,a\mod b)\space (2) bx2+(amodb)y2=gcd(b,amodb) (2)
    而根据欧几里得算法可知:
    g c d ( a , b ) ≡ g c d ( b , a m o d    b )   ( 3 ) gcd(a,b)\equiv gcd(b,a\mod b)\space (3) gcd(a,b)gcd(b,amodb) (3)
    (这个非常好理解,这里不予阐述证明)。所以我们由 ( 1 ) ( 2 ) ( 3 ) (1)(2)(3) (1)(2)(3)式可以得到 a x 1 + b y 1 = b x 2 + ( a m o d    b ) y 2   ( 4 ) ax_1+by_1=bx_2+(a\mod b)y_2\space (4) ax1+by1=bx2+(amodb)y2 (4),即:
    a x 1 + b y 1 = b x 2 + ( a − a / b × b ) y 2 = a y 2 + b ( x 2 − a / b × y 2 )   ( 5 ) ax_1+by_1=bx_2+(a-a/b\times b)y_2=ay_2+b(x_2-a/b\times y_2)\space (5) ax1+by1=bx2+(aa/b×b)y2=ay2+b(x2a/b×y2) (5)
    一一对应我们可得:

    x 1 = y 2 , y 1 = ( x 2 − a / b × y 2 )   ( 6 ) x_1=y_2,y_1=(x_2-a/b\times y_2)\space (6) x1=y2,y1=(x2a/b×y2) (6)

    通过上述步骤,我们求解得到了 x 1 , y 1 x_1,y_1 x1,y1,那么我们按此步骤叠下去,即是一个递归求解的过程。根据欧几里得算法,我们一直往下,而最终我们知道 b = 0 , a = g c d ( a , b ) b=0,a=gcd(a,b) b=0,a=gcd(a,b),此时易知 x = 1 , y = 0 x=1,y=0 x=1,y=0为最后求得方程的解,那么我们再回溯回去计算,得到最终的 x 1 , y 1 x_1,y_1 x1,y1。故得证。

  • 步骤

    由上证明实际上我们就是根据公式 ( 6 ) (6) (6) x 1 = y 2 , y 1 = ( x 2 − a / b × y 2 ) x_1=y_2,y_1=(x_2-a/b\times y_2) x1=y2,y1=(x2a/b×y2)来进行推导的。我们递归去得到最后解为 x = 1 , y = 0 x=1,y=0 x=1,y=0,再反推回来得到 x 1 , y 1 x_1,y_1 x1,y1。就这么简单。

  • 代码

    int exGcd(int a,int b,int &x,int &y){
        //利用引用最后x,y就是我们想要的值。
        if(b==0){
            //说明到达已知解。
            x=1;
            y=0;
            return a;
        }
        int gcd=exGcd(b,a%b,x,y);//递归到最后寻找到我们已知的解。
        //开始回溯,通过已知解,利用公式返回。
        int temp=x;//temp=x2
        x=y;//x1=y2;
        y=temp-a/b*y;//y1=(x2-a/b*y2)。
        return gcd;
    }
    
  • 升级->求解全部解

    我们设新解为 x 1 + s 1 , y 1 − s 2 x_1+s_1,y_1-s2 x1+s1,y1s2,则有 a × ( x 1 + s 1 ) + b × ( y 1 − s 2 ) = g c d a\times (x_1+s_1)+b\times(y_1-s_2)=gcd a×(x1+s1)+b×(y1s2)=gcd成立,带入我们的公式中可得到 a s 1 = b s 2 as_1=bs_2 as1=bs2,所以 s 1 s 2 = b a \frac{s_1}{s_2}=\frac{b}{a} s2s1=ab,则我们可以对 b , a b,a b,a约分得到 s 1 , s 2 s_1,s_2 s1,s2的最小值,即为 b / g c d , a / g c d b/gcd,a/gcd b/gcd,a/gcd,故全部解为:

    ⟮ x = x 1 + b g c d × k , y = y 2 − a g c d × k \lgroup {x=x_1+\frac{b}{gcd}\times k,y=y_2-\frac{a}{gcd}\times k} x=x1+gcdb×k,y=y2gcda×k,其中 k ∈ Z k\in Z kZ。而其中的最小非负整数解即为: ( x % b g c d + b g c d ) % b g c d (x\%\frac{b}{gcd}+\frac{b}{gcd})\%\frac{b}{gcd} (x%gcdb+gcdb)%gcdb

  • 应用一:求解方程 a x + b y = c ax+by=c ax+by=c

    我们知道怎么求解 a x + b y = g c d ax+by=gcd ax+by=gcd之后,那么我们就应该要知道怎么求解 a x + b y = c ax+by=c ax+by=c了,其中 c ∈ Z c\in Z cZ。首先我们先求解出 a x + b y = g c d ax+by=gcd ax+by=gcd的解 ( x 1 , y 1 ) (x_1,y_1) (x1,y1),那么我们对两边同时乘以 c g c d \frac{c}{gcd} gcdc即可得到该方程的解了。对于获取全部解同上我们可以假设证明得到。

  • 应用二:同余方程 a x ≡ c ( m o d    m ) ax\equiv c(\mod m) axc(modm)的解

    我们先来解释一下什么是同余式吧:对于 a , c a,c a,c来说,如果 ( a − c ) % m = 0 (a-c)\%m=0 (ac)%m=0,那么就说 a a a c c c m m m同余,我们记作 a ≡ c ( m o d    m ) a\equiv c(\mod m) ac(modm),那么 m m m就成为同余式 m m m的模。

    那么我们如何通过扩展欧几里得算法来解决同余方程 a x ≡ c ( m o d    m ) ax\equiv c(\mod m) axc(modm)的解呢?

    我们可知有 ( a x − c ) % m = 0 (ax-c)\%m=0 (axc)%m=0成立,那么设存在整数 y y y使得 a x − c = m y ax-c=my axc=my,我们通过移项并令 y = − y y=-y y=y,即得到 a x + m y = c ax+my=c ax+my=c,这个不就是我们上面的方程吗?那么我们知道当 c % g c d ( a , m ) = = 0 c\%gcd(a,m)==0 c%gcd(a,m)==0时才有解,那么我们就可以根据这个判断是否有解。而对于解的形式我们可以先求出一组解,然后通过全解公式得到 x ′ = x + m g c d ( a , m ) × k , y ′ = y − m g c d ( a , m ) × k x'=x+\frac{m}{gcd(a,m)}\times k,y'=y-\frac{m}{gcd(a,m)}\times k x=x+gcd(a,m)m×k,y=ygcd(a,m)m×k k ∈ Z k\in Z kZ )。那么此问题易得。我们整理如下:

    • c % g c d ( a , m ) c\%gcd(a,m) c%gcd(a,m) ≠ 0 ≠0 =0,那么同余方程无解,
    • 否则,通解为: x ′ = x + m g c d ( a , m ) × k x'=x+\frac{m}{gcd(a,m)}\times k x=x+gcd(a,m)m×k。最小正整数解为: ( x % m g c d ( a , m ) + m g c d ( a , m ) ) % m g c d ( a , m ) (x\%\frac{m}{gcd(a,m)}+\frac{m}{gcd(a,m)})\%\frac{m}{gcd(a,m)} (x%gcd(a,m)m+gcd(a,m)m)%gcd(a,m)m
  • 应用二例题:洛谷P1082同余方程

    • 原题链接

    • AC代码

      /**
        *@filename:同余方程
        *@author: pursuit
        *@CSDNBlog:unique_pursuit
        *@email: 2825841950@qq.com
        *@created: 2021-03-29 12:31
      **/
      #include <bits/stdc++.h>
      
      using namespace std;
      
      typedef long long ll;
      const int maxn = 100000 + 5;
      const int mod = 1e9+7;
      
      ll a,b;
      ll x,y;//存储解。
      ll exGcd(ll a,ll b,ll &x,ll &y){
          if(b==0){
              x=1;
              y=0;
              return a;
          }
          int gcd=exGcd(b,a%b,x,y);
          int temp=x;//temp=x2;
          x=y;//x1=y2;
          y=temp-a/b*y;//y1=x2-a/b*y2;
          return gcd;
      }
      void solve(){
          int gcd=exGcd(a,b,x,y);
          //此时我们得到了x,y。利用公式求解最小值。
          cout<<((x%(b/gcd)+(b/gcd))%(b/gcd))<<endl;
      }
      int main() {
          while(cin>>a>>b){
              solve();
          }
          return 0;
      }
      
  • 应用三:逆元的求解

    我们首先解释一下什么是逆元:

    假设 a 、 b 、 m a、b、m abm是整数, m > 1 m>1 m>1,且有 a b ≡ 1 ( m o d    m ) a b ≡ 1 (\mod m) ab1(modm)成立,

    那么就说 a a a b b b互为模m的逆元,一般记作 a ≡ 1 b a\equiv \frac{1}{b} ab1或者 b ≡ 1 a b\equiv \frac{1}{a} ba1。通俗的讲就是,如果两个整数的乘积模m后等于1,那么就称它们互为m的逆元。

    而逆元的作用就是可以在取模的时候等价代换成分数,将除法运算变成乘法运算。即若对于 ( x / y ) % m (x/y)\%m (x/y)%m,我们没有除法的取模分配,这个时候我们就可以求出 y y y的逆元(假设求得为 q q q),那么我们就可以转换为 ( x ∗ q ) % m = ( x % m ) ∗ ( q % m ) (x*q)\%m=(x\%m)*(q\%m) (xq)%m=(x%m)(q%m)

    那么即要求我们求解同余式方程 a x ≡ 1 ( m o d    m ) ax\equiv 1(\mod m) ax1(modm),这个我们就可以参考应用二来解决了,最后代入到实际引用中,同样,对于逆元是要求越小越好,我们可以求出最小正整数解。

    当然对于逆元的求解我们还可以使用费马小定理,若对这个不太清楚的还请阅读费马小定理

  • 应用三模板例题:HDU1576

    • 扩展欧几里得算法

      /**
        *@filename:B
        *@author: pursuit
        *@CSDNBlog:unique_pursuit
        *@email: 2825841950@qq.com
        *@created: 2021-03-29 17:24
      **/
      #include <bits/stdc++.h>
      
      using namespace std;
      
      typedef long long ll;
      const int maxn = 100000 + 5;
      const int mod = 1e9+7;
      
      int exGcd(int a,int b,int &x,int &y){
          if(b==0){
              x=1;
              y=0;
              return a;
          }
          int gcd=exGcd(b,a%b,x,y);
          int temp=x;//temp=x2;
          x=y;//x1=y2;
          y=temp-a/b*y;//y2=x2-a/b*y1;
          return gcd;
      }
      int n,b;
      void solve(){
          int x,y;
          int gcd=exGcd(b,9973,x,y);
          x=(x%(9973/gcd)+9973/gcd)%(9973/gcd);
          cout<<(n*(x%9973))%9973<<endl;
      }
      int main() {
          int t;
          while(cin>>t){
              while(t--){
                  cin>>n>>b;
                  solve();
              }
          }
          return 0;
      }
      
    • 费马小定理

      /**
        *@filename:B
        *@author: pursuit
        *@CSDNBlog:unique_pursuit
        *@email: 2825841950@qq.com
        *@created: 2021-03-29 17:24
      **/
      #include <bits/stdc++.h>
      
      using namespace std;
      
      typedef long long ll;
      const int maxn = 100000 + 5;
      const ll mod = 9973;
      
      
      ll n,b;
      ll quick_pow(ll a,ll b){
          ll ans=1;
          b%=mod;
          while(b){
              if(b&1)ans=ans*a%mod;
              a=a*a%mod;
              b>>=1;
          }
          return ans;
      }
      void solve(){
          ll res=quick_pow(b,mod-2);
          cout<<res*n%mod<<endl;
      }
      int main() {
          int t;
          while(cin>>t){
              while(t--){
                  cin>>n>>b;
                  solve();
              }
          }
          return 0;
      }
      
posted @   unique_pursuit  阅读(91)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示