(本文参考了 镜外之主 dalao 的博客)
一、引入 线性同余方程组
诸多个形如 ax ≡ b (mod p) 的,以x为未知数的一次方程,组成的方程组。
二、引入 数论倒数
引入:
首先,在mod中,仅有加、减、乘法对于mod存在“封闭性”
即
( a + b ) %p = (a %p )+ ( b %p ) √
( a - b ) %p = (a %p )- ( b %p ) √
( a × b ) %p = (a %p )× ( b %p ) √
( a / b ) %p = (a %p )/ ( b %p ) ×
在实际运算中,由于某些过程中间结果过大,long long int都存不下,我们不得不边计算边求余,这时如果我们遇见除法,是不是就完蛋了呢?
当然不是 i(>o<)i,我们拥有一个秘密武器——数论倒数
定义:
如果(a,m)=1且存在唯一的b使得a×b≡1(mod m)且1≤b<m,即b使得 a ≡(1/b)(mod m),则称b为a在模m意义下的数论倒数,记为inv(a)。
简单来说,就是(a / b) % p = (a * inv(a) ) % p = (a % p * inv(a) % p) % p
——这样我们就可以在mod中做"除法"啦 ~(—v—*)~
!Warning:当且仅当(a,p)=1,即a,p互质的时候,a才有在%p环境下的数论倒数!
三、中国剩余定理——解 线性同余方程组
引入
用现代数学的语言来说明的话,中国剩余定理给出了以下的一元线性同余方程组:
有解的判定条件,并用构造法给出了在有解情况下解的具体形式。
中国剩余定理 说明:
方程组有解前提:整数m1,m2, ... ,mn 两两互质,
1 int isHasAnswer(long long int *m)
2 //return 0 == No answer
3 //return 1 == Has answer
4 {
5 int i,j;
6 int flag=0;
7 for(i=1;i<=len_m;i++)
8 for(j=i+1;j<=len_m;j++)
9 {
10 if(gcd(m[i],m[j])==1)//m[i]与m[j]互质
11 {
12 flag=1;
13 return 0;
14 }
15 }
16 return 1;
17 }
则对任意的整数:a1,a2, ... ,an,方程组有解,并且通解可以用如下方式构造得到:
![](https://gss0.bdstatic.com/94o3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D223/sign=afd164d67a310a55c024d9f684444387/7af40ad162d9f2d30fcbdacaaaec8a136327cc39.jpg)
![](https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D203/sign=56bcfe3fc55c1038207ec9c28110931c/91ef76c6a7efce1b22fa36efac51f3deb58f65c6.jpg)
![](https://gss2.bdstatic.com/-fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D20/sign=3421af3261d0f703e2b292dc08fa9d75/91ef76c6a7efce1b232431efac51f3deb48f658c.jpg)
![](https://gss2.bdstatic.com/9fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D17/sign=9757be9e4410b912bbc1f2f9c3fd6213/f3d3572c11dfa9ecd055af3261d0f703918fc198.jpg)
![](https://gss0.bdstatic.com/-4o3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D14/sign=600e2894abcc7cd9fe2d30dd3801d19f/dcc451da81cb39db7a5c542fd8160924ab18302e.jpg)
![](https://gss2.bdstatic.com/-fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D20/sign=987bd370be003af349badb60342ace9c/d0c8a786c9177f3ec9a1cf7478cf3bc79f3d5635.jpg)
![](https://gss2.bdstatic.com/-fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D17/sign=41ce7c820246f21fcd345a54f624e601/dc54564e9258d10925da9bcbd958ccbf6c814d89.jpg)
![](https://gss1.bdstatic.com/9vo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D270/sign=1e2b1996a1ec8a13101a50e7c7039157/5ab5c9ea15ce36d39c3ebe4932f33a87e950b194.jpg)
![](https://gss1.bdstatic.com/9vo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D20/sign=b30da9ccf1d3572c62e29bdc8b1383a3/9a504fc2d56285355c012c0493ef76c6a7ef6336.jpg)
![](https://gss3.bdstatic.com/-Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D486/sign=cb3ee938d72a283447a637036db5c92e/2fdda3cc7cd98d10aa8ef514223fb80e7bec90b9.jpg)
在模的意义下,方程组
只有一个解:
四、扩展欧几里得算法
用途:
扩展欧几里得算法 是用来在已知a, b求解一组x,y,
使它们满足贝祖等式: ax+by = gcd(a, b) =d(一定存在整数解,根据数论中的相关定理)。
时间复杂度:
求 n 个不超过 m 的正整数的最大公约数—— O(n+logm)。
代码:
1 /*已知a,b(a>b),求解x,y,满足ax+by=gcd(a,b),并同时返回gcd(a,b)*/
2 int exGcd(int a,int b,int &x,int &y)//x,y使用了引用传递的方式
3 {
4 if(b==0)
5 {
6 x=1;
7 y=0;
8 /*
9 当a%b==0时,显然gcd(a,b)=b,
10 所以b = gcd(a,b) = gcd(b,a%b) = gcd(b,0)
11 此时显然 b*1+0*0 = gcd(b,0) = gcd(a,b)
12 故 x=1,y=0
13 */
14 }
15 else
16 {
17 //g=gcd(a,b)
18 int g = exGcd(b,a%b,x,y);
19 int t = x;
20 x = y;
21 y = t - a/b * y;
22 return g;
23 /*
24 求解 x,y的方法的理解:
25 设 a>b。
26 1,显然当 b=0,gcd(a,b)=a。此时 x=1,y=0;
27 2,a>b>0 时
28 设 ax1+ by1= gcd(a,b);
29 bx2+ (a mod b)y2= gcd(b,a mod b);
30 根据朴素的欧几里德原理有 gcd(a,b) = gcd(b,a mod b);
31 则:ax1+ by1= bx2+ (a mod b)y2;
32 即:ax1+ by1= bx2+ (a - [a / b] * b)y2=ay2+ bx2- [a / b] * by2;
33 也就是ax1+ by1 == ay2+ b(x2- [a / b] *y2);
34 根据恒等定理得:
35
36 x1=y2;
37 y1=x2- [a / b] *y2;
38
39 这样我们就得到了求解 x1,y1 的方法:x1,y1 的值基于 x2,y2.
40 上面的思想是以递归定义的,因为 gcd 不断的递归求解,一定会有个时候 b=0,
41 所以递归可以结束。
42 */
43 }
44 }