首页 > 其他分享 >线性同余方程+中国剩余定理

线性同余方程+中国剩余定理

时间:2023-08-27 16:45:30浏览次数:44  
标签:方程 frac pmod 定理 线性 include ll 同余

逆元

求解\(ax=b\pmod m\),其实等价于\(ax+my=b\),然后扩欧就无了。

可以应用于求当是\(a,p\)互质,求\(a\)在模\(p\)意义下的逆元,方法就是求解\(ax=1\pmod p\)。

中国剩余定理(CRT)

问题:

有\(m_1,m_2,...,m_n\),\(n\)个整数两两互质,还给定\(a_1,a_2,...,a_n\),需要我们求解一个方程组:

\( \begin{cases} x=a_1\pmod {m_1}\\ x=a_2\pmod {m_2}\\ ...\\ x=a_n\pmod {m_n} \end{cases} \)

定理

设\(m=\prod\limits_{i=1}^n m_i\)(全部\(m_i\)的积),\(M_i=m/m_i\)(除本身外所有\(m_i\)的积),以及\(t_i\)为方程\(M_it_i=1\pmod {m_i}\)(\(M_i\)在模\(m_i\)意义下的逆元)。

则整数解\(x=\sum\limits_{i=1}^na_iM_it_i\)。

证明

对于一个方程编号为\(i\),我们可以发现此时对于所有的\(k \in [1,n]\)且\(k!=i\),由\(M_i\)的定义会有\(a_iM_it_i=0\pmod{m_k}\),又由\(t_i\)的定义会有\(a_iM_it_i=a_i\pmod {m_i}\),则\(x=\sum\limits_{i=1}^na_iM_it_i\)带入原方程组显然成立。

通解

对于一个整数k,由于\(m\)的定义,显然会有\(x+km\)对原方程组仍然成立。

实现:P1495 【模板】中国剩余定理(CRT)/ 曹冲养猪

#include<algorithm>
#include<stdio.h>
#include<queue>
#define ll long long
using namespace std;
int n;
int a[15],b[15];
ll M[15],t[15];
ll exgcd(ll a,ll b,ll &x,ll &y) {
	if(b==0) {
		x=1,y=0;
		return a;
	}
	ll d=exgcd(b,a%b,x,y),z=x-a/b*y;
	x=y,y=z;
	return d;
}
int main() {
//	freopen("P1495.in","r",stdin);
//	freopen("P1495.out","w",stdout);
	ll m=1;
	scanf("%d",&n);
	for(int i=1;i<=n;i++) {
		scanf("%d%d",&a[i],&b[i]);
		m*=a[i];
	}	
	ll sum=0;
	for(int i=1;i<=n;i++) {
		M[i]=m/a[i];
		ll y,d=exgcd(M[i],a[i],t[i],y);
		sum=(sum+1ll*b[i]*M[i]*t[i]%m+m)%m;
	}
	printf("%lld",sum);
	return 0;
}

扩展中剩(EXCRT)

虽然说是拓展,但好像与中剩没什么关系。

问题

我们注意到,上面中剩虽然好用,但是局限性太大了,要求所有的模数都互质,我们更普遍见到模数不互质的情况,此时就可以引出扩展中剩:一种通过合并方程的求解同余方程方法。

推导

我们接下来就来探讨该如何对方程进行合并,假如给出两个方程:
\(\begin{cases} x=a_1\pmod {m_1}\\ x=a_2\pmod {m_2} \end{cases}\)

我们先将方程写成等式形式:
\(\begin{cases} x=a_1+k_1m_1\\ x=a_2+k_2m_2 \end{cases}\)

联立得:\(a_1+k_1m_1=a_2+k_2m_2\).

移项得:\(k_1m_1=(a_2-a_1)+k_2m_2\).

接下来设\(d=\gcd(m_1,m_2)\),将原式除以\(d\),便得:\(k_1\frac {m_1} d=\frac {a_2-a_1} d+k_2\frac {m_2} d\).

我们假设以\(\frac {m_2} d\)为一个模数,就会得到:\(k_1\frac {m_1} d=\frac {a_2-a_1} d\pmod {\frac {m_2} d}\),发现我们将\(k_2\)消去了。

