数论相关:同余方程与同余方程组的解法

同余方程

形如a*x≡b(mod n)的式子称为线性同余方程。对于这样的式子有解的充要条件是gcd(a,n)|b.

于是扩展gcd求解
将原方程化为一次不定方程 a*y+n*y=b.
利用扩展欧几里得算法求解不定方程a * x + n* y = b的整数解的求解全过程,步骤如下:

1、先计算Gcd(a,n),若n不能被Gcd(a,n)整除,则方程无整数解;否则,在方程两边同时除以b/gcd(a,n),得到新的不定方程a’* x + n’* y = gcd(a,n).

2、利用扩展欧几里德算法求出方程a’* x + n’* y = 1的一组整数解x0,y0,则gcd(a,n)* x0,gcd(a,n)* y0是方程a’* x + n’ * y = gcd(a,n)的一组整数解;

3、根据数论中的相关定理,记k=b/gcd(a,n),可得方程a* x + n * y = b的所有整数解为:

   x = k*x0 + n/gcd(a,n)* t
   y =k* y0 –n/gcd(a,n)* t    (t=0,1,2,……)

调整得到正整数解
注意因为解有多个,而我们要求最优解,所以(x+=n/gcd(a,n)%(n/gcd(a,n));

青蛙的约会

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
long long  init(){
    long long  rv=0,fh=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') fh=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        rv=(rv<<1)+(rv<<3)+c-'0';
        c=getchar();
    }
    return fh*rv;
}
long long x,y,m,n,l;
long long exgcd(long long a,long long b,long long &x,long long &y){
    if(b==0){
        x=1;y=0;return a;
    }
    long long  t=exgcd(b,a%b,y,x);
    y-=(a/b)*x;
    return t;
}
int main(){
    freopen("in.txt","r",stdin);
    x=init();y=init();m=init();n=init();l=init();   
    if(m - n< 0) swap(m, n), swap(x, y);
    long long  a=0,b=0;
    long long  t=exgcd(m-n,l,a,b);
    if(n==m||(x-y)%t!=0){
        printf("Impossible");
        return 0;
    }
    (a*=(y-x)/t)%=(l/t);
    (a+=l)%=(l/t);//以保证最优解
    cout<return 0;
}

exgcd可以用来求逆元

a*x≡1(mod n) 已知a,n求x
因为n是个素数,所以gcd(a,n)==1;
原方程可化为 a*x≡gcd(a,n)(mod n)
用exgcd求解即可。

同余方程组

x%p1 = b1
x%p2 = b2
x%p3 = b3
x%p4 = b4
求x的最小正整数解
小范围数据直接枚举

对于模数互质的情况,使用中国剩余定理(CRT)
令m=p1p2p3…pn
构造出
//ni(k,p)是k在模p意义下逆元
x = (m/p1*ni(m/p1, p1)*a1 + …) c% m
CRT求解同余方程组

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;
long long a1,a2,a3,a4,b1,b2,b3,b4;
long long m;
long long exgcd(long long a,long long b,long long &x,long long &y){
    if(!b){
        x=1;y=0;
        return a;
    }
    long long t=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return t;
}
long long ni(long long a,long long b){
    long long x=0,y=0;
    long long t=exgcd(a,b,x,y);
    if(t!=1) return -1;
    else return((x+b)%b);
}
int main(){
    freopen("in.txt","r",stdin);
    cin>>a1>>b1>>a2>>b2>>a3>>b3>>a4>>b4;
    m=a1*a2*a3*a4;
    cout<<(m/a1*ni(m/a1,a1)*b1+m/a2*ni(m/a2,a2)*b2+m/a3*ni(m/a3,a3)*b3+m/a4*ni(m/a4,a4)*b4)%m;
    fclose(stdin);
    return 0;
}

对于一般情况采用exgcd两两合并,
x+a1k1=b1
x+k2a2=b2
a1k1-a2k2=b1-b2
t=exgcd(a1,-a2,k1,k2) 实际上-a2可以写作a2
合并:k1=(k1*(b1-b2)/t)%a2; //此处是模a2,因为可以看成是模a2意义下的同余方程
b1-=a1*k1 //b1就是原式中的x
a1=a1/t*a2 //把a1变成lcm(a1,a2)
b1%=a1 //把b1调整至新式子的B
此时就把两个式子合并为了一个,待所有的都合并完后,结果就是b1调整好的最小正整数(b1+=a1)%=a1

#include 
#include 
#include 
#include 
#include 
#include 
#define LL long long 
using namespace std;
LL init(){
    LL rv=0,fh=1;
    char c=getchar();
    while(c<'0'||c>'9'){
        if(c=='-') fh=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
        rv=(rv<<1)+(rv<<3)+c-'0';
        c=getchar();
    }
    return rv*fh;
}
LL a,b,a1,b1;
LL exgcd(LL a,LL b,LL &x,LL &y){
    if(!b){
        x=1;y=0;return a;
    }
    LL t=exgcd(b,a%b,y,x);
    y-=a/b*x;
    return t;
}
int main(){
    freopen("in.txt","r",stdin);
    a=init();b=init();
    for(int i=1;i<=3;i++){
        a1=init();b1=init();
        LL x=0,y=0;
        LL t=exgcd(a,a1,x,y);
        x=(x*(b-b1)/t)%a1;
        b-=a*x;
        a=a/t*a1;
        b%=a;
    }
    (b+=a)%=a;
    cout<return 0;
}

求逆元

a*x≡1(mod n)
逆元存在的充要条件是gcd(a,n)==1;
一般采用exgcd求逆元,若p是质数,也可使用费马小定理,快速幂

你可能感兴趣的:(复习,数论,同余)