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

来源:互联网 发布:阿里云香港b区速度 编辑:程序博客网 时间:2024/04/29 13:53

同余方程

形如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 <iostream>#include <cstdio>#include <cstring>#include <algorithm>#include <cstdlib>#include <cmath>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<<a;    fclose(stdin);    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 <iostream>#include <cstdio>#include <cstdlib>#include <algorithm>#include <cstring>#include <cmath>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 <iostream>#include <cstdio>#include <cstring>#include <cmath>#include <algorithm>#include <cstdlib>#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<<b;    fclose(stdin);    return 0;}

求逆元

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

原创粉丝点击