zoukankan      html  css  js  c++  java
  • 拓展欧几里得算法

    写在前面:

      这篇博客是我在[◹]对 算术基本定理 的研究 中的一部分

    by celebi-yoshi

    目录

    1. 已知正整数a,b,求一组p,q使满足p*a+q*b ≡ 0 (MOD GCD(a,b))

    2. 已知正整数a,b,素数p,求一个整数x使满足x*a ≡ b (MOD p)

    3. 已知正整数a,b,素数p,求一个整数x使满足ax ≡ b (MOD p)

    4. 已知正整数a,b,c,求一个整数x使满足x*a ≡ b (MOD c)

    5. 已知正整数a,b,c,求一个整数x使满足ax ≡ b (MOD c)

    6. 解线性同余方程组 

    结论

      拓展欧几里得算法能解决的问题有:

    已知正整数a,b,求一组p,q使满足p*a+q*b ≡ 0 (MOD GCD(a,b))

    已知正整数a,b,素数p,求一个整数x使满足x*a ≡ b (MOD p)

    已知正整数a,b,素数p,求一个整数x使满足ax ≡ b (MOD p)

     已知正整数a,b,c,求一个整数x使满足x*a ≡ b (MOD c)

     已知正整数a,b,c,求一个整数x使满足ax ≡ b (MOD c)

    解线性方程组

    证明

    • 已知正整数a,b,求一组p,q使满足p*a+q*b ≡ 0 (MOD GCD(a,b))

      现在已经有了a-b == GCD(a,b) * (n1-n2),那就可以再来个直接点的

      GCD(a,b) + (p*n1+q*n2) == GCD(a,b)

      即p*a+q*b == GCD(a,b)

      ([]Bézout引理)

      想要求出一组p,q∈Z,满足上式

     

      首先套用欧几里得算法的逻辑:

        结合算术基本定理,有

          GCD(a,b) == P1min(a1,b1)P2min(a2,b2)......Pnmin(an,bn)

          a MOD b == P1a1-b1P2a2-b2......Pnan-bn(执行的条件为ai>=bi)

        则能得到GCD(a,b) == GCD(b,a MOD b)

        那么就有p*a+q*b == GCD(a,b) == GCD(b,a MOD b)

        这样就可以写一个递归函数,一层一层MOD下去

        这样化简,直到ai MOD bi == 0

        即p*GCD(a,b) == GCD(a,b),那么在最后一层里面p==1

    所以在拓展欧几里得里面,bi==0这一层中,ai即为GCD(a,b)

      (到此为止和欧几里得算法一模一样,我甚至是复制的欧几里得算法)

     

      而如果想要求出p和q,那么有一个回溯的过程就好了

      如何回溯得到p和q呢?

      来找一下相邻的层数间pi和pi+1,qi和qi+1的关系

      因为有p*a+q*b == GCD(a,b)

      则在每一层中:

        GCD(a,b)

          == pi*a+qi*b

          == pi+1*b + qi+1*(a MOD b)

          == pi+1*b + qi+1*(a - (a/b)*b)

          == pi+1*b + qi+1*a - qi+1*(a/b)*b

          == qi+1*a + pi+1*b - qi+1*(a/b)*b

          == qi+1*a + (pi+1 - qi+1*(a/b))*b

      即每层中pi == qi+1qi == pi+1 - qi+1*(a/b)

      然后就可以回溯了!

      然后就能写出不返回gcd,单纯求出一组p,q的拓展欧几里得了!

      关于应用①中的多解问题,见[◹]拓展欧几里得算法的多解

    C++:

     1 #include<bits/stdc++.h>
     2 #define OUT(x) cout<<#x<<":"<<x<<endl;
     3 
     4 using namespace std;
     5 
     6 int x,y;
     7 
     8 void exgcd(int a,int b)
     9 {
    10     if(!b){x=1;y=0;}
    11     else{
    12         exgcd(b,a%b);
    13         int tmp=x;
    14         x=y;y=tmp-(a/b)*y;
    15     }
    16 }
    17 
    18 int main(int argc,char *argv[],char *enc[])
    19 {
    20     exgcd(12,17);
    21     OUT(x);OUT(y);
    22     return 0;
    23 }

    Java:

     1 class Pony{
     2     
     3     static int x,y;
     4 
     5     static void exgcd(int a,int b)
     6     {
     7         if(b==0){x=1;y=0;}
     8         else{
     9             exgcd(b,a%b);
    10             int tmp=x;
    11             x=y;y=tmp-(a/b)*y;
    12         }
    13     }
    14 
    15     public static void main(String[] args) throws Exception
    16     {
    17         int a,b;a=54;b=27;
    18         exgcd(a,b);
    19         System.out.println(x+" "+y);
    20     }
    21 }

      上面这个版本是我写的,比较诡异,来个常见的版本

    C++:

     1 #include<bits/stdc++.h>
     2 #define OUT(x) cout<<#x<<":"<<x<<endl;
     3 
     4 using namespace std;
     5 
     6 int x,y;
     7 
     8 int exgcd(int a,int b)
     9 {
    10     if(!b){
    11         x=1;y=0;
    12         return a;
    13     }
    14     int ret=exgcd(b,a%b);
    15     int tmp=x;
    16     x=y;y=tmp-(a/b)*y;
    17     return ret;
    18 }
    19 
    20 int main(int argc,char *argv[],char *enc[])
    21 {
    22     OUT(exgcd(12,17));
    23     OUT(x);OUT(y);
    24     return 0;
    25 }

    Java:

     1 class Pony{
     2     
     3     static int x,y;
     4 
     5     static int exgcd(int a,int b)
     6     {
     7         if(b==0){
     8             x=1;y=0;
     9             return a;
    10         }
    11         else{
    12             int ret=exgcd(b,a%b);
    13             int tmp=x;
    14             x=y;y=tmp-(a/b)*y;
    15             return ret;
    16         }
    17     }
    18 
    19     public static void main(String[] args) throws Exception
    20     {
    21         int a,b;a=54;b=27;
    22         exgcd(a,b);
    23         System.out.println(x+" "+y);
    24     }
    25 }

      仔细感受一下其中的差别啦~

    • 已知正整数a,b,素数p,求一个整数x使满足x*a ≡ b (MOD p)

      首先判断一下,如果a%p==0,那么无解

      如果a%p!=0,那么必定有GCD(a,p)==1

      

      利用分治的方法,先利用上述①求得一个正整数x‘,使满足

        x'*a + tmp*p ≡ gcd(a,p) (MOD p)

        即x'*a ≡ 1 (MOD p)

      然后易得

        x=x'*b

        x*a ≡ b (MOD p)

     C++:

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 int a,b,p,x,y;
     6 
     7 int exgcd(int a,int b)
     8 {
     9     if(!b){
    10         x=1;y=0;
    11         return a;
    12     }
    13     int ret=exgcd(b,a%b);
    14     int tmp=x;
    15     x=y;y=tmp-(a/b)*y;
    16     return ret;
    17 }
    18 
    19 int main(int argc,char *argv[],char *enc[])
    20 {
    21     scanf("%d%d%d",&a,&b,&p);
    22     if(exgcd(a,p)!=p){
    23         x*=b;
    24         printf("%d*%d ≡%d (mod %d)
    ",x,a,b,p);
    25     }
    26     else printf("NOPE
    ");
    27     
    28     return 0;
    29 }

    Java:

     1 import java.util.Scanner;
     2 
     3 class Pony{
     4     
     5     static int a,b,p,x,y;
     6 
     7     static int exgcd(int a,int b)
     8     {
     9         if(b==0){
    10             x=1;y=0;
    11             return a;
    12         }
    13         else{
    14             int ret=exgcd(b,a%b);
    15             int tmp=x;
    16             x=y;y=tmp-(a/b)*y;
    17             return ret;
    18         }
    19     }
    20 
    21     public static void main(String[] args) throws Exception
    22     {
    23         Scanner cin=new Scanner(System.in);
    24 
    25         a=cin.nextInt();
    26         b=cin.nextInt();
    27         p=cin.nextInt();
    28         if(exgcd(a,p)!=p){
    29             x*=b;
    30             System.out.printf("%d*%d ≡ %d (mod %d)
    ",x,a,b,p);
    31         }
    32         else System.out.println("NOPE");
    33     }
    34 }

    • 已知正整数a,b,素数p,求一个整数x使满足ax ≡ b (MOD p)

      详见[◹]Baby-step giant-step算法

    • 已知正整数a,b,c,求一个整数x使满足x*a ≡ b (MOD c)

      ②中的无解是因为a%p==0,a为素数p的倍数

      如果a不为p的倍数,gcd(a,p)==1

      分治,结合①可以解得x'*a ≡ 1 (mod p)

      在④中其实同理,只是有的时候gcd(a,c)不一定是1

      我们想要gcd(a,c)为1

    在同余中有一个神奇的性质:

    A' == a'*d'

    B' == b'*d'

    C' == c'*d'

    若A' ≡ B' (mod C')

    则a'*d' ≡ b'*d' (mod c'*d')

    即a' ≡ b' (mod c')

    其中,a',b',c',d',A',B',C'都为整数

    暂且叫这条性质为放缩

      求解x*a ≡ b (MOD c),想要延续②中的思路

      如果gcd(a,c)不是b的因子或b不等于0,那么可以知道gcd(a,c)!=1,而方程一定无解(下面有证明)

      而如果gcd(a,c)为b的因子或b等于0,那么有

        a==a'*gcd(a,c)

        c==c'*gcd(a,c)

        b==b'*gcd(a,c)

        有x*ab (MOD c)

        则x*a'*gcd(a,c)b'*gcd(a,c) (MOD c'*gcd(a,c))

        根据放缩,即x*a' ≡ b' (MOD c')

        此时,gcd(a',c')==1

      这样一来,就完美转化为了②中的思路,可以求解了

      但是,如果b中没有gcd(a,c)这个因子或b不等于0,是否就没有解呢?

      答案是,没有解,一定没有解

    同余方程x*a ≡ b (mod c)有解x的充分必要条件是b%gcd(a,c)==0

      证明如下:

        设整数A,C,a,b,c,d,其中A==a*d,c==c*d,abcd两两互质

    我们想要通过缩放来得到gcd(a,c)==1,这是我们的目标

        设满足A ≡ b (mod C)

        则a*d ≡ b (mod c*d)

        根据放缩,有a ≡ b/d (mod c)

        ∵abcd两两互质

        ∴d不是b的因数且b不等于0

        ∴b/d不是整数

        而在同余方程中的每个变元都必须为整数

        所以出现分数的情况视为违规

        则这个同余式不成立

        所以同余方程x*a ≡ b (mod c)有解x的充分必要条件是b%gcd(a,c)==0

     

      由以上证明可以看出来在同余方程中,出现无解情况的本质

     

    代码如下:

    C++:

     1 #include<bits/stdc++.h>
     2 
     3 using namespace std;
     4 
     5 int a,b,c,x,y;
     6 int times;
     7 
     8 int exgcd(int a,int b)
     9 {
    10     if(!b){
    11         x=1;y=0;
    12         return a;
    13     }
    14     int ret=exgcd(b,a%b);
    15     int tmp=x;
    16     x=y;y=tmp-(a/b)*y;
    17     return ret;
    18 }
    19 
    20 int main(int argc,char *argv[],char *enc[])
    21 {
    22     scanf("%d%d%d",&a,&b,&c);
    23     
    24     int tmp=exgcd(a,c);
    25     
    26     if(b%tmp!=0) printf("NOPE
    ");
    27     else{
    28         while(b%tmp==0){
    29             a/=tmp;
    30             b/=tmp;
    31             c/=tmp;
    32             ++times;
    33         }
    34         
    35         if(tmp!=c){
    36             x*=b;
    37             printf("%d*%d ≡ %d (mod %d)
    ",x,a*times*tmp,b*times*tmp,c*times*tmp);
    38         }
    39         else printf("NOPE
    ");
    40     }
    41     
    42     return 0;
    43 }

    Java:

     1 import java.util.Scanner;
     2 
     3 class Pony{
     4     
     5     static int a,b,c,x,y;
     6     static int times;
     7 
     8     static int exgcd(int a,int b)
     9     {
    10         if(b==0){
    11             x=1;y=0;
    12             return a;
    13         }
    14         else{
    15             int ret=exgcd(b,a%b);
    16             int tmp=x;
    17             x=y;y=tmp-(a/b)*y;
    18             return ret;
    19         }
    20     }
    21 
    22     public static void main(String[] args) throws Exception
    23     {
    24         Scanner cin=new Scanner(System.in);
    25 
    26         a=cin.nextInt();
    27         b=cin.nextInt();
    28         c=cin.nextInt();
    29 
    30         int tmp=exgcd(a,c);
    31 
    32         if(b%tmp!=0) System.out.println("NOPE");
    33         else{
    34             while(b%tmp==0){
    35                 a/=tmp;
    36                 b/=tmp;
    37                 c/=tmp;
    38                 ++times;
    39             }
    40 
    41             if(tmp!=c){
    42                 x*=b;
    43                 System.out.printf("%d*%d ≡ %d (mod %d)
    ",x,a*times*tmp,b*times*tmp,c*times*tmp);
    44             }
    45             else System.out.println("NOPE");
    46         }
    47     }
    48 }

    • 已知正整数a,b,c,求一个整数x使满足ax ≡ b (MOD c)

      详见[◹]拓展Baby-step giant-step算法

    • 解线性同余方程组

      俗称的拓展中国剩余定理(虽然这样叫很不准确)

      详见[◹]解线性同余方程组

  • 相关阅读:
    软件是否有必要进行性能测试
    电子工业出版社博文视点在上海第六届UPA中国用户可用性大会上
    一站式学习 Linux C语言编程
    免费讲座:从草根到巨人——互联网时代的LAMP开源架构
    相忘于江湖,《监控》 来自热心读者simon
    博文视点大讲堂第18期:从草根到巨人——互联网时代的LAMP开源架构
    博文视点Open Party第8期
    都是过客,相煎何急?
    每个人心中都有一片极乐净土
    CSS单位和CSS默认值大全
  • 原文地址:https://www.cnblogs.com/Antigonae/p/10105911.html
Copyright © 2011-2022 走看看