中国余数定理

来源:互联网 发布:mac恢复u盘制作 编辑:程序博客网 时间:2024/05/21 10:49

我国古代数学名著《孙子算经》载有一道数学问题:“今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二。问物几何?”这里的几何指多少的意思。翻译成数学语言就是:求正整数N,使N除以3余2,除以5余3,除以7余2。


建立数学原型:
已知m1、m2、m3是两两互质的正整数,求最小正整数x,使它被m1、m2、m3除所得余数分别为C1、C2、C3 。
列成同余式子:

x≡a1 (mod m1)(表示x除以m1和a1除以m1的余数一样,下面一样)

x≡a2 (mod m2),

x≡a3 (mod m3),

对于孙子算经中的例子也就是,

x≡2 (mod 3)

x≡3 (mod 5),

x≡2 (mod 7),

现在需要求解这个x。


求解方程组需要下列步骤:

(1) 求M=m1 X m2 X m3 X ... X mK,这是普通的模;

(2) 求M1 = M/m1, M2 = M/m2, ... , Mk =M/mk;

(3) 求M1, M2, ..., Mk在模m1, m2, ..., mk的乘法逆 ;

(4) 方程组的解是,

那什么是乘法的逆呢?也就是乘法逆元。

若ax ≡1 (mod f ),责称a对f的逆为x 只有a与f互素时,a关于模f的乘法逆元有唯一解。如果不互素,则无解。


下面开始求解方程组:

(1) M = 3 X 5 X 7 = 105

(2)

(3) 根据上面的求逆公式: 35 X 2  mod 3 = 1, 21 X 1 mod 5 = 1, 15 X 1 mod 7 = 1,所以M1、M2、M3的逆为:

在实际写算法时,我们可以通过扩展欧几里德算法去求解逆元。

因为ax mod f = 1, x为a模f的逆元,假设此时的商为y,有:

ax = fy + 1 -----> ax-fy = 1,明显的扩展欧几里德算法的模型。

(4) 

这个满足条件的x就是 23.


参考文档:

http://shuxueshi.jie.blog.163.com/blog/static/13611628820104179856631/

http://book.51cto.com/art/200812/102579.htm


当时我们的算法老师还出了个中国余数定理的算法题:

实现中国余数定理算法并求满足下列条件的x? 

 (1)x mod 97=26       (2)x mod 32=17

具体的解决方法也是按照上面的方法,这是求解两个数,但扩展到多个数也是一样的。

写成c语言代码解决如下:

[cpp] view plaincopy
  1. #include<stdio.h>  
  2.   
  3. int count(int a, int b)  
  4. {  
  5.     return a / b;  
  6. }  
  7.   
  8. int extendedGcd(int a, int b, int * x, int * y)  
  9. {  
  10.     int r;   //最大公约数  
  11.     int temp;    
  12.     if (b == 0)  
  13.     {  
  14.         *x = 1;  
  15.         *y = 0;  
  16.         return a;    //a是最大公约数  
  17.     }  
  18.     r = extendedGcd(b, a % b, x, y);  //x和y是一个指针  
  19.     //x1 = y2, y1 = x2 - (a / b) * y2   
  20.       
  21.     temp = *x;  
  22.     *x = *y;  
  23.     *y = temp - (a / b) * (*y);  
  24.     return r;     //返回最大公约数  
  25. }  
  26.   
  27. int main()  
  28. {  
  29.     /*求正整数N, 使N除以97余26,除以32余17*/  
  30.     int m1 = 97, m2 = 32;   //除数  
  31.     int a1 = 26, a2 = 17;   //余数  
  32.     int M = m1 * m2;    
  33.       
  34.     int M1 = count(M, m1);    
  35.     int M2 = count(M, m2);  
  36.       
  37.     int x1, y1; //通过扩展欧几里德算法求逆元,x1是M1模m1的逆  
  38.     int x2, y2;    //x2是M2模m2的逆  
  39.     extendedGcd(M1, m1, &x1, &y1);    //求M1的逆  
  40.     extendedGcd(M2, m2, &x2, &y2);    //求M2的逆   
  41.       
  42.     int num;   //满足要求的x  
  43.     num = (a1 * M1 * x1 + a2 * M2 * x2) % M;  
  44.     printf("所求的数字为: %d\n", num + M);    //求得的num是一个负数,但这不影响我们所要求的数字,加上97和32的最小公倍数M即可  
  45.       
  46.     return 0;  
  47. }  
0 0
原创粉丝点击