接下来将两边都除去\(\frac {m_1} d\),得到:\(k_1=inv(\frac {m_1} d,\frac {m_2} d) \frac {a_2-a_1} d+\frac {m_2}dy\),其中\(inv(\frac {m_1} d,\frac {m_2} d)\)表示\(\frac {m_1} d\)在模\(\frac {m_2} d\)意义下的逆元,因为两数显然互质,所以有逆元,此式中我们将同余方程形式转换成了等式,便于后面操作。

然后我们可以将\(k1\)回带到最开始的式子\(x=a_1+k_1m_1\)中,得到了\(x=inv(\frac {m_1} d,\frac {m_2} d) \frac {a_2-a_1} dm_1+\frac {m_1m_2}dy+a_1\).

发现我们为了将未知的\(y\)消去,可以设定模数为\(\frac {m_2} d\),于是我们便得到了一个新的同余方程:\(x=inv(\frac {m_1} d,\frac {m_2} d) \frac {a_2-a_1} dm_1+a_1\pmod {\frac {m_1m_2} d}\),方程中的一切量都是我们已知的,自此,我们成功将两个同余方程合并了一个新的同余方程,达到了我们的目的。

注意,方程中出现了\(\frac {a_2-a_1} d\)这一个分数,只有当\(d|a_2-a_1\)时使得方程有解,否则方程无解,这需要在一开始就判断。

以此类推,最后会只剩下一个方程\(x=a\pmod m\),此时的余数\(a\)就是\(x\)的最小正整数取值。

实现

有几个小点需要注意:

1.逆元的求解可以使用扩欧,但要记得最后最好将其转化为非负数。

2.求出\(inv(\frac {m_1} d,\frac {m_2} d) \frac {a_2-a_1} d\)后我们可以将其对\(\frac {m_2} d\)取模,毕竟它本来就是在对\(\frac {m_2} d\)取模意义下的。

3.记得将每次合并方程后求出的\(a\)对\(m\)取模然后转为非负。

4.乘数较为频繁,模次数较少,当数据太强时记得使用int128增强范围。

代码:P4777 【模板】扩展中国剩余定理(EXCRT)

#include<algorithm>
#include<stdio.h>
#include<queue>
#define ll long long
#define maxn 100005
using namespace std;
int n;
ll read() {
	ll k=0; char c=getchar();
	while(c<'0' || c>'9') c=getchar();
	while(c>='0' && c<='9') k=k*10+c-'0',c=getchar();
	return k;
}
ll exgcd(ll a,ll b,ll &x,ll &y) {
	if(b==0) {x=1,y=0; return a;}
	ll d=exgcd(b,a%b,x,y),z=x-a/b*y;
	x=y,y=z;
	return d;
}
ll inv(ll a,ll b) {
	ll x,y,d=exgcd(a,b,x,y);
	x=(x%b+b)%b;
	return x;
}
ll m[maxn],r[maxn];
int main() {
//	freopen("P4777.in","r",stdin);
//	freopen("P4777.out","w",stdout);
	n=read();
	for(int i=1;i<=n;i++) {
		m[i]=read(),r[i]=read();
	}
	for(int i=1;i<n;i++) {
		ll m1=m[i],m2=m[i+1],r1=r[i],r2=r[i+1];
//		printf("%lld %lld %lld %lld ",m1,r1,m2,r2);
		ll x,y,d=exgcd(m1,m2,x,y);
		m[i+1]=(m1/d)*(m2/d)*d;
		r[i+1]=(__int128)inv(m1/d,m2/d)*((r2-r1)/d)%(m2/d)*m1+r1;
		r[i+1]=(r[i+1]%m[i+1]+m[i+1])%m[i+1];
//		printf("%lld %lld\n",m[i+1],r[i+1]);
	}
	printf("%lld",r[n]);
	
	return 0;
}

THE END

标签:方程,frac,pmod,定理,线性,include,ll,同余
From: https://www.cnblogs.com/1n87/p/17660460.html

