zoukankan      html  css  js  c++  java
  • 线性同余方程组

    写在前面:

      记录了个人的学习过程,同时方便复习

      笔者渣渣,网上的证明看不懂,只能自己证

      注意不要把中国剩余定理和解线性同余方程组混为一谈

      中国剩余定理仅仅是一个很巧妙的算法

      在学习如何解线性同余方程组之前,根本没必要学习中国剩余定理

      本篇全原创,转载请留言

    目录

    by otakuap

    • 线性同余方程组是否有解

      同余方程组的解什么时候存在呢?

      先假设要解这么一个同余方程组:

        x ≡ a1 (mod b1)

        x ≡ a2 (mod b2)

        ……

        x ≡ an (mod bn)

        我们假设其中的每一个同余方程都成立!

      我在[◹]拓展欧几里得算法的应用④中提到了同余方程的一个有趣的性质:放缩性

      利用放缩,我们来把所有的模数变成一样的!

        定义B=Πni=1 bi

        根据放缩,则上面那个同余方程组可以转化为:

        x*B/b1 ≡ a1*B/b1 (mod B)

        x*B/b2 ≡ a2*B/b2 (mod B)

        ……

        x*B/bn ≡ an*B/bn (mod B)

      在同余方程组中有这么一个显然的性质:

        在同一模数的意义下,同一个数的对应值不能有多个!(在函数中可以多对一,不能一对多)

      一旦出现

        x*B/bi ≡ c (mod B)

        x*B/bi ≡ d (mod B)

        cd为非负整数,且c!=d

      那么一定无解

      这就是同余方程组有解无解的本质了

      在这里可以得到上面的线性同余方程组的成立条件:

    ① 每一组同余方程都有解

    (详见[◹]拓展欧几里得算法应用④)

    ② 每一个x*B/bi都唯一对应一个ai*B/bi

      只要以上条件有一个不满足,那么这组同余方程一定无解!

      在中国剩余定理解线性同余方程组中,模数两两互质

      保证了对应的唯一性

      这就是为什么中国国剩余定理来解线性同余方程组,要么不能解,要么一定有解

    • 解的周期性

      首先假设有多个周期函数f(x),他们的周期分别为Ti

      这多个周期函数方程组成的方程组的解的最小正周期为T

      则显然,TTi的最小公倍数

      同理

      先假设要解这么一个同余方程组:

        x ≡ a1 (mod b1)

        x ≡ a2 (mod b2)

        ……

        x ≡ an (mod bn)

        我们假设其中的每一个同余方程都成立!

      利用放缩,我们来把所有的模数变成一样的!

        定义B=LCM(bi)

    注意此处是最小公倍数而不单单是积

        根据放缩,则上面那个同余方程组可以转化为:

        x*B/b1 ≡ a1*B/b1 (mod B)

        x*B/b2 ≡ a2*B/b2 (mod B)

        ……

        x*B/bn ≡ an*B/bn (mod B)

      解这么一组线性同余方程组,如果有解,那么它的最小非负整数解一定是[0,B-1]这个区间中的某个整数!

    • 失败的尝试

    某只马血的教训

      先来看一个同余方程组中有趣的性质:

    性质:

    A1 ≡ B1 (mod C)

    A2 ≡ B2 (mod C)

    如果这两个同余式都成立,那么一定有:

    A1+A2 ≡ (B1+B2)%C (mod C)

    暂且叫这条性质为可加性

     

    证明:

      根据%运算的分配律

        (a+b)%c == ((a%c)+(b%c))%C

      则有

        若a ≡ a%c (mod c)

        b ≡ b%c (mod c)

        则a+b ≡ ((a%c)+(b%c))%c (mod c)

      这个尝试的思路,大概是先把模数都弄成一样的,然后转化出该线性同余方程组的最终同余方程

      利用[◹]拓展欧几里得算法的应用④,判定这个最终方程有没有解,解出来是多少

        如果有解,不能说明该线性同余方程组有解

        如果无解,不能说明该线性同余方程组无解

      为什么呢?

      因为可加性的逆命题是假命题

      举个例子:

        x ≡ 2 (mod 3)

        x ≡ 5 (mod 6)

        x ≡ 2 (mod 7)

      根据放缩,有

        14x ≡ 28 (mod 42)

        7x ≡ 35 (mod 42)

        6x ≡ 12 (mod 42)

      根据可加性,有

        27x ≡ 33 (mod 42)

      解得一个x==9

      反带回去:

        14*9 ≡ 0 (mod 42)

        7*9 ≡ 21 (mod 42)

        6*9 ≡ 12 (mod 42)

      根本不成立

      同余方程组中还有一个趣的性质:

    性质:

    A1 ≡ B1 (mod C)

    A2 ≡ B2 (mod C)

    如果这两个同余式都成立,那么一定有:

    A1*A2 ≡ (B1*B2)%C (mod C)

    暂且叫这条性质为可乘性

     

    证明:

      根据%运算的分配律

        (a*b)%c == ((a%c)*(b%c))%C

      则有

        若a ≡ a%c (mod c)

        b ≡ b%c (mod c)

        则a*b ≡ ((a%c)*(b%c))%c (mod c)

      和可加性一样,利用可乘性求解x的尝试也是失败的

      因为可乘性的逆命题是假命题

      举个例子:

        x ≡ 5 (mod 6)

        x ≡ 2 (mod 7)

      根据放缩,有

        7x ≡ 35 (mod 42)

        6x ≡ 12 (mod 42)

      根据可乘性,有

        42x ≡ 0 (mod 42)

      显然,x为任意整数

      更别说什么反带回去了

      虽然这两个尝试都失败了,但是它们还是很有价值的

     

    • 解线性同余方程组

      首先,在[◹]拓展欧几里得算法中提到的放缩的逆命题是真命题

      其次,我们已经知道了什么情况下,待解的线性同余方程组有解

      我们可以采用拆分,合并的解法来解决问题:

      先假设要解这么一个同余方程组:

        x ≡ a1 (mod b1)

        x ≡ a2 (mod b2)

        ……

        x ≡ an (mod bn)

        其中可能有些同余方程根本就没有解,详见[◹]拓展欧几里得算法

      定义B=LCM(bi)

      根据放缩,则上面那个同余方程组可以转化为:

        x*B/b1 ≡ a1*B/b1 (mod B)

        x*B/b2 ≡ a2*B/b2 (mod B)

        ……

        x*B/bn ≡ an*B/bn (mod B)

      根据有无解的条件②,先判断有没有一对多的情况

        如果有,那么肯定无解

        如果没有,那么可能有解,继续

      然后根据有无解的条件①,一个个判断有无 无解的同余方程 并求出xi,xi都是最小非负整数解

        如果存在无解的同余方程,那么一定无解

        如果不存在无解的同余方程,那么一定有解

    待完成

      1 #include<bits/stdc++.h>
      2 
      3 using namespace std;
      4 
      5 int const MAXN=10010;
      6 int x,y,tmpb,tmpa,tmp,times;
      7 int k;//k组同余方程
      8 int a[MAXN];//每个同余方程的余数
      9 int b[MAXN];//每个同余方程的模数
     10 int bi[MAXN],ai[MAXN];
     11 int B=1;
     12 
     13 void C_swap(int &a,int &b){int c=a;a=b;b=c;return;}
     14 
     15 int exgcd(int a,int b)
     16 {
     17     if(!b){
     18         x=1;y=0;
     19         return a;
     20     }
     21     int ret=exgcd(b,a%b);
     22     int tmp=x;
     23     x=y;y=tmp-(a/b)*y;
     24     return ret;
     25 }
     26 
     27 int lcm(int a,int b){
     28     return a/exgcd(a,b)*b;
     29 }
     30 
     31 void ponySort(int l,int r){
     32     int i=l,j=r,mid=bi[(l+r)>>1];
     33     while(i<=j){
     34         while(bi[i]<mid) ++i;
     35         while(bi[j]>mid) --j;
     36         if(i<=j){
     37             C_swap(bi[i],bi[j]);
     38             C_swap(ai[i],ai[j]);
     39             ++i;--j;
     40         }
     41     }
     42     if(l<j) ponySort(l,j);
     43     if(i<r) ponySort(i,r);
     44     return;
     45 }
     46 
     47 int main(int argc,char *argv[],char *enc[])
     48 {
     49     scanf("%d",&k);
     50     for(int i=1;i<=k;++i)
     51         scanf("%d%d",&b[i],&a[i]);
     52     
     53     for(int i=1;i<=k;++i)
     54         printf("x ≡ %d (mod %d)
    ",a[i],b[i]);
     55     printf("
    ");
     56     
     57     for(int i=1;i<=k;++i)
     58         B=lcm(B,b[i]);
     59     
     60     for(int i=1;i<=k;++i){
     61         bi[i]=B/b[i];
     62         ai[i]=B/b[i]*a[i];
     63     }
     64     
     65     for(int i=1;i<=k;++i)
     66         printf("x*%d ≡ %d (mod %d)
    ",bi[i],ai[i],B);
     67     printf("
    ");
     68     
     69     ponySort(1,k);
     70     
     71     for(int i=1;i<=k;++i)
     72         printf("x*%d ≡ %d (mod %d)
    ",bi[i],ai[i],B);
     73     printf("
    ");
     74     
     75     for(int i=1;i<=k;++i)
     76     {
     77         if(tmpa!=ai[i] && tmpb==bi[i]){
     78             printf("NOPE
    ");
     79             return 0;
     80         }
     81         tmpa=ai[i];
     82         tmpb=bi[i];
     83     }
     84     
     85     /* xi*bi[i] ≡ ai[i] (mod B) */
     86     
     87     for(int i=1;i<=k;++i){
     88         
     89         tmp=exgcd(bi[i],B);
     90         
     91         if(ai[i]%tmp!=0){
     92             printf("NOPE
    ");
     93             return 0;
     94         }
     95         else{
     96             times=0;
     97             while(ai[i]%tmp==0){
     98                 ai[i]/=tmp;
     99                 ++times;
    100             }
    101             
    102             if(tmp!=B){
    103                 x*=ai[i];
    104                 x=((x%(B/(tmp*times)))+(B/(tmp*times)))%(B/(tmp*times));
    105                 printf("%d*%d ≡ %d (mod %d)
    ",x,bi[i],ai[i]*times*tmp,B);
    106             }
    107             else{
    108                 printf("NOPE
    ");
    109                 return 0;
    110             }
    111         }
    112     }
    113     
    114     return 0;
    115 }
    116 /*
    117 3
    118 3 2
    119 6 5
    120 7 2
    121 */
  • 相关阅读:
    python 之 Multiprocessing 多进程
    python 之 Threading 多线程
    Python实战 -- 利用Flask搭建微电影网站(一)蓝图构建
    R语言学习——作图
    python 之 pandas 总结
    python 之 numpy 总结
    C++使用Jsoncpp源码
    C++左值、左值引用、右值、右值引用
    std::forward
    std::move
  • 原文地址:https://www.cnblogs.com/Antigonae/p/10134783.html
Copyright © 2011-2022 走看看