[数论]拓展欧几里得算法

Posted yanick

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[数论]拓展欧几里得算法相关的知识,希望对你有一定的参考价值。

欧几里得算法(辗转相除法)

用来求解最大公约数

1 int gcd(int a,int b){
2      return b ? gcd(b,a%b) : a;
3 }

在 #include<algorithm> 中也可以直接调用 __gcd(a,b)

 


 

拓展欧几里得算法

求解不定方程:

引理:存在 x , y 使得 ax+by=gcd(a,b)

设a,b,c为任意整数,若方程ax+by=c的一组解是(x0,y0),则它的任意整数解都可以写成(x0+k*b/gcd(a,b),y0-k*a/gcd(a,b)),k取任意整数

 

 1 typedef long long ll;
 2 
 3 ll exgcd(ll a,ll b)
 4 {
 5     if(b){
 6         ll r=exgcd(b,a%b);
 7         ll k=x;
 8         x=y;
 9         y=k-a/b*y;
10         return r;
11     }
12     else{
13         x=1,y=0;
14         return a;
15     }
16 }

 


 

解线性同余方程

关于 x 的模方程 ax%b=c 的解,方程转换为 ax+by=c 其中 y 一般为非正整数,则问题变为用 exgcd 解不定方程

 


 

计算乘法逆元 

若 a*x≡1(mod b) ,且 a与 b互质,那么我们就能定义: x为 a的逆元

这就是利用拓欧求解线性同余方程 a*x≡c(mod b) 的 c=1 的情况。我们就可以转化为解 a*x + b*y = 1这个方程

 

 1 typedef long long ll;
 2 
 3 void exgcd(ll a,ll b)//扩展欧几里得算法求乘法逆元,x为a的逆元
 4 {
 5     if(!b){
 6         x=1,y=0;
 7         return;
 8     }
 9     exgcd(b,a%b);
10     ll k;
11     k=x;
12     x=y;
13     y=k-(a/b)*y;
14 }

 


 

一些例题

zoj 3609

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 ll gcd;
 5 
 6 ll exgcd(ll a,ll b,ll &x,ll &y)
 7 {
 8     if(b==0){
 9         x=1,y=0;
10         return a;
11     }
12     ll ans=exgcd(b,a%b,x,y);
13     ll temp=x;
14     x=y;
15     y=temp-a/b*y;
16     return ans;
17 }
18  
19 ll cal(ll a,ll b,ll c)
20 {
21     ll x,y;
22     gcd=exgcd(a,b,x,y);
23     ll ans=(x%b+b)%b;
24     if(!ans)
25         ans=b;
26     return ans;
27 }
28  
29 int main()
30 {
31     ll a,b,t;
32     cin>>t;
33     while(t--){
34         cin>>a>>b;
35         ll ans=cal(a,b,1);
36         if(gcd!=1)
37             cout<<"Not Exist\n";
38         else 
39             cout<<ans<<endl;
40     }
41     return 0;
42 }

 

zoj 3593

 

 1 /*根据公式ax+by=c;当x,y同号时等于max(x,y)
 2 当a,b异号时等于(abs(x)+abs(y))
 3 因为a,b大于0,所以不管x,y同号还是异号都是当x,y,最接近时,答案最小
 4 写出通式 x=x1+b/(gcd)*k, y=y1-a/gcd*k
 5 假设x,y相等, k=(y-x)/(a+b),因为x,y不一定相等,所以枚举k附近的点*/
 6 #include<bits/stdc++.h>
 7 using namespace std;
 8 typedef long long ll;
 9 ll x,y;
10 
11 ll exgcd(int a,int b){
12     if(b){
13         ll n=exgcd(b,a%b);
14         ll xt=x;
15         x=y,y=xt-a/b*y;
16         return n;
17     }
18     x=1,y=0;
19     return a;
20 }
21 
22 int main(){
23     ll n,m,t,a,b,c;
24     cin>>t;
25     while(t--){
26         cin>>n>>m>>a>>b;
27         c=n-m;
28         ll ans=exgcd(a,b);
29         if(c%ans!=0){
30             cout<<-1<<endl;
31             continue;
32         }
33         c/=ans,a/=ans,b/=ans;
34         x*=c,y*=c;
35         ll k=(y-x)/(a+b);
36         ans=10000000000ll;
37         for(ll i=k-1;i<k+2;i++){
38             ll xx;
39             ll x0=x+b*i,y0=y-a*i;
40             if(x0<=0&&y0>=0||y0<=0&&x0>=0)
41                 xx=abs(x0)+abs(y0);
42             else
43                 xx=max(abs(x0),abs(y0));
44             if(xx<ans)
45                 ans=xx;
46         }
47         cout<<ans<<endl;
48     }
49     return 0;
50 }

 

poj 1061

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 typedef long long ll;
 5 ll x,y;
 6 
 7 ll exgcd(ll a,ll b)
 8 {
 9     if(b){
10         ll r=exgcd(b,a%b);
11         ll k=x;
12         x=y;
13         y=k-a/b*y;
14         return r;
15     }
16     else{
17         x=1,y=0;
18         return a;
19     }
20 }
21 
22 int main()
23 {
24     ll p,q,m,n,l;
25     scanf("%lld%lld%lld%lld%lld",&p,&q,&m,&n,&l);
26         ll a=n-m,b=l,c=p-q;
27         ll ans=exgcd(a,b);
28         if(c%ans)
29             printf("Impossible\n");
30         else{
31             ll k=c/ans,t=b/ans;
32             x*=k;
33             if(t<0)
34                 t=-t;
35             x=(x%t+t)%t;
36             cout<<x<<endl;
37         }
38     return 0;
39 }

 

hdu 1576

 

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll x,y;

ll exgcd(int a,int b){
    if(b){
        ll n=exgcd(b,a%b);
        ll xt=x;
        x=y,y=xt-a/b*y;
        return n;
    }
    x=1,y=0;
    return a;
}

int main(){
    ll n,m,t,a,b=9973,c;
    cin>>t;
    while(t--){
        cin>>n>>a;
        ll ans=exgcd(a,b);
        x=(x%b+b)%b;
        ans=n*(x%b)%b;
        cout<<ans<<endl;
    }
    return 0;
}

 

以上是关于[数论]拓展欧几里得算法的主要内容,如果未能解决你的问题,请参考以下文章

理论: 数论:拓展欧几里得算法及其证明

数论之拓展欧几里得求解不定方程和同余方程组

蓝桥集训之数论基础

数论及其应用——欧几里得算法

数论全集

数论扩展欧几里得算法