中國剩餘定理(又稱 孫子定理)
阿新 • • 發佈:2019-02-05
部落格圖片都飛了,所以看的時候請多多擔待。
中國剩餘定理是數論中的一個關於一元線性同餘方程組的定理,說明了一元線性同餘方程組有解的準則以及求解方法。也稱為孫子定理。
本文大部分使用的內容來自維基百科。
中國剩餘定理說明:
題意:給出你n個ai和mi,最後讓求出x的最小值是多少。
假設整數m1, m2, … , mn其中任兩數互質,則對任意的整數:a1, a2, … , an,方程組 (S)有解,並且通解可以用如下方式構造得到:
1.設是整數m1, m2, … , mn的乘積,並設
,即 是除了mi以外的n − 1個整數的乘積。
2.設為模的數論倒數:
3.方程組(S)的通解形式為:
在模 M的意義下,方程組 (S)只有一個解:
**注:**對任何,由於,所以說明存在整數 使得這樣的叫做 模 的數論倒數。
具體證明可以參照wiki 這裡寫連結內容
例子也可以看wiki中的“物不知數”。
以下給出一般情況下也就是整數m1, m2, … , mn其中任兩數互質情況下的模板:
關於擴充套件歐幾里得可以看以前的文章這裡是傳送門哦~
程式碼1:
////m[]任意兩個互質 #include <iostream> #include <algorithm> #include <cmath> #include <cstdio> using namespace std; #define LL long long const int N=1e6+10; const LL Me=1e9+7; const int inf=0x3f3f3f3f; int a[N],m[N]; LL exgcd(LL a,int b,LL &x,LL &y) { if(a==0&&b==0) return -1; if(!b) { x=1; y=0; return a; } LL d=exgcd(b,a%b,y,x); //回代 注意這裡的與擴充套件gcd的不同 y-=a/b*x; return d; } LL CRT(int n) { LL M=1; for(int i=0;i<n;i++) M*=m[i]; LL ret=0; for(int i=0;i<n;i++) { LL x,y; LL tm=M/m[i]; int xxx=exgcd(tm,m[i],x,y); ret=(ret+tm*x*a[i])%M; } return (ret+M)%M; } int main() { int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d%d",&m[i],&a[i]); } cout<<CRT(n)<<endl; return 0; }
拓展:
中國剩餘數定理是適用於n個mi兩兩互質的情況的,如果不互質呢,下面就是一個轉換:
模不兩兩互質的同餘式組可化為模兩兩互質的同餘式組,再用孫子定理直接求解。
84=22×3×7,160=25×5,63=32×7,由推廣的孫子定理可得 與同解。
注意求解過程中應先檢查同餘式組上是否存在矛盾,存在矛盾的同餘式組無解。
證明過程:
程式碼2:
#include <iostream> #include <algorithm> #include <cmath> #include <cstdio> using namespace std; #define LL long long const int N=1e6+10; const LL Me=1e9+7; const int inf=0x3f3f3f3f; int a[N],m[N]; LL exgcd(LL a,int b,LL &x,LL &y) { if(a==0&&b==0) return -1; if(!b) { x=1; y=0; return a; } LL d=exgcd(b,a%b,y,x); //回代 注意這裡的與擴充套件gcd的不同 y-=a/b*x; return d; } LL CRT(int n) { if(n==1) { if(m[0]>a[0]) return a[0]; else return -1; } LL x,y,d; for(int i=1; i<n; i++) { if(m[i]<=a[i]) return -1; d=exgcd(m[0],m[i],x,y); if((a[i]-a[0])%d!=0) ////不能整除則無解 return -1; LL t=m[i]/d; x=((a[i]-a[0])/d*x%t+t)%t; ////第0個與第i個模線性方程的特解 a[0]=x*m[0]+a[0]; m[0]=m[0]*m[i]/d; a[0]=(a[0]%m[0]+m[0])%m[0]; } return a[0]; } int main() { int n; scanf("%d",&n); for(int i=0; i<n; i++) { scanf("%d%d",&m[i],&a[i]); } cout<<CRT(n)<<endl; return 0; }