同余方程组,中国剩余定理,孙子定理(学习)

同余方程组,中国剩余定理(孙子定理)学习

从孙子定理介绍起把,其实对于它的由来大家还是很有兴趣了解一下的。
以下是我取于互动百科的内容:
中国南北朝时期(5~6世纪)著名的著作《孙子算经》中“物不知数”问题所阐述的定理。物不知数问题的原题是:“今有物,不知其数,三三数之剩二,五五数之剩三,七七数之剩二,问物几何?”这属于数论的一次同余方程组问题。用现代数学符号可表为求下列同余方程的整数解:
这里写图片描述
这里写图片描述
《孙子算经》没有给出具体算法。宋代秦九韶在《数书九章》中第一次详细地、完整地阐述了求解一次同余方程组的算法,他称做“大衍总数术”,其中包括求kj的一种机械化算法──大衍求一术。


个人认为孙子定理理解起来还是挺困难的,不真正理解原理,只是一味套模版并没有什么用,非常容易忘记,所以好好学吧。

孙子定理又名中国剩余定理,其实就是求 模线性方程组 其 x 值。
x ≡ a1 ( % n1);
x ≡ a2 ( % n2);
x ≡ a3 ( % n1);
……
求其值要满足一系列的模线性方程,具我知道的可以说有4种方法。
1. 枚举法。
枚举满足每个方程的一系列数字,找到满足各个方程的数字(既各个方程都有的数字)。
注:个人不细讲,因为这个方法好理解,而且可以说这只是个理论成立,对于现实里由于复杂度大,并不适用。

2. 化为同余数法
如果把上述的模线性方程组的mod值化成相同是不是简单许多。秉着这个思想:
X≡2 (mod 7 ) ; X≡5 (mod 9 ) ;X≡1 (mod 5 )
这三个同余式,余数不同,分别为7、9、5,为了能利用同余式的和差特性,简化计算,先设法使它们的除数相同,为此:
X≡2 (mod 7 )两边都乘9* 5,得X* 45≡2* 45 (mod 7* 45 ) →45 X≡90 (mod 315 ) …(1)
X≡5 (mod 9 ) 两边都乘7* 5,得X* 35≡5* 35 (mod 9* 35 ) →35 X≡175 (mod 315 ) …(2)
X≡1 (mod 5 ) 两边都乘7* 9,得X* 63≡1* 63 (mod 9* 63 ) →63 X≡ 63 (mod 315 ) …(3)
(1)+(2)+(3)= (4) → 143 X≡328 (mod 315 ) …4)
根据同余式的加减性质,(1)+(2)+(3)得:
143 X≡328 (mod 315 ) 即 143 X≡13 (mod 315 )
再最后单独求解一个同余方程,用扩展欧几里德很快得出。
注:个人认为虽然这个方法好理解,也简单。但如果 ni 够大,方程够多,最后的mod数很大而难以实现原来的效果。

3. 逐级满足法
见名思意,逐级满足。这个方法的基本思路是:
这里我把x用C来代写以免于下面x,y整数解冲突,下面整数解用x,y是让学过扩展欧几里德的人更好的理解。总的来说,它要求把第一和第二方程合并,再把合并方程和第三个方程再合并,知道合并完全。先解算出合符第一个方程的C(注意C是满足现在这级的C,所以C在不断满足的过程中变化)。再解算出合符第一、第二个方程的C,【过程:对于头两个关系式,C % n1==a1, C % n2==a2,可以联立方程组 n1* x+n2* y==a2-a1 ,很熟悉,直接套用扩展欧几里得算法得到一组解x,y,再取x的最小值,带入式子C%n1==a1(化为不定方程)中,便得到了答案 C 。】求出了合并方程的C,那么合并方程的n怎么求?
答案是 n = lcm(n1,n2)。
新式子公式是这样的:c mod lcm(n1,n2) ==C 这里C是要求的新答案,C是前两个式子求的的一个解,lcm(n1,n2)是a1,a2的最小公倍数。
你可以理解为新的答案C即要mod n1,又要modn2,所以就mod lcm(n1,n2)。而余数C 是刚前面求得的答案,也就是说c不管怎么折腾,都还会保留一个C,即不会破坏前面的成果!

//MOD[]为ni;
//c[]为ai; 这里因为x≡ai (%ni)就为 x + ni*y = ai;作为不定方程ax + by = c来看,ai就为ci。
            int now_mod = MOD[0]; //n1第一个方程
            int now_c = c[0];     //a1第一个方程
            for(int i = 1; i < 4; i++)
            {
//n1*x+n2*y==a2-a1,这里由于在用扩展欧几里德时,a,b会变化,   所以再引用了变量now_mod。下面只作方程一和二的讲解
                int a = now_mod;  //n1
                int b = MOD[i];   //n2
                int c_real = c[i] - now_c; //a2-a1
                int x,y;
                int d = exgcd(a,b,x,y);
                x = x * c_real/d;
                int r = b/d;
                x = (x%r+r)%r;    //得到最小正的x。
                now_c = now_mod * x + now_c; //变化的C
                now_mod = now_mod * (MOD[i]/d); //变化的n
        //得到第一和第二合并的式子就是 c(这个其实无所谓,重点在下面俩的变化) ≡ now_c (mod now_mod)
            }
            int ans = now_c;

注:在我看来这个算法最实用,虽然算法难了点,但它可以在
gcd(ni,n(i-1)) == 任何数 适用,而下面我们介绍的贼难的“大衍求一术”解法只适用与 ni 互质。

4. 大衍求一术法
X≡R1 (mod m1 ) X≡2 (mod 7 )
X≡R2 (mod m2) X≡5 (mod 9 )
X≡R3 (mod m3) X≡1 (mod 5 )
名词注释及计算步骤:
1 余数R:R1=2、R2=5、R3=1
2 模,亦即除数m:例中m1=7、m2=9、m3=5
3 模的最小公倍数G:G=m1* m2* m3,例中M=7* 9* 5=315
4 衍数(局部公倍数)y:Y1=m2m3、Y2=m1m3,Y3=m1m2,例中Y1=9* 5=45、Y2=7* 5=35、Y3=7* 9=63
5 乘率:(衍数*乘率)除以模(除数),而余数为1。即:
衍数 * 乘率 ≡ 1 (mod m),乘率C可以经过反算而得到。
(要求的)X ≡ Σ 余数* 衍数* 乘率 (mod G)。
注:案例是拿的,原理不知,条件是互质,感觉还是挺麻烦的额。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值