中國剩餘定理(CRT)與擴展中國剩餘定理(EXCRT)

以上兩種算法都是用於解決現行同餘方程組問題,只不過中國剩餘定理是擴展中國剩餘定理的一個特殊化解法。ios

1.中國剩餘定理

中國剩餘定理給出瞭如下的一元線性同餘方程組:

有解的斷定條件,並用構造法給出了在有解狀況下解的具體形式。
中國剩餘定理說明:假設整數\(m_1,m_2, ... ,m_n\)兩兩互質,則對任意的整數:\(a_1,a_2, ... ,a_n\),方程組 有解,而且通解能夠用以下方式構造獲得:
是整數\(m_1,m_2, ... ,m_n\)的乘積,並設是除了mi之外的n- 1個整數的乘積。
則有
則方程組 的通解形式爲
該解在模M的意義下惟一。
若是把該解帶入原方程,則發現正確性顯然。
代碼實現實際上是模擬上面的過程,其中求逆元的過程能夠用費馬小定理和擴展歐幾里得,這裏博主選用的是擴展歐幾里得。算法

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ll long long
#define ull unsigned long long
#define N 11
#define M number
using namespace std;

int n;
ll a[N],b[N],x[N],all=1,ans;

inline ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0){
		x=1;
		y=0;
		return a;
	}
	ll gcd=exgcd(b,a%b,x,y);
	ll tmp=x;
	x=y;
	y=tmp-a/b*y;
	return gcd;
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]),all*=a[i];
	for(int i=1;i<=n;i++){
		ll now=all/a[i];
		ll y;
		ll gcd=exgcd(now,a[i],x[i],y);
		x[i]=(x[i]%a[i]+a[i])%a[i];
//		printf("%d\n",x[i]);
	}
	for(int i=1;i<=n;i++){
		ans+=((all/a[i]*b[i])%all*x[i])%all;
		ans%=all;
	}
	printf("%lld\n",ans);
	return 0;
}

擴展中國剩餘定理

運用中國剩餘定理的條件是模數兩兩互素,那麼擴展中國剩餘定理的使用條件是任意模數。這裏給出證實。
對於一個方程組來講,第一個方程的通解很明顯能用擴展歐幾里得算法得出。
把該解寫成通解的形式,帶入第二個式子。例如該通解爲\(x_0+t*m\),再帶入第二個方程式後,我麼要作的是找到一個t使該通解一樣知足第二個式子。以此類推。
注意,不管任什麼時候候,該通解在模已經解決的方程的模數的最小公倍數下惟一。代碼:(由於long long乘long long可能會溢出,這裏用快速乘)spa

#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ll long long
#define ld long double
#define ull unsigned long long
#define N 100100
#define M number
using namespace std;

inline ll exgcd(ll a,ll b,ll &x,ll &y){
	if(b==0){
		x=1;
		y=0;
		return a;
	}
	ll gcd=exgcd(b,a%b,x,y);
	ll tmp=x;
	x=y;
	y=tmp-a/b*y;
	return gcd;
}

ll n;
ll a[N],b[N];

inline ll ksc(ll x,ll y,ll mod){
	ll z=(ld)x/mod*y;
	ll res=(ull)x*y-(ull)mod*z;
	return (res%mod+mod)%mod;
}

int main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++) scanf("%lld%lld",&a[i],&b[i]);
	ll m=a[1],x0=b[1];
	for(int i=2;i<=n;i++){
		ll t,q;
		ll g=exgcd(m,a[i],t,q);
		ll now_m=m/g*a[i];
		t=ksc(t,((b[i]-x0)/g+a[i])%a[i],a[i]);
		x0=(x0%now_m+ksc(t,m,now_m))%now_m;
		m=now_m;
	}
	printf("%lld",x0);
	return 0;
}
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息