同余问题(2)逆元,孙子定理

定理:如果a,b和c是正整数,且那么有
推导:
因为  ,所以,

进一步推出结论:如果a,b,c  和m是整数,且m>0,d=(c,m),,有
推导:


由此产生的新推论:if a,b,c and m are integers such that m>0,(c,m)=1,and , then.

数字变大:ifa,b,c and m are integers such that k>0, m>0,and

推导:

 

有趣的相关定理:

if , then  { 
means the least common multiple of }
推导:

由此得出结论,如果所有的mi,mj两两互素,那么

一元线性同余方程转化成两个变量的线性丢潘图方程: ax+my=b,(a,m)=d, 如果 那么 没有解,否则有d个解,解是的形式,其中t=0,1,2……d-1 这d个解即是d个模m剩余类

当 中的b=1时,x就是a的逆。对于一元线性同余方程,使用拓展欧几里得可以求出结果,使用逆元也能得出结论。设 那么, 即 问题就转化成了求解a的逆 若 那么a就是自身的逆 。这很好理解啊。。(两者联系思考) 这时,   1为模m的二次剩余 【二次剩余: if (a,m)=1, a为模m的二次剩余】

重要转化:等价于
推导:


逆元的递推:设是i模M的一个逆,那么有。
证明:设


练习:zoj 3609 Modular Inverse
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3609
分析:由ax≡1(mod m) 可以得到ax+mk=1,现在要求x的最小正整数,即求上式逆元。
#include <iostream>
#include<cstdio>
using namespace std;
void extend_Eu(int a,int b,int &f,int &x,int &y){
    if(b==0){
        x=1;
        y=1;
        f=a;
        return ;
    }
    extend_Eu(b,a%b,f,x,y);
    int t=x;
    x=y;
    y=t-a/b*y;
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int T,a,m;
    while(cin>>T){
        while(T--){
            int b,f,x,y;
            scanf("%d%d",&a,&m);
            b=m;
            extend_Eu(a,b,f,x,y); 
            if(f!=1){  cout<<"Not Exist\n"; continue; } //if(gcd(a,x)!=1)
            while(x<0){
                x+=b;
            }
            cout<<x<<endl;
        }
    }
    return 0;
}

nefu 84 五指山 
http://acm.nefu.edu.cn/JudgeOnline/problemshow.php?problem_id=84
分析:有条件可列出等式,dk1+x=y+nk2 <-->dk1+nk2=y-x 大圣至少要翻的筋斗数就是k1的值。明显,这是拓展欧几里得。(Extend_gcd)
import java.util.*;
public class Main {
 static long gcd(long a,long b){
  return b!=0?gcd(b,a%b):a;
 }
    static long Extend_Eulid(long d,long f)  
    {  
        long x1=1,x2=0,x3=f,y1=0,y2=1,y3=d ;  
        while(y3>0 && y3!=1)  
        {  
            long q=x3/y3 ;  
            long t1,t2,t3 ;  
            t1=x1-q*y1;
            t2=x2-q*y2;
            t3=x3-q*y3;  
            x1=y1;
            x2=y2;
            x3=y3 ;  
            y1=t1;
            y2=t2;
            y3=t3 ;  
        }  
        if(y3==0)return -1 ;  
        return y2 ;  
    }  
 public static void main(String[] args) {
     long t,n,d,x,y;
     Scanner sc=new Scanner (System.in);
     t=sc.nextLong();
        for(int i=0;i<t;i++){
         n=sc.nextLong();
         d=sc.nextLong();
         x=sc.nextLong();
         y=sc.nextLong();
         long c=(y-x+n)%n;
         long gd=gcd(d,n);
         if(c%gd!=0){
          System.out.println("Impossible");
          continue;
         }
         d/=gd;
         n/=gd;
         long res=Extend_Eulid(d,n);
         if(res<0) res+=n;
         res=res*(c/gd)%n;
         System.out.println(res);
        }
         
 }

}


中国剩余定理(孙子定理):
对于方程
同余问题(2)逆元,孙子定理_第1张图片
其中两两互素,x具有解:其中,yk是Mk模mk下的逆。很好理解啊。。

中国古代求解一次同余式组的方法:

数学著作《孙子算经》卷下第二十六题,叫做“物不知数”问题,原文如下:有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?

即:已知 n%3=2,  n%5=3,  n%7=2,  求n。 (假设除数两两互质)
令x= n%3=2 , y= n%5=3 ,z= n%7=2
使5×7×a被3除余1,有35×2=70,即a=2;
使3×7×b被5除余1,用21×1=21,即b=1;
使3×5×c被7除余1,用15×1=15,即c=1。

那么n =(70×x+21×y+15×z)%lcm(3,5,7) = 23 这是n的最小解。

用程序实现:

#include <iostream>
#include<cstdio>
using namespace std;
int gcd(int a,int b){
    return b?gcd(b,a%b):a;
}
int get(int a,int b,int c){
    int lc=a*b/gcd(a,b),ans=lc;
    while(ans%c!=1){
        ans+=lc;
    }
    return ans;
}
int main()
{
    int m[3]={3,5,7},n[3]={2,3,2};  //m,n分别代表除数和余数
    //int m[3]={23,28,33},n[3]={283,102,23};
    int r=gcd(gcd(m[0],m[1]),m[2]),D=m[0]*m[1]*m[2]/r;
    int q1=get(m[1],m[2],m[0]),q2=get(m[0],m[2],m[1]),q3=get(m[0],m[1],m[2]);
    printf("%d\n",(q1*n[0]+q2*n[1]+q3*n[2])%D);
    return 0;
}


例题: poj 1006 Biorhythms
题意:http://poj.org/problem?id=1006
#include <iostream>
#include<cstdio>
using namespace std;
int get(int a,int b,int c){
    int lc=a*b,ans=lc;
    while(ans%c!=1){
        ans+=lc;
    }
    return ans;
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int p,e,i,d,k=0; // 23, 28, and 33
    int pd=get(28,33,23),ed=get(23,33,28),id=get(23,28,33);
    while(~scanf("%d%d%d%d",&p,&e,&i,&d)){
        if(p==-1)break;
        int ans=(pd*p+ed*e+id*i)%21252;
        ans=(ans-d+21252)%21252; //防止负数的产生。
        if(ans==0)ans+=21252;  //防止得出的结果和题目给出的日期一样。
        printf("Case %d: the next triple peak occurs in %d days.\n",++k,ans);
    }
    return 0;
}


拓展的问题:一元线性同余方程组,见 http://blog.csdn.net/thearcticocean/article/details/49452859

你可能感兴趣的:(同余)