相关文章

  • 用线性表实现的通讯录管理 C++代码
    /****************************************//*主控菜单处理测试程序main2.c************//***************************************/#include<iostream>#include<string>usingnamespacestd;#defineLIST_INIT_SIZE100#defineLISTINCREMENT10intOK=......
  • 线性回归基本原理和公式推导
    回复我们公众号“1号程序员”的“E001”可以获取《BAT机器学习面试1000题》下载链接。[关注并回复:【E001】]线性回归是一种监督式机器学习算法,它计算因变量与一个或多个独立特征之间的线性关系。当独立特征的数量为1时,被称为单变量线性回归;在存在多于一个特征的情况下,被称......
  • 高等数学——微分中值定理
    微分中值定理罗尔定理费马引理\(f(x)\)在\(x_{0}\)\(U(x_{0})\)有定义,在\(x_{0}\)处可导,如\(f(x)\lef(x_{0})\),所有的\(x\inU(x_{0})\)。则\(f'(x_{0})=0\)。导数等于零的点为函数的驻点(或稳定点,临界点)。罗尔定理如果\(f(x)\)满足:在\([a,b]\)连续。在......
  • 线性筛不大全
    众所周知,OI界有一股清流,它的名字叫做筛法这之中,有一线性筛十分出名,人称XXS.今天稍微总结一下最近用过的,比较厉害的,线性筛.目前用到的比较常用的线性筛,大多是建立在质数的基础上的,也就是以最普通的筛法求质数为基点,向外延伸.筛法求质数voidWonder_of_U(){......
  • 《线性代数》2. 向量的高级话题
    规范化和单位向量在了解完向量的基础知识后,我们来探讨更多和向量有关的高级话题。首先向量是一个有向线段,由原点指向空间中的某一个点,所以向量除了具有方向之外,还应该具有大小。比如有两个向量\(\vec{u}\)、\(\vec{w}\),分别是\((3,4)^{T}\)、\((4,3)^{T}\),那么它们的长度是多......
  • 文理分科(最大流最小割定理)
    传送门数据范围一眼网络流。考虑每个人文理只能选一个,考虑最小割。考虑源点\(S\)向\((i,j)\)连一条费用为\(art_{i,j}\)的边,\((i,j)\)向汇点\(T\)连一条费用为\(science_{i,j}\)的边。若割\(S\)与\((i,j)\)的边,则表示\((i,j)\)不选文,若割\((i,j_\)与\(T\)的边,则表示\((i,j)\)不......
  • 欧拉定理学习笔记
    欧拉定理:若\(gcd(a,m)=1\),则\(a^{\varphi(m)}\equiv1\pmod{m}\)证明:令\(r_1,r_2,···,r_{\varphi(m)}\)为模m下的一个简化剩余系,则\(ar_1,ar_2,···,ar_{\varphi(m)}\)也为模m下的一个简化剩余系,令\(f=r_1*r_2*···*r_{\varphi(m)}\),则有:\(f\equivar_1*ar_2*···*ar_{......
  • 数据结构代码题-线性表
    王道数据结构大题代码线性表#include<stdio.h>#include<stdlib.h>voiddelMin(int*arr,intlen){ if(!len){ printf("数组为空"); return0; } intmin=*arr,minPos=0; for(inti=0;i<len;i++){ if(min>*(arr+i)){ min=*(arr+......
  • 《线性代数》1. 一切从向量开始
    什么是向量我们在初等数学的时候,研究的都是一个数,而到线性代数,我们将从研究一个数拓展到研究一组数,而一组数的基本表示方法就是向量(Vector)。向量是线性代数研究的基本元素,它就是一组数,比如\((1,2,3)\)就是一个向量。那么问题来了,向量究竟有什么作用呢?或者说我们研究一组数有......
  • 【线性代数】第五章 特征值和特征向量
    1.特征值和特征向量特征值和特征向量的定义:对于n阶矩阵A,如果存在一个数λ以及非零n维列向量α,使得Aα=λα成立则称λ是矩阵A的一个特征值。非零向量α是矩阵A属于特征值的一个特征向量。这个式子可以写成(λE-A)α=0,α≠0,所以特征向量α可以说成这个齐次方程的非零......