拓展欧几里得算法

写在前面:

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

 

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)\n",x,a,b,p);
25     }
26     else printf("NOPE\n");
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)\n",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\n");
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)\n",x,a*times*tmp,b*times*tmp,c*times*tmp);
38         }
39         else printf("NOPE\n");
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)\n",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算法

 

  • 解线性同余方程组

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

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

 

posted @ 2018-12-11 22:58  Antigonae  阅读(876)  评论(0编辑  收藏  举报